]> git.ipfire.org Git - thirdparty/krb5.git/commitdiff
set svn:eol-style to native for *.[ch]
authorKen Raeburn <raeburn@mit.edu>
Wed, 20 Jun 2007 01:19:59 +0000 (01:19 +0000)
committerKen Raeburn <raeburn@mit.edu>
Wed, 20 Jun 2007 01:19:59 +0000 (01:19 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@19596 dc483132-0cff-0310-8789-dd5450dbe970

186 files changed:
src/lib/krb5/krb/srv_dec_tkt.c
src/windows/identity/doc/cred_aquisition.h
src/windows/identity/doc/cred_data_types.h
src/windows/identity/doc/cred_main.h
src/windows/identity/doc/cred_msgs.h
src/windows/identity/doc/cred_prop_pages.h
src/windows/identity/doc/main_page.h
src/windows/identity/doc/plugin_framework.h
src/windows/identity/doc/plugin_locale.h
src/windows/identity/doc/plugin_main.h
src/windows/identity/doc/plugin_structure.h
src/windows/identity/doc/ui_actions.h
src/windows/identity/doc/ui_context.h
src/windows/identity/doc/ui_main.h
src/windows/identity/doc/ui_menus.h
src/windows/identity/help/khhelp.h
src/windows/identity/include/khdefs.h
src/windows/identity/include/kherror.h
src/windows/identity/include/khlist.h
src/windows/identity/include/khmsgtypes.h
src/windows/identity/include/netidmgr.h
src/windows/identity/kconfig/api.c
src/windows/identity/kconfig/kconfig.h
src/windows/identity/kconfig/kconfiginternal.h
src/windows/identity/kconfig/kconfigmain.c
src/windows/identity/kconfig/registry.c
src/windows/identity/kconfig/test/utiltest.c
src/windows/identity/kcreddb/attrib.c
src/windows/identity/kcreddb/attrib.h
src/windows/identity/kcreddb/buf.c
src/windows/identity/kcreddb/buf.h
src/windows/identity/kcreddb/credential.c
src/windows/identity/kcreddb/credential.h
src/windows/identity/kcreddb/credset.c
src/windows/identity/kcreddb/credset.h
src/windows/identity/kcreddb/credtype.c
src/windows/identity/kcreddb/credtype.h
src/windows/identity/kcreddb/identity.c
src/windows/identity/kcreddb/identity.h
src/windows/identity/kcreddb/init.c
src/windows/identity/kcreddb/kcreddb.h
src/windows/identity/kcreddb/kcreddbinternal.h
src/windows/identity/kcreddb/kcreddbmain.c
src/windows/identity/kcreddb/langres.h
src/windows/identity/kcreddb/resource.h
src/windows/identity/kcreddb/type.c
src/windows/identity/kcreddb/type.h
src/windows/identity/kherr/kherr.c
src/windows/identity/kherr/kherr.h
src/windows/identity/kherr/kherrinternal.h
src/windows/identity/kherr/kherrmain.c
src/windows/identity/kmm/kmm.c
src/windows/identity/kmm/kmm.h
src/windows/identity/kmm/kmm_module.c
src/windows/identity/kmm/kmm_plugin.c
src/windows/identity/kmm/kmm_reg.c
src/windows/identity/kmm/kmm_registrar.c
src/windows/identity/kmm/kmminternal.h
src/windows/identity/kmm/kmmmain.c
src/windows/identity/kmm/kplugin.h
src/windows/identity/kmq/consumer.c
src/windows/identity/kmq/init.c
src/windows/identity/kmq/kmq.h
src/windows/identity/kmq/kmqinternal.h
src/windows/identity/kmq/kmqmain.c
src/windows/identity/kmq/msgtype.c
src/windows/identity/kmq/publisher.c
src/windows/identity/nidmgrdll/dllmain.c
src/windows/identity/plugins/common/dynimport.c
src/windows/identity/plugins/common/dynimport.h
src/windows/identity/plugins/common/krb5common.c
src/windows/identity/plugins/common/krb5common.h
src/windows/identity/plugins/krb4/errorfuncs.c
src/windows/identity/plugins/krb4/errorfuncs.h
src/windows/identity/plugins/krb4/krb4configdlg.c
src/windows/identity/plugins/krb4/krb4funcs.c
src/windows/identity/plugins/krb4/krb4funcs.h
src/windows/identity/plugins/krb4/krb4main.c
src/windows/identity/plugins/krb4/krb4newcreds.c
src/windows/identity/plugins/krb4/krb4plugin.c
src/windows/identity/plugins/krb4/krbcred.h
src/windows/identity/plugins/krb4/langres.h
src/windows/identity/plugins/krb5/datarep.c
src/windows/identity/plugins/krb5/datarep.h
src/windows/identity/plugins/krb5/errorfuncs.c
src/windows/identity/plugins/krb5/errorfuncs.h
src/windows/identity/plugins/krb5/krb5configcc.c
src/windows/identity/plugins/krb5/krb5configid.c
src/windows/identity/plugins/krb5/krb5configids.c
src/windows/identity/plugins/krb5/krb5funcs.c
src/windows/identity/plugins/krb5/krb5funcs.h
src/windows/identity/plugins/krb5/krb5identpro.c
src/windows/identity/plugins/krb5/krb5main.c
src/windows/identity/plugins/krb5/krb5newcreds.c
src/windows/identity/plugins/krb5/krb5plugin.c
src/windows/identity/plugins/krb5/krb5props.c
src/windows/identity/plugins/krb5/krbcred.h
src/windows/identity/plugins/krb5/langres.h
src/windows/identity/sample/templates/credprov/config_id.c
src/windows/identity/sample/templates/credprov/config_ids.c
src/windows/identity/sample/templates/credprov/config_main.c
src/windows/identity/sample/templates/credprov/credacq.c
src/windows/identity/sample/templates/credprov/credprov.h
src/windows/identity/sample/templates/credprov/credtype.c
src/windows/identity/sample/templates/credprov/langres.h
src/windows/identity/sample/templates/credprov/main.c
src/windows/identity/sample/templates/credprov/plugin.c
src/windows/identity/sample/templates/credprov/proppage.c
src/windows/identity/ui/aboutwnd.c
src/windows/identity/ui/aboutwnd.h
src/windows/identity/ui/addrchange.c
src/windows/identity/ui/addrchange.h
src/windows/identity/ui/appglobal.h
src/windows/identity/ui/cfg_appear_wnd.c
src/windows/identity/ui/cfg_general_wnd.c
src/windows/identity/ui/cfg_identities_wnd.c
src/windows/identity/ui/cfg_notif_wnd.c
src/windows/identity/ui/cfg_plugins_wnd.c
src/windows/identity/ui/configwnd.c
src/windows/identity/ui/configwnd.h
src/windows/identity/ui/credfuncs.c
src/windows/identity/ui/credfuncs.h
src/windows/identity/ui/credwnd.c
src/windows/identity/ui/credwnd.h
src/windows/identity/ui/debugfuncs.c
src/windows/identity/ui/debugfuncs.h
src/windows/identity/ui/htwnd.c
src/windows/identity/ui/htwnd.h
src/windows/identity/ui/khmapp.h
src/windows/identity/ui/main.c
src/windows/identity/ui/mainmenu.c
src/windows/identity/ui/mainmenu.h
src/windows/identity/ui/mainwnd.h
src/windows/identity/ui/notifier.c
src/windows/identity/ui/notifier.h
src/windows/identity/ui/passwnd.c
src/windows/identity/ui/passwnd.h
src/windows/identity/ui/propertywnd.c
src/windows/identity/ui/propertywnd.h
src/windows/identity/ui/reqdaemon.c
src/windows/identity/ui/reqdaemon.h
src/windows/identity/ui/resource.h
src/windows/identity/ui/statusbar.c
src/windows/identity/ui/statusbar.h
src/windows/identity/ui/timer.c
src/windows/identity/ui/timer.h
src/windows/identity/ui/toolbar.c
src/windows/identity/ui/toolbar.h
src/windows/identity/uilib/action.c
src/windows/identity/uilib/alert.c
src/windows/identity/uilib/configui.c
src/windows/identity/uilib/configui.h
src/windows/identity/uilib/creddlg.c
src/windows/identity/uilib/intaction.h
src/windows/identity/uilib/khaction.h
src/windows/identity/uilib/khactiondef.h
src/windows/identity/uilib/khalerts.h
src/windows/identity/uilib/khconfigui.h
src/windows/identity/uilib/khhtlink.h
src/windows/identity/uilib/khnewcred.h
src/windows/identity/uilib/khprops.h
src/windows/identity/uilib/khremote.h
src/windows/identity/uilib/khrescache.h
src/windows/identity/uilib/khtracker.h
src/windows/identity/uilib/khuidefs.h
src/windows/identity/uilib/propsheet.c
src/windows/identity/uilib/propwnd.c
src/windows/identity/uilib/rescache.c
src/windows/identity/uilib/trackerwnd.c
src/windows/identity/uilib/uibind.c
src/windows/identity/uilib/uilibmain.c
src/windows/identity/uilib/version.c
src/windows/identity/util/hashtable.c
src/windows/identity/util/hashtable.h
src/windows/identity/util/mstring.c
src/windows/identity/util/mstring.h
src/windows/identity/util/perfstat.c
src/windows/identity/util/perfstat.h
src/windows/identity/util/sync.c
src/windows/identity/util/sync.h
src/windows/identity/util/utils.h
src/windows/kfwlogon/kfwcommon.c
src/windows/kfwlogon/kfwcpcc.c
src/windows/kfwlogon/kfwlogon.c
src/windows/kfwlogon/kfwlogon.h
src/windows/winlevel.h

index 7dad1ed16320a857ce94d4ce06d2acef30915b11..e994ac9950e151bad97c3f15b20f6560075a21d0 100644 (file)
@@ -1,94 +1,94 @@
-/*\r
- * lib/krb5/krb/srv_dec_tkt.c\r
- *\r
- * Copyright 2006 by the Massachusetts Institute of Technology.\r
- * All Rights Reserved.\r
- *\r
- * Export of this software from the United States of America may\r
- *   require a specific license from the United States Government.\r
- *   It is the responsibility of any person or organization contemplating\r
- *   export to obtain such a license before exporting.\r
- *\r
- * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and\r
- * distribute this software and its documentation for any purpose and\r
- * without fee is hereby granted, provided that the above copyright\r
- * notice appear in all copies and that both that copyright notice and\r
- * this permission notice appear in supporting documentation, and that\r
- * the name of M.I.T. not be used in advertising or publicity pertaining\r
- * to distribution of the software without specific, written prior\r
- * permission.  Furthermore if you modify this software you must label\r
- * your software as modified software and not distribute it in such a\r
- * fashion that it might be confused with the original M.I.T. software.\r
- * M.I.T. makes no representations about the suitability of\r
- * this software for any purpose.  It is provided "as is" without express\r
- * or implied warranty.\r
- *\r
- *\r
- * Server decrypt ticket via keytab or keyblock. \r
- *\r
- * Different from krb5_rd_req_decoded. (krb5/src/lib/krb5/krb/rd_req_dec.c)\r
- *   - No krb5_principal_compare or KRB5KRB_AP_ERR_BADMATCH error.\r
- *   - No replay cache processing.\r
- *   - No skew checking or KRB5KRB_AP_ERR_SKEW error.\r
- *   - No address checking or KRB5KRB_AP_ERR_BADADDR error.\r
- *   - No time validation.\r
- *   - No permitted enctype validation or KRB5_NOPERM_ETYPE error.\r
- *   - Does not free ticket->enc_part2 on error. \r
- */\r
-\r
-#include <k5-int.h>\r
-\r
-krb5_error_code KRB5_CALLCONV\r
-krb5int_server_decrypt_ticket_keyblock(krb5_context context,\r
-                                      const krb5_keyblock *key,\r
-                                      krb5_ticket *ticket)\r
-{\r
-    krb5_error_code retval;\r
-    krb5_data *realm;\r
-    krb5_transited *trans;\r
-\r
-    retval = krb5_decrypt_tkt_part(context, key, ticket);\r
-    if (retval) \r
-       goto done;\r
-\r
-    trans = &ticket->enc_part2->transited;\r
-    realm = &ticket->enc_part2->client->realm;\r
-    if (trans->tr_contents.data && *trans->tr_contents.data) {\r
-       retval = krb5_check_transited_list(context, &trans->tr_contents,\r
-                                          realm, &ticket->server->realm);\r
-       goto done;\r
-    }\r
-\r
-    if (ticket->enc_part2->flags & TKT_FLG_INVALID) {  /* ie, KDC_OPT_POSTDATED */\r
-       retval = KRB5KRB_AP_ERR_TKT_INVALID;\r
-       goto done;\r
-    }\r
-\r
-  done:\r
-    return retval;\r
-}\r
-\r
-\r
-krb5_error_code        KRB5_CALLCONV\r
-krb5_server_decrypt_ticket_keytab(krb5_context context,\r
-                                 const krb5_keytab kt,\r
-                                 krb5_ticket *ticket)\r
-{\r
-    krb5_error_code       retval;\r
-    krb5_enctype          enctype;\r
-    krb5_keytab_entry     ktent;\r
-\r
-    enctype = ticket->enc_part.enctype;\r
-\r
-    if ((retval = krb5_kt_get_entry(context, kt, ticket->server,\r
-                                    ticket->enc_part.kvno,\r
-                                    enctype, &ktent)))\r
-        return retval;\r
-\r
-    retval = krb5int_server_decrypt_ticket_keyblock(context,\r
-                                                   &ktent.key, ticket);\r
-    /* Upon error, Free keytab entry first, then return */\r
-\r
-    (void) krb5_kt_free_entry(context, &ktent);\r
-    return retval;\r
-}\r
+/*
+ * lib/krb5/krb/srv_dec_tkt.c
+ *
+ * Copyright 2006 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ * Server decrypt ticket via keytab or keyblock. 
+ *
+ * Different from krb5_rd_req_decoded. (krb5/src/lib/krb5/krb/rd_req_dec.c)
+ *   - No krb5_principal_compare or KRB5KRB_AP_ERR_BADMATCH error.
+ *   - No replay cache processing.
+ *   - No skew checking or KRB5KRB_AP_ERR_SKEW error.
+ *   - No address checking or KRB5KRB_AP_ERR_BADADDR error.
+ *   - No time validation.
+ *   - No permitted enctype validation or KRB5_NOPERM_ETYPE error.
+ *   - Does not free ticket->enc_part2 on error. 
+ */
+
+#include <k5-int.h>
+
+krb5_error_code KRB5_CALLCONV
+krb5int_server_decrypt_ticket_keyblock(krb5_context context,
+                                      const krb5_keyblock *key,
+                                      krb5_ticket *ticket)
+{
+    krb5_error_code retval;
+    krb5_data *realm;
+    krb5_transited *trans;
+
+    retval = krb5_decrypt_tkt_part(context, key, ticket);
+    if (retval) 
+       goto done;
+
+    trans = &ticket->enc_part2->transited;
+    realm = &ticket->enc_part2->client->realm;
+    if (trans->tr_contents.data && *trans->tr_contents.data) {
+       retval = krb5_check_transited_list(context, &trans->tr_contents,
+                                          realm, &ticket->server->realm);
+       goto done;
+    }
+
+    if (ticket->enc_part2->flags & TKT_FLG_INVALID) {  /* ie, KDC_OPT_POSTDATED */
+       retval = KRB5KRB_AP_ERR_TKT_INVALID;
+       goto done;
+    }
+
+  done:
+    return retval;
+}
+
+
+krb5_error_code        KRB5_CALLCONV
+krb5_server_decrypt_ticket_keytab(krb5_context context,
+                                 const krb5_keytab kt,
+                                 krb5_ticket *ticket)
+{
+    krb5_error_code       retval;
+    krb5_enctype          enctype;
+    krb5_keytab_entry     ktent;
+
+    enctype = ticket->enc_part.enctype;
+
+    if ((retval = krb5_kt_get_entry(context, kt, ticket->server,
+                                    ticket->enc_part.kvno,
+                                    enctype, &ktent)))
+        return retval;
+
+    retval = krb5int_server_decrypt_ticket_keyblock(context,
+                                                   &ktent.key, ticket);
+    /* Upon error, Free keytab entry first, then return */
+
+    (void) krb5_kt_free_entry(context, &ktent);
+    return retval;
+}
index 30f626f8e834c3f20b1c5552fd2b1b8fc06a8b48..bce826dc8be6777b7be3fa44bb8f86e48f535884 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/*! \page cred_acq Managed credential acquisition\r
-\r
-    Credential providers and identity providers must participate in\r
-    managed credential acquisition in order to respond to the user's\r
-    requests to obtain new credentials for an identity or to renew\r
-    credentials for an existing identity.\r
-\r
-    There are two major processes that result in managed credential\r
-    acuqisition.  One is the acquisition of credentials, while the\r
-    other is credential renewal.  During a renewal, existing\r
-    credentials are used to obtain new credentials which expire later\r
-    than the existing credential.  Typically, the identity provider\r
-    performs the task of obtaining renewed initial credentials while\r
-    the other credential providers obtain new credentials based on\r
-    these initial credentials.\r
-\r
-    \section cred_acq_new New Credentials\r
-\r
-    When a user initiates the process of initial credential\r
-    acquisition, Network Identity Manager broadcasts a\r
-    <::KMSG_CRED,::KMSG_CRED_NEW_CREDS> message.  Credential providers\r
-    which need to participate in the credential acquisition should\r
-    respond to this message as detailed in \ref cred_acq_handle.\r
-\r
-    \section cred_acq_renew Renew Credentials\r
-\r
-    Network Identity Manager broadcasts a\r
-    <::KMSG_CRED,::KMSG_CRED_RENEW_CREDS> message to initiate the\r
-    process of renewing credentials.  This may be triggered\r
-    automatically or by a user action.  Credential providers which\r
-    need to participate in the renewal should respond to this message\r
-    as detailed in \ref cred_acq_handle.\r
-\r
-    The following pages provide detailed information:\r
-\r
-    - \subpage cred_acq_new_resp\r
-    - \subpage cred_acq_dlgproc\r
- */\r
-\r
-/*! \page cred_acq_new_resp Handling new credentials acquisition\r
-\r
-    The process of acquiring credentials happens as follows:\r
-\r
-    - Network Identity Manager creates a ::khui_new_creds object and a\r
-      credentials acquisition window.\r
-\r
-    - <::KMSG_CRED,::KMSG_CRED_RENEW_CREDS> or\r
-      <::KMSG_CRED,::KMSG_CRED_NEW_CREDS> is sent to all the\r
-      credentials providers.\r
-\r
-    - The credential providers create the panels (where appropriate)\r
-      for customizing their respective credential types.  The type,\r
-      panel and any dependency information is populated into a\r
-      ::khui_new_creds_by_type structure and added to the\r
-      ::khui_new_creds structure. (See khui_cw_add_type()).\r
-\r
-    - <::KMSG_CRED, ::KMSG_CRED_DIALOG_PRESTART> is sent to all the\r
-      credentials providers.  Credentials providers should use this\r
-      message to finialize initialization in preparation of showing\r
-      the credentials acquisition window, such as by initializing the\r
-      controls of the individual panels.\r
-\r
-    - <::KMSG_CRED, ::KMSG_CRED_DIALOG_START> is sent to all the\r
-      credentials providers.\r
-\r
-    - The dialog for obtaining credentials is displayed.\r
-      Notifications between the main dialog and the individual panels\r
-      are done through ::KHUI_WM_NC_NOTIFY messages to the dialog\r
-      procedures.\r
-\r
-    - Once the dialog processing is done, a ::WMNC_DIALOG_PREPROCESS\r
-      message is sent to the dialog procedure.\r
-\r
-    - Network Identity Manager posts\r
-      <::KMSG_CRED,::KMSG_CRED_DIALOG_PROCESS> message to all the\r
-      credentials providers.  Each provider should check if the user\r
-      cancelled the dialog or indicated that the new credentials\r
-      should be obtained and act accordingly.  The \a result field of\r
-      the ::khui_new_creds structure will be set to either\r
-      ::KHUI_NC_RESULT_PROCESS or ::KHUI_NC_RESULT_CANCEL to indicate\r
-      whether the user wishes to acquire credentials or cancel the\r
-      operation.\r
-\r
-    - A <::KMSG_CRED, ::KMSG_CRED_END> message signals the end of the\r
-      credentials acquisition process.  Each credentials provider is\r
-      responsible for removing the ::khui_new_creds_by_type structre\r
-      from the ::khui_new_creds structure and freeing up any resources\r
-      it allocated earlier in preparation for obtaining new\r
-      credentials.\r
-\r
-    \section cred_acq_handle Responding to credential acquisition messages\r
-\r
-    The credential acquisition messages are\r
-    <::KMSG_CRED,::KMSG_CRED_NEW_CREDS> and <::KMSG_CRED,\r
-    ::KMSG_CRED_RENEW_CREDS>.  They are structured as follows:\r
-\r
-    - \b type : ::KMSG_CRED\r
-    - \b subtype: ::KMSG_CRED_NEW_CREDS or ::KMSG_CRED_RENEW_CREDS\r
-    - \b uparam : 0 (unused)\r
-    - \b vparam : a pointer to a ::khui_new_creds structure.\r
-\r
-    The \a vparam parameter of the message, as shown above, is a\r
-    pointer to a ::khui_new_creds structure.  You can use the \a\r
-    subtype field of this structure to determine whether this is a new\r
-    credentials acquisition or a renewal.\r
-\r
-    In response to this message, a credentials provider is expected to\r
-    provide a configuration panel which the user can use to customize\r
-    how the credentials of this type are to be obtained.  The panel is\r
-    described by the ::khui_new_creds_by_type structure.\r
-\r
-    \subsection cred_acq_panel_spec Specifying the credentials type panel\r
-\r
-    The credentials type panel is used by the user to customize how\r
-    credentials of the specified type are to be obtained.  The\r
-    ::khui_new_creds_by_type structure that describes the panel can be\r
-    used to specify a number of parameters that guide how the panel is\r
-    to be displayed in the new credentials acquisition dialog.\r
-\r
-    The \a name field defines a localized string that will be\r
-    displayed in the tab control that houses the panel.  If it is \a\r
-    NULL, then the name of the credentials type is used.  Optionally,\r
-    an icon can be specified in the \a icon field which will appear\r
-    alongside the name.  A tooltip may be provided in the \a tooltip\r
-    field which will be displayed when the user hovers the mouse over\r
-    the tab.\r
-\r
-    In order to assert that the tab appears at a specific position in\r
-    the list of tabs, you can specify a positive number in the \a\r
-    ordinal field.  Zero does not count as a valid ordinal.  The\r
-    panels with positive ordinals are arranged first in increasing\r
-    order of ordinal (conflicts are resolved by sorting along the \a\r
-    name).  Then the panels without a positive ordianl are arranged\r
-    behind these in increasing order of \a name.\r
-\r
-    Currently, the credentials provider must specify a dialog template\r
-    that will be used to create the embedded dialog for configuring\r
-    new credentials for the type.  This is done by setting the\r
-    khui_new_creds_by_type::h_module, khui_new_creds_by_type::dlg_proc\r
-    and khui_new_creds_by_type::dlg_template fields.\r
-\r
-    Following is example code which suggests how this could be done:\r
-\r
-    \code\r
-       // Message handling code for KMSG_CRED_NEW_CREDS or\r
-       // KMSG_CRED_INIT_CREDS\r
-       ...\r
-       khui_new_creds * c;\r
-       khui_new_creds_by_type * t;\r
-\r
-       c = (khui_new_creds *) vparam;\r
-       t = PMALLOC(sizeof(*t));\r
-       ZeroMemory(t, sizeof(*t));\r
-\r
-       t->type = my_cred_type;\r
-\r
-       // set look and feel params\r
-       t->ordinal = 3; // third in line\r
-       t->name = L"My panel name";\r
-       t->icon = LoadIcon(my_hInstance, MAKEINTRESOURCE(IDI_PANEL_ICON));\r
-       t->tooltip = L"Configure credentials of my type";\r
-\r
-       // specify the dialog template to use\r
-       t->h_module = my_hInstance;\r
-       t->dlg_proc = my_dialog_procedure;\r
-       t->dlg_template = MAKEINTRESOURCE(IDD_NEW_CREDS);\r
-\r
-       if(KHM_FAILED(khui_cw_add_type(c,t))) {\r
-           // handle error\r
-       }\r
-    \endcode\r
-\r
-    It is important to note that the ::khui_new_creds_by_type pointer\r
-    that is passed into khui_cw_add_type() points to an allocated\r
-    block of memory which should remain in memory until\r
-    <::KMSG_CRED,::KMSG_CRED_END> message is received.\r
-\r
-    For information on how the dialog procedure should be written, see\r
-    \ref cred_acq_dlgproc .\r
\r
-*/\r
-\r
-/*! \page cred_acq_dlgproc Writing the dialog procedure for a cred type panel\r
-\r
-    \r
-*/\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 identity providers must participate in
+    managed credential acquisition in order to respond to the user's
+    requests to obtain new credentials for an identity or to renew
+    credentials for an existing identity.
+
+    There are two major processes that result in managed credential
+    acuqisition.  One is the acquisition of credentials, while the
+    other is credential renewal.  During a renewal, existing
+    credentials are used to obtain new credentials which expire later
+    than the existing credential.  Typically, the identity provider
+    performs the task of obtaining renewed initial credentials while
+    the other credential providers obtain new credentials based on
+    these initial credentials.
+
+    \section cred_acq_new New Credentials
+
+    When a user initiates the process of initial credential
+    acquisition, Network Identity Manager broadcasts a
+    <::KMSG_CRED,::KMSG_CRED_NEW_CREDS> message.  Credential providers
+    which need to participate in the credential acquisition should
+    respond to this message as detailed in \ref cred_acq_handle.
+
+    \section cred_acq_renew Renew Credentials
+
+    Network Identity Manager broadcasts a
+    <::KMSG_CRED,::KMSG_CRED_RENEW_CREDS> message to initiate the
+    process of renewing credentials.  This may be triggered
+    automatically or by a user action.  Credential providers which
+    need to participate in the renewal 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 credentials happens as follows:
+
+    - Network Identity Manager creates a ::khui_new_creds object and a
+      credentials acquisition window.
+
+    - <::KMSG_CRED,::KMSG_CRED_RENEW_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. (See khui_cw_add_type()).
+
+    - <::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 processing is done, a ::WMNC_DIALOG_PREPROCESS
+      message is sent to the dialog procedure.
+
+    - Network Identity Manager 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 \a result field of
+      the ::khui_new_creds structure will be set to either
+      ::KHUI_NC_RESULT_PROCESS or ::KHUI_NC_RESULT_CANCEL to indicate
+      whether the user wishes to acquire credentials or cancel the
+      operation.
+
+    - A <::KMSG_CRED, ::KMSG_CRED_END> message signals the end of the
+      credentials acquisition process.  Each 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_NEW_CREDS> and <::KMSG_CRED,
+    ::KMSG_CRED_RENEW_CREDS>.  They are structured as follows:
+
+    - \b type : ::KMSG_CRED
+    - \b subtype: ::KMSG_CRED_NEW_CREDS or ::KMSG_CRED_RENEW_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 a new
+    credentials acquisition or a renewal.
+
+    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_creds_by_type 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_creds_by_type 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.  If it is \a
+    NULL, then the name of the credentials type is used.  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.
+
+    Currently, the credentials provider must specify a dialog template
+    that will be used to create the embedded dialog for configuring
+    new credentials for the type.  This is done by setting the
+    khui_new_creds_by_type::h_module, khui_new_creds_by_type::dlg_proc
+    and khui_new_creds_by_type::dlg_template fields.
+
+    Following is example code 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 = PMALLOC(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";
+
+       // specify the dialog template to use
+       t->h_module = my_hInstance;
+       t->dlg_proc = my_dialog_procedure;
+       t->dlg_template = MAKEINTRESOURCE(IDD_NEW_CREDS);
+
+       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_END> 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
+
+    
+*/
index dcd115219104adf0024c802cb626157b18e1e04e..f2a4122112d2ce7c49afcdd081e034b59d2c410f 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/*! \page cred_data_types Data types in Network Identity Manager\r
-\r
-    Network Identity Manager's Credentials Database supports several\r
-    useful data types.  In addition, plug-ins can define custom data\r
-    types.  Only a few operations are expected of these data types\r
-    since the core KCDB delegates fine grained operations to other\r
-    entities that understand the underlying format.\r
-\r
-    A field in a credential can have any one of these data types, but\r
-    it must have some data type.  Each value can be at most \a\r
-    KCDB_TYPE_MAXCB bytes in length regardless of the data type.\r
-\r
-    Some data types have a fixed size (such as \a Int32), while others\r
-    are variable size.  The required memory for each field in a\r
-    credential is allocated as needed.\r
-\r
-    \section kcdb_pg_dt Data types\r
-\r
-    Descriptions of individual data types are below.\r
-\r
-    \subsection kcdb_pg_idt Individual data types\r
-\r
-    \subsubsection kcdb_pg_idt_v Void\r
-\r
-    Type identifier : ::KCDB_TYPE_VOID\r
-\r
-    The Void data type is used to indicate that the associated object\r
-    does not contain any data.\r
-\r
-    \subsubsection kcdb_pg_idt_s String\r
-\r
-    Type identifier : ::KCDB_TYPE_STRING\r
-\r
-    A unicode string that is terminated with a unicode NULL (L'\\0').\r
-    By default, the type has the following flags:\r
-\r
-    \a KCDB_TYPE_FLAG_CB_AUTO\r
-\r
-    This is because, as long as the string is terminated with a unicode NULL,\r
-    the length of the string, and therefore it's size in bytes, can be inferred\r
-    from the data itself.\r
-\r
-    \subsubsection kcdb_pg_idt_d Date\r
-\r
-    Type identifier : ::KCDB_TYPE_DATE\r
-\r
-    Dates and times in Network Identity Manager are stored in \a\r
-    FILETIME structures.  Utility functions are provided for\r
-    converting from other formats such as \a time_t.\r
-\r
-    \subsubsection kcdb_pg_idt_i Interval\r
-\r
-    Type identifier : ::KCDB_TYPE_INTERVAL\r
-\r
-    Stores an interval of time. Stored as a 64-bit signed integer. The\r
-    string representation of this data type is different from the \a\r
-    Date data type and designates an interval of time.\r
-\r
-    The special value _I64_MAX (which is defined in limits.h as\r
-    0x7fffffffffffffff, or in other words, the largest positive value\r
-    that can be stored in a 64-bit signed integer) is used to\r
-    represent an interval of unknown length.\r
-\r
-    The string representations of a data value of Interval type are\r
-    defined as follows for English (US):\r
-\r
-    - "(Unknown)" if the value is _I64_MAX\r
-\r
-    - "(Expired)" if the value is less than zero\r
-\r
-    - "%d days %d hours" if the value is greater than 24 hours\r
-\r
-    - "%d hours %d mins" if the value is greater than 1 hour\r
-\r
-    - "%d mins %d secs" if the value is greater than 1 minute\r
-\r
-    - "%d seconds" otherwise\r
-\r
-    \subsubsection kcdb_pg_idt_i32 Int32\r
-\r
-    Type identifier : ::KCDB_TYPE_INT32\r
-\r
-    A signed 32-bit integer.\r
-\r
-    \subsubsection kcdb_pg_idt_i64 Int64\r
-\r
-    Type identifier : ::KCDB_TYPE_INT64\r
-\r
-    A signed 64-bit integer.\r
-\r
-    \subsubsection kcdb_pg_idt_da Data\r
-\r
-    Type identifier : ::KCDB_TYPE_DATA\r
-\r
-    Raw data.  Can contain a byte stream.  This data type can be used\r
-    by plug-ins to associate raw data with a credential.  However,\r
-    there is no built-in string representation for this data type.  As\r
-    such, this is not meant to be used for storing anything that has\r
-    to be displayed to the user verbatim.\r
-\r
-    \section kcdb_pg_cust Custom data types\r
-\r
-    \subsection kcdb_pg_cb Custom data type call backs\r
-\r
-    Custom data types in the Network Identity Manager Credentials\r
-    Database are defined using \a kcdb_type structures that must\r
-    include several callback functions.  The expected behavior of\r
-    these callback functions is documented below.\r
-\r
-    \subsubsection kcdb_pg_cb_ts toString\r
-\r
-    \code\r
-      khm_int32   toString(\r
-        const void * data,\r
-        khm_int32 cb_data,\r
-        wchar_t *buffer,\r
-        khm_int32 *pcb_buffer,\r
-        khm_int32 flags);\r
-    \endcode\r
-\r
-    Produce the localized string representation of the object pointed to by\r
-    \a data.  The size of the data block is specified by the \a cb_data\r
-    parameter.  If the data type specified the \a KCDB_TYPE_FLAG_CB_AUTO flag\r
-    then \a cb_data can be \a KCDB_CBSIZE_AUTO, in which case the size of the\r
-    data block is to be inferred.\r
-\r
-    \a toString should assume that the block of data pointed to by \a data is\r
-    valid for this data type.\r
-\r
-    The \a pcb_buffer parameter is always a valid pointer to an \a khm_int32\r
-    variable.\r
-\r
-    The \a buffer parameter is a pointer to a \a wchar_t buffer which is to\r
-    receive the unicode string representing the object.  \a buffer may be\r
-    \a NULL, in which case the required size of the buffer should be returned\r
-    in \a pcb_buffer.  In this case, the function should return\r
-    \a KHM_ERROR_TOO_LONG.\r
-\r
-    If the \a buffer parameter is not \a NULL and the \a pcb_buffer specifies\r
-    that the buffer is large enough to hold the string representation, the\r
-    function should copy the string representation to the buffer, set the\r
-    \a pcb_buffer to the number of bytes that were copied including the\r
-    terminating \a NULL, and return \a KHM_ERROR_SUCCESS.\r
-\r
-    If the \a buffer parameter is not \a NULL and the \a pcb_buffer specifies\r
-    a buffer that is not large enough, the function should set \a pcb_buffer\r
-    to the required size (including the terminating \a NULL) and then return\r
-    \a KHM_ERROR_TOO_LONG.\r
-\r
-    \subsubsection kcdb_pg_cb_cmp comp\r
-\r
-    \code\r
-      khm_int32 comp(\r
-        const void * data1,\r
-        khm_int32 cb_data1,\r
-        const void * data2,\r
-        khm_int32 cb_d2);\r
-    \endcode\r
-\r
-    Compares two objects and returns a value indicating the relative ordering.\r
-\r
-    Since the KCDB does not interpret any data type, it relies on a loose\r
-    definition of what a relative ordering is.  It is left up to each data\r
-    type callback to interpret what 'ascending' and 'descending' mean.\r
-\r
-    The return value \a r should be as follows:\r
-\r
-    \a r < 0 : if \a data1 < \a data2\r
-\r
-    \a r > 0 : if \a data1 > \a data2\r
-\r
-    \a r = 0 : if \a data1 = \a data2 or no relative ordering can be determined\r
-    for the two objects \a data1 and \a data2.\r
-\r
-    The function should assume that both objects are valid for this data type.\r
-\r
-    The size specifiers \a cb_data1 and \a cb_data2 can (either or both) be\r
-    \a KCDB_CBSIZE_AUTO if the data type specified \a KCDB_TYPE_FLAG_CB_AUTO\r
-    flag.\r
-\r
-    \subsubsection kcdb_pg_cb_dup dup\r
-\r
-    \code\r
-      khm_int32 dup(\r
-        const void * d_src,\r
-        khm_int32 cb_src,\r
-        void * d_dst,\r
-        khm_int32 * pcb_dst);\r
-    \endcode\r
-\r
-    Duplicate an object.  The object pointed to by \a d_src is to be copied to\r
-    the buffer pointed to by \a d_dst.  The function is to assume that \a d_src\r
-    object is valid.  The size specifier \a cb_src may be \a KCDB_CBSIZE_AUTO\r
-    if \a KCDB_TYPE_FLAG_CB_AUTO was specified for the data type.\r
-\r
-    If \a d_dst pointer is \a NULL, then the required buffer size should be\r
-    returned in \a pcb_dst.  In this case, the function itself should return\r
-    \a KHM_ERROR_TOO_LONG.  The same behavior should occur if \a d_dst is non\r
-    \a NULL and \a pcb_dst indicates that the buffer is not sufficient.\r
-\r
-    If \a d_dst is not \a NULL and \a pcb_dst indicates that the buffer is\r
-    sufficient, then a copy of the object in \a d_src should be placed in\r
-    \a d_dst.  The function shold return \a KHM_ERROR_SUCCESS and set\r
-    \a pcb_dst to the number of bytes that were copied.\r
-\r
-    This callback will only be called when the credentials database is\r
-    retrieving objects from the outside.  Once it receives an object it may be\r
-    copied or moved as required.  Hence the object should not assume to reside\r
-    in a specific location of memory.  Also, \a dup is not intended to perform\r
-    such functions as reference counting which require knowledge of a precise\r
-    number of instances of an object, as the credentials database may copy\r
-    the object simply by copying the block of memory.\r
-\r
-    Note that whenever \a pcb_dst is to be set, it MUST be set to a valid byte\r
-    count.  It can not be assigned \a KCDB_CBSIZE_AUTO even if the data type\r
-    supports it.  The \a pcb_dst parameter is used internally to allocate\r
-    memory for the object.\r
-    \r
-    \subsubsection kcdb_pg_cb_iv isValid\r
-\r
-    \code\r
-      khm_boolean isValid(\r
-        const void * data,\r
-        khm_int32 cb_data);\r
-    \endcode\r
-\r
-    Checks if the object pointed to by the \a data pointer is a valid object\r
-    for this data type.  If the data type specified the \a KCDB_TYPE_CB_AUTO\r
-    flag, then the \a cb_data parameter may be \a KCDB_CBSIZE_AUTO, in which\r
-    the size of the object should be inferred from the data.\r
-\r
-    The function should be able to determine the validity of the object and\r
-    return \a TRUE if it is valid.  Return \a FALSE if it isn't, or if the\r
-    size of the object can not be inferred from the given data, or if the\r
-    inferred size exceeds \a KCDB_TYPE_MAXCB.\r
-\r
-*/\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 Network Identity Manager
+
+    Network Identity Manager'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
+
+    Type identifier : ::KCDB_TYPE_VOID
+
+    The Void data type is used to indicate that the associated object
+    does not contain any data.
+
+    \subsubsection kcdb_pg_idt_s String
+
+    Type identifier : ::KCDB_TYPE_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
+
+    Type identifier : ::KCDB_TYPE_DATE
+
+    Dates and times in Network Identity Manager are stored in \a
+    FILETIME structures.  Utility functions are provided for
+    converting from other formats such as \a time_t.
+
+    \subsubsection kcdb_pg_idt_i Interval
+
+    Type identifier : ::KCDB_TYPE_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 designates an interval of time.
+
+    The special value _I64_MAX (which is defined in limits.h as
+    0x7fffffffffffffff, or in other words, 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
+
+    Type identifier : ::KCDB_TYPE_INT32
+
+    A signed 32-bit integer.
+
+    \subsubsection kcdb_pg_idt_i64 Int64
+
+    Type identifier : ::KCDB_TYPE_INT64
+
+    A signed 64-bit integer.
+
+    \subsubsection kcdb_pg_idt_da Data
+
+    Type identifier : ::KCDB_TYPE_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 Network Identity Manager Credentials
+    Database are defined using \a kcdb_type structures that 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.
+
+*/
index a9949aedb62f79d87a87a9cdcb3319ad9c1f93d2..97ac0c2af821538da1a3bf3fd7e8b00a6ae65977 100644 (file)
@@ -1,36 +1,36 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/*! \page cred Credentials Providers \r
-\r
-    \section cred_contents Contents\r
-\r
-    - \subpage cred_data_types\r
-    - \subpage cred_acq\r
-    - \subpage cred_prop_pages\r
-    - \subpage cred_msgs\r
-*/\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
+*/
index 6d7cd3d1b23fc44645ff3944a374cc1d19a46991..405acef454334f199b34907cb824f5d7b9207bb6 100644 (file)
@@ -1,48 +1,48 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/*! \page cred_msgs Handling credentials provider messages\r
-\r
-A credentials provider plug-in receives a number of messages during the\r
-course of execution.  This section describes the appropriate ways of\r
-handling these messages.\r
-\r
-\section pi_credmsg_system System mesages\r
-\r
-There are only two system messages that a credentials provider needs\r
-to handle.  Both of these are explained elsewhere as they deal with\r
-initialization and uninitialization of the plug-in.  See the following\r
-two sections for details on handling these messages.\r
-\r
-- <::KMSG_SYSTEM,::KMSG_SYSTEM_INIT> \ref pi_pt_cred_init\r
-- <::KMSG_SYSTEM,::KMSG_SYSTEM_EXIT> \ref pi_pt_cred_exit\r
-\r
-\section pi_credmsg_cred Credential messages\r
-\r
-\r
-\r
-*/\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 plug-in 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 plug-in.  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
+
+
+
+*/
index fd1da5ad60059306b86fded128d0b19fda46126a..ac82e1c5d9b892660a3174ab50c17df5b2b3f32d 100644 (file)
@@ -1,84 +1,84 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/*! \page cred_prop_pages Property Pages for Credentials\r
-\r
-   This section describes the logistics of property pages.  When a\r
-   user selects the 'Properties' option from a menu (either the File\r
-   menu or a context menu), then a KHUI_ACTION_PROPERTIES action is\r
-   triggered.  This is handled by the credentials window and triggers\r
-   the launch of a property sheet if there is a valid context to\r
-   extract properties from.\r
-\r
-   Sequence of actions:\r
-\r
-   - KHUI_ACTION_PROPERTIES action is triggered.\r
-\r
-   - The main window dispatches the action to the credentials window.\r
-\r
-   - If there is a valid context, then the credentials window calls\r
-     khui_ps_create_sheet() to create an empty property sheet\r
-     structure of type ::khui_property_sheet.  The \a ctx member of\r
-     the structure is populated with the property context obtained\r
-     through khui_context_get().\r
-\r
-   - A global message is broadcast of type\r
-     <::KMSG_CRED,::KMSG_CRED_PP_BEGIN> with the parameter blob that\r
-     is a pointer to the ::khui_property_sheet structure.\r
-\r
-   - Subscribers to <::KMSG_CRED> messages handle the message, check\r
-     the \a ctx member of the structure and determine whether or not\r
-     and what type property pages to add to the property sheet.  New\r
-     property sheets are added by calling khui_ps_add_page().\r
-\r
-   - Once all the pages are added, a\r
-     <::KMSG_CRED,::KMSG_CRED_PP_PRECREATE> message is broadcast.\r
-     This is a chance for the property page providers to do any\r
-     processing before the property page is created.\r
-\r
-   - The property sheet is created and made visible with a call to\r
-     khui_ps_show_sheet().\r
-\r
-   - The Network Identity Manager message loop takes over.  Further interaction\r
-     including notifications of 'Ok','Cancel','Apply' and other\r
-     property sheet related actions are handled through WIN32\r
-     messages.\r
-\r
-   - Once the user closes the property sheet, a\r
-     <::KMSG_CRED,::KMSG_CRED_PP_END> message is sent to all\r
-     subscribers.  Individual subscribers who added pages to the\r
-     property sheet must free up any associated resources at this\r
-     point.\r
-\r
-   - All the ::khui_property_page structures that were allocated as\r
-     well as the ::khui_property_sheet structure are freed up with a\r
-     call to khui_ps_destroy_sheet().\r
-\r
-The maximum number of property sheets that can be open at one time is\r
-currently set to 256.  Each property sheet can have a maximum of 16\r
-property pages.\r
- */\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 Network Identity Manager 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.
+ */
index 55ccb69dc21d0a217b8d71388267840ab8c72b30..58aece3fc5899b86264fc2e719aedecea44f243e 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/*! \mainpage Network Identity Manager\r
-\r
-    \image html khimaira_logo.png\r
-\r
-    \section main_dev Documentation for Developers\r
-\r
-    Network Identity Manager is a credentials manager, which is\r
-    capable of managing Kerberos v5, Kerberos v4, Andrew File System,\r
-    and Kerberized Certificate Authority credentials.  This document\r
-    describes the API that is implemented by the Khimaira framework\r
-    upon which Network Identity Manager is based.\r
-\r
-    See the following sections for more information :\r
-    - \subpage license\r
-    - \subpage bugs\r
-    - \subpage releases\r
-\r
-    &copy; 2004-2007 Massachusetts Institute of Technology\r
-\r
-    &copy; 2005-2007 Secure Endpoints Inc.\r
-*/\r
-\r
-/*!\r
-    \page license License agreement and credits\r
-\r
-    Network Identity Manager is distributed under the MIT License.\r
-\r
-    \section license_l MIT License\r
-\r
-    Copyright &copy; 2004,2005,2006,2007 Massachusetts Institute of Technology\r
-\r
-    Copyright &copy; 2005,2006,2007 Secure Endpoints Inc.\r
\r
-    Permission is hereby granted, free of charge, to any person\r
-    obtaining a copy of this software and associated documentation\r
-    files (the "Software"), to deal in the Software without\r
-    restriction, including without limitation the rights to use, copy,\r
-    modify, merge, publish, distribute, sublicense, and/or sell copies\r
-    of the Software, and to permit persons to whom the Software is\r
-    furnished to do so, subject to the following conditions:\r
-\r
-    The above copyright notice and this permission notice shall be\r
-    included in all copies or substantial portions of the Software.\r
-\r
-    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
-    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
-    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
-    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\r
-    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\r
-    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
-    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\r
-    DEALINGS IN THE SOFTWARE.\r
-\r
-    \section license_credits Credits\r
-\r
-    Network Identity Manager was developed at the Massachusetts Institute of\r
-    Technology in partnership with Secure Endpoints Inc.\r
-\r
-    <a href="http://web.mit.edu/is">Information Services and\r
-    Technology</a> at <a href="http://web.mit.edu">Massachusetts\r
-    Institute of Technology</a>\r
-\r
-    <a href="http://www.secure-endpoints.com">Secure Endpoints Inc.</a>\r
-*/\r
-\r
-/*! \page bugs Reporting bugs\r
-\r
-    Network Identity Manager bugs can be reported to \r
-    <a href="mailto:kfw-bugs@mit.edu">kfw-bugs@mit.edu</a> or \r
-    <a href="mailto:netidmgr@secure-endpoints.com">netidmgr@secure-endpoints.com</a>\r
-\r
-    When reporting bugs, please include as much information as\r
-    possible to help reproduce the problem.\r
-\r
-    \image html khimaira_logo_small.png\r
-*/\r
-\r
-/*! \page releases Prior releases\r
-\r
-    The following is a list of releases of Network Identity Manager.\r
-    Whenever there is an addition to the API or a significant change\r
-    in behavior, the API version is incremented.  A plug-in that is\r
-    developed against a particular version of the API will be\r
-    compatible with any release of Network Identity Manager that\r
-    implements that version of the API.\r
-\r
-    The Network Identity Manager version number is set as the file and\r
-    product version of <tt>nidmgr32.dll</tt>.\r
-\r
-    - <b>1.2.0.2</b> Kerberos for Windows 3.2 Beta 2 <em>Apr 11, 2007</em>\n\r
-      API version : <b>8</b>\r
-\r
-    - <b>1.2.0.1</b> <em>Apr 06, 2007</em>\n\r
-      API version : <b>8</b>\r
-\r
-    - <b>1.2.0.0</b> Kerberos for Windows 3.2 Beta 1 <em>Mar 29, 2007</em>\n\r
-      API version : <b>8</b>\r
-\r
-    - <b>1.1.11.0</b> <em>Mar 20, 2007</em>\n\r
-      API version : <b>8</b>\r
-\r
-    - <b>1.1.10.0</b> <em>Feb 28, 2007</em>\n\r
-      API version : <b>8</b>\r
-\r
-    - <b>1.1.9.0</b> <em>Jan 20, 2007</em>\n\r
-      API version : <b>7</b>\r
-\r
-    - <b>1.1.8.0</b> Kerberos for Windows 3.1 Final <em>Nov 22, 2006</em>\n\r
-      API version : <b>6</b>\r
-\r
-    - <b>1.1.6.0</b> Kerberos for Windows 3.1 Beta 4 <em>Nov 17, 2006</em>\n\r
-      API version : <b>6</b>\r
-\r
-    - <b>1.1.4.0</b> Kerberos for Windows 3.1 Beta 3 <em>Nov 08, 2006</em>\n\r
-      API version : <b>6</b>\r
-\r
-    - <b>1.1.2.0</b> <em>Oct 09, 2006</em>\n\r
-      API version : <b>6</b>\r
-\r
-    - <b>1.1.0.2</b> <em>Sep 21, 2006</em>\n\r
-      API version : <b>6</b>\r
-\r
-    - <b>1.1.0.1</b> <em>Jul 19, 2006</em>\n\r
-      API version : <b>5</b>\r
-\r
-    - <b>1.1.0.0</b> <em>Mar 08, 2006</em>\n\r
-      API version : <b>5</b>\r
-\r
-    - <b>1.0.0.0</b> Kerberos for Windows 3.0 <em>Dec 05, 2005</em>\n\r
-      API version : <b>4</b>\r
-\r
-    - <b>0.1.2.0</b> Second Alpha release <em>Nov 30, 2005</em>\n\r
-      API version : <b>3</b>\n\r
-      Released along with Kerberos for Windows 3.0 beta 2.\r
-\r
-    - <b>0.1.1</b> First Alpha release <em>Nov 01, 2005</em>\n\r
-      Released along with Kerberos for Windows 3.0 beta.\r
-\r
-*/\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 Network Identity Manager
+
+    \image html khimaira_logo.png
+
+    \section main_dev Documentation for Developers
+
+    Network Identity Manager is a credentials manager, which is
+    capable of managing Kerberos v5, Kerberos v4, Andrew File System,
+    and Kerberized Certificate Authority credentials.  This document
+    describes the API that is implemented by the Khimaira framework
+    upon which Network Identity Manager is based.
+
+    See the following sections for more information :
+    - \subpage license
+    - \subpage bugs
+    - \subpage releases
+
+    &copy; 2004-2007 Massachusetts Institute of Technology
+
+    &copy; 2005-2007 Secure Endpoints Inc.
+*/
+
+/*!
+    \page license License agreement and credits
+
+    Network Identity Manager is distributed under the MIT License.
+
+    \section license_l MIT License
+
+    Copyright &copy; 2004,2005,2006,2007 Massachusetts Institute of Technology
+
+    Copyright &copy; 2005,2006,2007 Secure Endpoints Inc.
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+    WHETHER IN AN ACTION 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
+
+    Network Identity Manager was developed at the Massachusetts Institute of
+    Technology in partnership with Secure Endpoints Inc.
+
+    <a href="http://web.mit.edu/is">Information Services and
+    Technology</a> at <a href="http://web.mit.edu">Massachusetts
+    Institute of Technology</a>
+
+    <a href="http://www.secure-endpoints.com">Secure Endpoints Inc.</a>
+*/
+
+/*! \page bugs Reporting bugs
+
+    Network Identity Manager bugs can be reported to 
+    <a href="mailto:kfw-bugs@mit.edu">kfw-bugs@mit.edu</a> or 
+    <a href="mailto:netidmgr@secure-endpoints.com">netidmgr@secure-endpoints.com</a>
+
+    When reporting bugs, please include as much information as
+    possible to help reproduce the problem.
+
+    \image html khimaira_logo_small.png
+*/
+
+/*! \page releases Prior releases
+
+    The following is a list of releases of Network Identity Manager.
+    Whenever there is an addition to the API or a significant change
+    in behavior, the API version is incremented.  A plug-in that is
+    developed against a particular version of the API will be
+    compatible with any release of Network Identity Manager that
+    implements that version of the API.
+
+    The Network Identity Manager version number is set as the file and
+    product version of <tt>nidmgr32.dll</tt>.
+
+    - <b>1.2.0.2</b> Kerberos for Windows 3.2 Beta 2 <em>Apr 11, 2007</em>\n
+      API version : <b>8</b>
+
+    - <b>1.2.0.1</b> <em>Apr 06, 2007</em>\n
+      API version : <b>8</b>
+
+    - <b>1.2.0.0</b> Kerberos for Windows 3.2 Beta 1 <em>Mar 29, 2007</em>\n
+      API version : <b>8</b>
+
+    - <b>1.1.11.0</b> <em>Mar 20, 2007</em>\n
+      API version : <b>8</b>
+
+    - <b>1.1.10.0</b> <em>Feb 28, 2007</em>\n
+      API version : <b>8</b>
+
+    - <b>1.1.9.0</b> <em>Jan 20, 2007</em>\n
+      API version : <b>7</b>
+
+    - <b>1.1.8.0</b> Kerberos for Windows 3.1 Final <em>Nov 22, 2006</em>\n
+      API version : <b>6</b>
+
+    - <b>1.1.6.0</b> Kerberos for Windows 3.1 Beta 4 <em>Nov 17, 2006</em>\n
+      API version : <b>6</b>
+
+    - <b>1.1.4.0</b> Kerberos for Windows 3.1 Beta 3 <em>Nov 08, 2006</em>\n
+      API version : <b>6</b>
+
+    - <b>1.1.2.0</b> <em>Oct 09, 2006</em>\n
+      API version : <b>6</b>
+
+    - <b>1.1.0.2</b> <em>Sep 21, 2006</em>\n
+      API version : <b>6</b>
+
+    - <b>1.1.0.1</b> <em>Jul 19, 2006</em>\n
+      API version : <b>5</b>
+
+    - <b>1.1.0.0</b> <em>Mar 08, 2006</em>\n
+      API version : <b>5</b>
+
+    - <b>1.0.0.0</b> Kerberos for Windows 3.0 <em>Dec 05, 2005</em>\n
+      API version : <b>4</b>
+
+    - <b>0.1.2.0</b> Second Alpha release <em>Nov 30, 2005</em>\n
+      API version : <b>3</b>\n
+      Released along with Kerberos for Windows 3.0 beta 2.
+
+    - <b>0.1.1</b> First Alpha release <em>Nov 01, 2005</em>\n
+      Released along with Kerberos for Windows 3.0 beta.
+
+*/
index a10af2a6f4f89e3ca5808f211089ef4270b68b23..5f225b81127a7d7544b094af66db0a01955eb592 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/*!\r
-\page pi_framework Plug-in Framework\r
-\r
-\section pi_fw_pnm Introduction to Plug-ins, Modules and Messages\r
-\r
-\subsection pi_fw_pnm_p Plug-ins\r
-\r
-A plug-in is a package that implements a defined API that will perform\r
-credentials management or related tasks on behalf of Network Identity\r
-Manager.\r
-\r
-The Network Identity Manager architecture is message based.  The core\r
-of each plug-in is a message handler.  The plug-in integrates with the\r
-application by subscribing to, and handling specific types of\r
-messages.\r
-\r
-The plug-in message handler runs in its own thread and receive\r
-asynchronous messages.  There are exceptions, such as when one plug-in\r
-requires another plug-in to handle a specific message before it can\r
-handle the message.  In addition, during certain operations that\r
-require user interaction, each plug-in can delegate code that will run\r
-in the main application thread (the user interface thread) to process\r
-window messages.\r
-\r
-\subsection pi_fw_pnw_m Modules\r
-\r
-One or more plug-ins can be bundled together into a module.  A module\r
-is a dynamically loadable library which exports a specific set of\r
-callbacks.  Currently, the only two required callbacks for a module\r
-are :\r
-\r
-- init_module(), and\r
-- exit_module()\r
-\r
-For more information about how a module is structured, see \ref\r
-pi_structure .\r
-\r
-\subsection pi_fw_pnm_msg Messages and Message Queues\r
-\r
-An integral part of this framework is the messaging system.  Most of\r
-the communication between the Network Identity Manager application and\r
-plug-ins is conducted through passing messages.\r
-\r
-A message has a type and subtype and is denoted in this documentation\r
-as \< \e message_type, \e message_subtype\>.  For example, when a\r
-plug-in is loaded, the first message it receives is \< ::KMSG_SYSTEM,\r
-::KMSG_SYSTEM_INIT \>.\r
-\r
-Each thread in the application, specially threads that were created\r
-for individual plug-in messages handlers, has an associated message\r
-queue that stores and manages all the messages that have been sent to\r
-subscribers in that thread.\r
-\r
-The most common recipient of a message is a message callback function\r
-(see ::kmq_callback_t ). The message handler for a plug-in is one such\r
-example. A message callback function receives the message type,\r
-subtype and two optional parameters for the message.\r
-\r
-Any acceptable recipient can subscribe to broadcast messages of any\r
-type.  Once subscribed, whenever a message of that type is broadcast,\r
-the message will get queued on the corresponding message queue.  Then,\r
-one of the dispatch functions can dispatch the message to the correct\r
-callback function. (see ::kmq_dispatch).\r
-\r
-Next \subpage pi_fw_pm ...\r
-\r
-*/\r
-\r
-/*!\r
-\r
-\page pi_fw_pm Module Manager\r
-\r
-The module manager is tasked with loading, unloading and managing the\r
-plug-in message processing. It maintains a separate thread for loading\r
-and registering modules.  When a module is successfully loaded and it\r
-registers one or more plug-ins, a new thread is created for each\r
-plug-in.  Plug-in specific initialization and other callback functions\r
-are called from within this new thread.  This is to prevent one\r
-plug-in from "hanging" other plug-ins and the main Network Identity\r
-Manager user interface threads.\r
-\r
-Read more :\r
-- \ref pi_structure\r
-\r
-\subsection pi_fw_pm_load Load sequence\r
-\r
-When kmm_load_module() is called to load a specific module, the\r
-following sequence of events occur:\r
-\r
-- The registration information for the module is located on the\r
-  registry key \c\r
-  \Software\MIT\NetIDMgr\PluginManager\Modules\[ModuleName].\r
-\r
-- The \c ImagePath value from the registration information is used to\r
-  locate the module binary.  If it is not an absolute path, then the\r
-  binary is located using the standard system search path starting\r
-  from the directory in which Network Identity Manager binaries are\r
-  located.\r
-\r
-- The binary is loaded into the address space of Network Identity\r
-  Manager along with any dependencies not already loaded.\r
-\r
-- If the Network Identity Manager core binary is signed, then the\r
-  signature is checked against the system and user certificate stores.\r
-  If this fails, the module is unloaded. See \ref pi_fw_pm_unload.\r
-\r
-- The init_module() entry point for the loaded module is called.  If\r
-  this function returns an error or if no plug-ins are registered,\r
-  then the module is immediately unloaded. See \ref pi_fw_pm_unload.\r
-\r
-- During processing of init_module(), if any localized resource\r
-  libraries are specified using kmm_set_locale_info(), then one of the\r
-  localized libraries will be loaded. See \ref pi_localization\r
-\r
-- During processing of init_module(), the module registers all the\r
-  plug-ins that it is implementing by calling kmm_register_plugin() for\r
-  each.\r
-\r
-- Once init_module() returns, each plug-in is initialized.  The method\r
-  by which a plug-in is initialized depends on the plug-in type.  The\r
-  initialization code for the plug-in may indicate that it didn't\r
-  initialize properly, in which case the plug-in is immediately\r
-  unregistered.  No further calls are made to the plug-in.\r
-\r
-- If no plug-in is successfully loaded, the module is unloaded. See\r
-  \ref pi_fw_pm_unload.\r
-\r
-- During normal operation, any registered plug-ins for a module can be\r
-  unloaded explicitly, or the plug-in itself may signal that it should\r
-  be unloaded.  If at anytime, all the plug-ins for the module are\r
-  unloaded, then the module itself is also unloaded unless the \c\r
-  NoUnload registry value is set in the module key.\r
-\r
-\subsection pi_fw_pm_unload Unload sequence\r
-\r
-- For each of the plug-ins that are registered for a module, the exit\r
-  code is invoked.  The method by which this happens depends on the\r
-  plug-in type.  The plug-in is not given a chance to veto the\r
-  decision to unload. Each plug-in is responsible for performing\r
-  cleanup tasks, freeing resources and unsubscribing from any message\r
-  classes that it has subscribed to.\r
-\r
-- The exit_module() entry point is called for the module.\r
-\r
-- If any localized resource libraries were loaded for the module, they\r
-  are unloaded.\r
-\r
-- The module is unloaded.\r
-\r
-The following diagram illustrates the relationship between modules and\r
-plug-ins as implemented in the Kerberos 5 plug-in distributed with\r
-Network Identity Manager.\r
-\r
-\image html modules_plugins_krb5.png\r
-\r
- */\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 Plug-in Framework
+
+\section pi_fw_pnm Introduction to Plug-ins, Modules and Messages
+
+\subsection pi_fw_pnm_p Plug-ins
+
+A plug-in is a package that implements a defined API that will perform
+credentials management or related tasks on behalf of Network Identity
+Manager.
+
+The Network Identity Manager architecture is message based.  The core
+of each plug-in is a message handler.  The plug-in integrates with the
+application by subscribing to, and handling specific types of
+messages.
+
+The plug-in message handler runs in its own thread and receive
+asynchronous messages.  There are exceptions, such as when one plug-in
+requires another plug-in to handle a specific message before it can
+handle the message.  In addition, during certain operations that
+require user interaction, each plug-in can delegate code that will run
+in the main application thread (the user interface thread) to process
+window messages.
+
+\subsection pi_fw_pnw_m Modules
+
+One or more plug-ins can be bundled together into a module.  A module
+is a dynamically loadable library which exports a specific set of
+callbacks.  Currently, the only two required callbacks for a module
+are :
+
+- init_module(), and
+- exit_module()
+
+For more information about how a module is structured, see \ref
+pi_structure .
+
+\subsection pi_fw_pnm_msg Messages and Message Queues
+
+An integral part of this framework is the messaging system.  Most of
+the communication between the Network Identity Manager application and
+plug-ins is conducted through passing messages.
+
+A message has a type and subtype and is denoted in this documentation
+as \< \e message_type, \e message_subtype\>.  For example, when a
+plug-in is loaded, the first message it receives is \< ::KMSG_SYSTEM,
+::KMSG_SYSTEM_INIT \>.
+
+Each thread in the application, specially threads that were created
+for individual plug-in messages handlers, has an associated message
+queue that stores and manages all the messages that have been sent to
+subscribers in that thread.
+
+The most common recipient of a message is a message callback function
+(see ::kmq_callback_t ). The message handler for a plug-in is one such
+example. A message callback function receives the message type,
+subtype and two optional parameters for the message.
+
+Any acceptable recipient can subscribe to broadcast messages of any
+type.  Once subscribed, whenever a message of that type is broadcast,
+the message will get queued on the corresponding message queue.  Then,
+one of the dispatch functions can dispatch the message to the correct
+callback function. (see ::kmq_dispatch).
+
+Next \subpage pi_fw_pm ...
+
+*/
+
+/*!
+
+\page pi_fw_pm Module Manager
+
+The module manager is tasked with loading, unloading and managing the
+plug-in message processing. It maintains a separate thread for loading
+and registering modules.  When a module is successfully loaded and it
+registers one or more plug-ins, a new thread is created for each
+plug-in.  Plug-in specific initialization and other callback functions
+are called from within this new thread.  This is to prevent one
+plug-in from "hanging" other plug-ins and the main Network Identity
+Manager user interface threads.
+
+Read more :
+- \ref pi_structure
+
+\subsection pi_fw_pm_load Load sequence
+
+When kmm_load_module() is called to load a specific module, the
+following sequence of events occur:
+
+- The registration information for the module is located on the
+  registry key \c
+  \Software\MIT\NetIDMgr\PluginManager\Modules\[ModuleName].
+
+- The \c ImagePath value from the registration information is used to
+  locate the module binary.  If it is not an absolute path, then the
+  binary is located using the standard system search path starting
+  from the directory in which Network Identity Manager binaries are
+  located.
+
+- The binary is loaded into the address space of Network Identity
+  Manager along with any dependencies not already loaded.
+
+- If the Network Identity Manager 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.
+
+- The init_module() entry point for the loaded module is called.  If
+  this function returns an error or if no plug-ins are registered,
+  then the module is immediately 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
+  plug-ins that it is implementing by calling kmm_register_plugin() for
+  each.
+
+- Once init_module() returns, each plug-in is initialized.  The method
+  by which a plug-in is initialized depends on the plug-in type.  The
+  initialization code for the plug-in may indicate that it didn't
+  initialize properly, in which case the plug-in is immediately
+  unregistered.  No further calls are made to the plug-in.
+
+- If no plug-in is successfully loaded, the module is unloaded. See
+  \ref pi_fw_pm_unload.
+
+- During normal operation, any registered plug-ins for a module can be
+  unloaded explicitly, or the plug-in itself may signal that it should
+  be unloaded.  If at anytime, all the plug-ins for the module are
+  unloaded, then the module itself is also unloaded unless the \c
+  NoUnload registry value is set in the module key.
+
+\subsection pi_fw_pm_unload Unload sequence
+
+- For each of the plug-ins that are registered for a module, the exit
+  code is invoked.  The method by which this happens depends on the
+  plug-in type.  The plug-in is not given a chance to veto the
+  decision to unload. Each plug-in is responsible for performing
+  cleanup tasks, freeing resources and unsubscribing from any message
+  classes that it has subscribed to.
+
+- The exit_module() entry point is called for the module.
+
+- If any localized resource libraries were loaded for the module, they
+  are unloaded.
+
+- The module is unloaded.
+
+The following diagram illustrates the relationship between modules and
+plug-ins as implemented in the Kerberos 5 plug-in distributed with
+Network Identity Manager.
+
+\image html modules_plugins_krb5.png
+
+ */
index 646f8957040f24b59123b3137fee69bddcb55323..3c6a236e69afe034c9db5d0191c078275e813847 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/*!\r
-\page pi_localization Localization\r
-\r
-If a module requires localized resources, it can register the\r
-localized resource libraries with the module manager when it receives\r
-the init_module() callback.  Note that you can only register localized\r
-resource libraries during init_module().\r
-\r
-The localized resource library is global to a module.  Each plug-in is\r
-not allowed to define its own localization library, although it is\r
-free to load and use any library as it sees fit.  The module manager\r
-does not manage these libraries for the plug-in.\r
-\r
-\section pi_loc_spec Specification of localized resources\r
-\r
-In order to register localized resource libraries, a module calls\r
-kmm_set_locale_info().  The \a locales parameter to the function holds\r
-a pointer to an array of ::kmm_module_locale records.  Each record\r
-specifies one language code and a filename of a library that holds the\r
-language resources for that language.\r
-\r
-It is recommended that you use the LOCALE_DEF convenience macro when\r
-defining locale records for use with kmm_set_locale_info().  This will\r
-ensure that future changes in the API will only minimally affect your\r
-code.  For example:\r
-\r
-\code\r
-kmm_module_locale my_locales[] = {\r
-LOCALE_DEF(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US), L"english.dll", KMM_MLOC_FLAG_DEFAULT),\r
-LOCALE_DEF(MAKELANGID(LANG_DUTCH,SUBLANG_DUTCH), L"dutch.dll", 0),\r
-LOCALE_DEF(MAKELANGID(LANG_SPANISH,SUBLANG_SPANISH_MODERN), L"spanish.dll", 0)\r
-};\r
-\r
-int n_locales = sizeof(my_locales)/sizeof(my_locales[0]);\r
-\r
-...\r
-\r
-kmm_set_locale_info(h_module, my_locales, n_locales);\r
-\r
-...\r
-\endcode\r
-\r
-See kmm_set_locale_info() and ::kmm_module_locale for more info.\r
-\r
-\section pi_loc_how Selection of localized resource library\r
-\r
-The module manager searches the array of ::kmm_module_locale objects\r
-passed into the kmm_set_locale_info() function for one that matches\r
-the current user locale (as opposed to the current system locale).  A\r
-record matches the locale if it has the same language ID.  \r
-\r
-If a match is found, that library is selected.  Otherwise, the list is\r
-searched for one that is compatible with the current user locale.  A\r
-locale record is compatible with the user locale if the primary\r
-language matches.\r
-\r
-If a match is still not found, the first record in the locale array\r
-that has the ::KMM_MLOC_FLAG_DEFAULT flag set will be selected.\r
-\r
-If a match is still not found, then the kmm_set_locale_info() will\r
-return ::KHM_ERROR_NOT_FOUND.\r
-\r
-\section pi_loc_usage Using localization\r
-\r
-The following convenience macros are available for using a module\r
-handle to load resources from the corresponding resource library.\r
-However, for performance reasons, it is advisable to obtain a handle\r
-to the resource library loaded by the module manager using\r
-kmm_get_resource_module() and then use it to access resources using\r
-the regular WIN32 API.\r
-\r
-- ::kmm_LoadAccelerators\r
-- ::kmm_LoadBitmap\r
-- ::kmm_LoadCursor\r
-- ::kmm_LoadIcon\r
-- ::kmm_LoadImage\r
-- ::kmm_LoadMenu\r
-- ::kmm_LoadString\r
-\r
-*/\r
-\r
-\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 plug-in 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 plug-in.
+
+\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
+
+*/
+
+
index 70040b28531902fd154ce0f7badc49578866af90..9542150a822576716d41ac41d9be598892de5716 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/*!\r
-\r
-\page plug-ins Network Identity Manager Modules and Plug-ins\r
-\r
-  The Network Identity Manager application does not include any\r
-  ability to manage any specific type of credential.  Instead it\r
-  exposes a framework on which plug-ins can be implemented to manage\r
-  credentials.\r
-\r
-  Plug-ins and localization are handled by the Network Identity\r
-  Manager Module Manager API.\r
-\r
-  The following sections describe plug-ins in detail:\r
-\r
-  - \subpage pi_framework\r
-  - \subpage pi_pt\r
-  - \subpage pi_structure\r
-  - \subpage pi_localization\r
-*/\r
-\r
-/*! \page pi_pt Plug-in Types\r
-\r
-The types of plug-ins that are currently supported by Network Identity\r
-Manager are :\r
-\r
-\section pi_pt_cred Credential Provider\r
-\r
-A credential provider plug-in essentially acts as an interface between\r
-Network Identity Manager and some entity which defines the credentials\r
-for the purpose of managing those credentials.\r
-\r
-There can be more than one credential provider in a module.\r
-\r
-\subsection pi_pt_cred_comm Communication\r
-\r
-Communication between Network Identity Manager and a credential\r
-provider occurs through a message processor.  When registering a\r
-credential provider, the module initialization code in init_module()\r
-specifies ::KHM_PITYPE_CRED as the \a type member and sets \a msg_proc\r
-member to a valid message processor in the ::khm_plugin record.\r
-\r
-\subsection pi_pt_cred_init Initialization\r
-\r
-Once init_module() has completed, the module manager sends a\r
-<::KMSG_SYSTEM,::KMSG_SYSTEM_INIT> message to the message processor.\r
-\r
-For credential provider plug-ins, <::KMSG_SYSTEM,::KMSG_SYSTEM_INIT> is\r
-guaranteed to be the first message it receives.\r
-\r
-The callback function should return KHM_ERROR_SUCCESS if it\r
-initializes properly or some other value otherwise.  If the return\r
-value signals an error, then the plug-in is assumed to have failed\r
-initialization and is immediately unloaded.\r
-\r
-The message processor is automatically subscribed to the following\r
-message types:\r
-- ::KMSG_SYSTEM\r
-- ::KMSG_KCDB\r
-\r
-Although a plug-in can use the <::KMSG_SYSTEM,::KMSG_SYSTEM_INIT>\r
-message enumerate existing credentials in the system, it should not\r
-obtain new credentials.  This is because other plug-ins that may depend\r
-on the new credential messages may not be loaded at this time. See the\r
-section on \ref cred_msgs for more information.\r
-\r
-\subsection pi_pt_cred_exit Uninitialization\r
-\r
-When the plug-in is to be removed, the module manager sends a\r
-<::KMSG_SYSTEM,::KMSG_SYSTEM_EXIT> to the message processor.  The\r
-plug-in must perform any necessary shutdown operations, free up\r
-resources and unsubscribe from any messages that it has subscribed to.\r
-\r
-This message is guaranteed to be the last message received by a\r
-credentials manager plug-in if the plug-in unsubsribes from all\r
-additional message classes that it subsribed to.\r
-\r
-The message types that the message processor is automatically\r
-subscribed to (See \ref pi_pt_cred_init) do not have to be\r
-unsubscribed from as they are automatically removed.\r
-\r
-\subsection pi_pt_cred_other Other Notes\r
-\r
-Since credential managers may receive privileged information, the\r
-signature requirements for credential managers are specially strict.\r
-\r
-*/\r
-\r
-\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 plug-ins Network Identity Manager Modules and Plug-ins
+
+  The Network Identity Manager application does not include any
+  ability to manage any specific type of credential.  Instead it
+  exposes a framework on which plug-ins can be implemented to manage
+  credentials.
+
+  Plug-ins and localization are handled by the Network Identity
+  Manager Module Manager API.
+
+  The following sections describe plug-ins in detail:
+
+  - \subpage pi_framework
+  - \subpage pi_pt
+  - \subpage pi_structure
+  - \subpage pi_localization
+*/
+
+/*! \page pi_pt Plug-in Types
+
+The types of plug-ins that are currently supported by Network Identity
+Manager are :
+
+\section pi_pt_cred Credential Provider
+
+A credential provider plug-in essentially acts as an interface between
+Network Identity Manager 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 Network Identity Manager 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 plug-ins, <::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 plug-in is assumed to have failed
+initialization and is immediately unloaded.
+
+The message processor is automatically subscribed to the following
+message types:
+- ::KMSG_SYSTEM
+- ::KMSG_KCDB
+
+Although a plug-in 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 plug-ins 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 plug-in is to be removed, the module manager sends a
+<::KMSG_SYSTEM,::KMSG_SYSTEM_EXIT> to the message processor.  The
+plug-in 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 plug-in if the plug-in 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.
+
+*/
+
+
index 452029e4f1093251a12c4709452f6e00fde6a982..68cee13e73af187c88191e8299d92318580dbc98 100644 (file)
@@ -1,55 +1,55 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/*!\r
-\r
-\page pi_structure Structure of a module\r
-\r
-A Network Identity Manager module is a dynamically loadable library\r
-with a specific set of exported symbols.  Each export symbol and\r
-general notes about writing a plug-in module are documented below.\r
-\r
-\section pi_str_reg  Registration and Version Information\r
-\r
-[TODO]\r
-\r
-\section pi_str_init Initialization\r
-\r
-Do not use DllMain or other system specific callback routines to\r
-perform intilization tasks other than creating mutexes, initializing\r
-thread local storage and other tasks that must be performed at that\r
-stage.  Specifically, do not call any Network Identity Manager API functions from\r
-within DllMain.\r
-\r
-\section pi_str_cb Callbacks\r
-\r
-The callbacks that must be implemented by a module are:\r
-\r
-- init_module()\r
-- exit_module()\r
-\r
- */\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 Network Identity Manager module is a dynamically loadable library
+with a specific set of exported symbols.  Each export symbol and
+general notes about writing a plug-in module are documented below.
+
+\section pi_str_reg  Registration and Version Information
+
+[TODO]
+
+\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 Network Identity Manager API functions from
+within DllMain.
+
+\section pi_str_cb Callbacks
+
+The callbacks that must be implemented by a module are:
+
+- init_module()
+- exit_module()
+
+ */
index 43bf3a94fbde764898f4d1e398de32331741d2e1..04f9aa6f2404bdf1a6e069e87f8aa75c49c02f9f 100644 (file)
@@ -1,30 +1,30 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/*! \page khui_actions Actions\r
-\r
- */\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
+
+ */
index 12843fb46a2f956398758b018db090d3454b867c..9799b5c4a09e8f25336537cdf4cec2804d95d451 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/*! \page khui_context Contexts\r
-\r
-    \section khui_context_contents Contents\r
-\r
-    - \ref khui_context_intro "Introduction"\r
-    - \subpage khui_context_using\r
-\r
-    \section khui_context_intro Introduction\r
-\r
-    Several ::KMSG_CRED messages and many messages depend on the\r
-    selections that the user has made on the user interface.  The UI\r
-    context functions and data structures provide access to this\r
-    information.\r
-\r
-    The Network Identity Manager user interface presents an outline view of all the\r
-    credentials that were provided by credentials providers.  This\r
-    view consists of headers representing the outline levels and rows\r
-    representing individual credentials.\r
-\r
-    Users can make multiple selections of credentials or headers from\r
-    this view.  If all the credentials and subheaders under a\r
-    particular outline level are selected, then the header itself is\r
-    automatically selected.  There may be multiple disjointed\r
-    selections of headers and credentials.\r
-\r
-    In addition, the current cursor position also acts as a selector.\r
-    The credential or header under the cursor may not actually be\r
-    selected.  The cursor is not the mouse pointer, but the focus\r
-    rectangle that may be moved either using the keyboard or by\r
-    clicking on a credential or header.\r
-\r
-    Thus there are two independent groups of selections:\r
-\r
-    - Credentials and headers which are in a selected state at some\r
-      specific point in time (the <b>current selection</b>).\r
-\r
-    - The current credential or selection which the cursor is on (the\r
-      <b>cursor selection</b>).\r
-\r
-    There are a few notes on how credentials are selected:\r
-\r
-    - An "empty" header (a header that does not contain any credential\r
-      rows) does not appear in a UI context.  However they can appear\r
-      as the current cursor context.\r
-\r
-    - At its current implementation, cursor selections of identity,\r
-      credential type, and individual credentials are treated as\r
-      special cases since they are the most common.\r
-\r
-    How the UI context is used when processing a specific action or\r
-    message depends on the action or message.  If an action operates\r
-    on a group of credentials, then the current selection may be used,\r
-    and on the other hand if an action or message relates to just one\r
-    credential, identity or credential type is invoked, then the\r
-    cursor selection is invoked.\r
-\r
-    For example, double-clicking a credential, or right clicking and\r
-    selecting 'Properties' from the context menu launches the property\r
-    window for a credential.  This operates on the cursor selection\r
-    since that reflects where the user double clicked.  However,\r
-    choosing 'Destroy' from the context menu invokes a command that\r
-    can be applied to a group of credential, and hence uses the\r
-    current selection.\r
-\r
-    Next: \ref khui_context_using "Using Contexts"\r
- */\r
-\r
-/*! \page khui_context_using Using Contexts \r
-\r
-    \section khui_context_using_1 Obtaining the context\r
-\r
-    Typically, messages sent by actions that rely on UI context will\r
-    obtain and store the context in a location that is accessible to\r
-    the handlers of the message.\r
-\r
-    If a plug-in needs to obtain the UI context, it should do so by\r
-    calling khui_context_get() and passing in a pointer to a\r
-    ::khui_action_context structure.\r
-\r
-    Once obtained, the contents of the ::khui_action_context structure\r
-    should be considered read-only.  When the plug-in is done with the\r
-    structure, it should call ::khui_context_release().  This cleans\r
-    up any additional memory allocated for storing the context as well\r
-    as releasing all the objects that were referenced from the\r
-    context.\r
-\r
-    \section khui_context_sel_ctx Selection context\r
-\r
-    The selection context is specified in the ::khui_action_context\r
-    structure in the \a sel_creds and \a n_sel_creds fields.  These\r
-    combined provide an array of handles to credentials which are\r
-    selected.\r
-\r
-    \note If \a n_sel_creds is zero, then \a sel_creds may be NULL.\r
-\r
-    \section khui_context_cur_ctx Cursor context\r
-\r
-    The scope of the cursor context is specified in the \a scope field\r
-    of the ::khui_action_context strucutre.  The scope can be one of:\r
-\r
-    - ::KHUI_SCOPE_NONE\r
-    - ::KHUI_SCOPE_IDENT\r
-    - ::KHUI_SCOPE_CREDTYPE\r
-    - ::KHUI_SCOPE_GROUP\r
-    - ::KHUI_SCOPE_CRED\r
-\r
-    Depending on the scope, several other members of the strucre may\r
-    also be set.\r
-\r
-    In general, the cursor context can be a single credential or an\r
-    entire outline level.  Unlike the selection context, since this\r
-    specifies a single point of selection it can not be disjointed.\r
-\r
-    The contents of the \a identity, \a cred_type, \a cred, \a headers\r
-    and \a n_headers are described in the documentation of each of the\r
-    scope values above.\r
-\r
-    \subsection khui_context_sel_ctx_grp KHUI_SCOPE_GROUP\r
-\r
-    The ::KHUI_SCOPE_GROUP scope is the generic scope which describes\r
-    a cursor selection that can not be simplified into any other\r
-    scope.\r
-\r
-    In this case, the selection is described by an array of\r
-    ::khui_header elements each of which specify a criterion for\r
-    narrowing down the selection of credentials.  The ::khui_header\r
-    structure specifies an attribute in the \a attr_id field and a\r
-    value in the \a data and \a cb_data fields.  The credentials that\r
-    are selected are those in the root credential set whose repective\r
-    attributes contain the values specified in each of the\r
-    ::khui_header elements.\r
-\r
-    For example, the following selection:\r
-\r
-    \image html credview-select-outline.jpg\r
-\r
-    will result in the following header specification:\r
-\r
-    \code\r
-    ctx.n_headers = 3;\r
-\r
-    ctx.headers[0].attr_id = KCDB_ATTR_LOCATION;\r
-    ctx.headers[0].data = L"grailauth@KHMTEST";\r
-    ctx.headers[0].cb_data = sizeof(L"grailauth@KHMTEST");\r
-\r
-    ctx.headers[1].attr_id = KCDB_ATTR_ID;\r
-    ctx.headers[1].data = &handle_to_identity;\r
-    ctx.headers[1].cb_data = sizeof(khm_handle);\r
-\r
-    ctx.headers[2].attr_id = KCDB_ATTR_TYPE;\r
-    ctx.headers[2].data = &kerberos_5_credtype;\r
-    ctx.headers[2].cb_data = sizeof(khm_int32);\r
-    \endcode\r
-\r
-    \note The attribute that is used to specify the header is not the\r
-        display attribute, but the canonical attribute.  For example,\r
-        in the above, the second header was actually\r
-        KCDB_ATTR_ID_NAME.  But KCDB_ATTR_ID was used since that is\r
-        the canonical source for KCDB_ATTR_ID_NAME.  See ::kcdb_attrib\r
-        for more information on canonical attributes.\r
-*/\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 Network Identity Manager 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 plug-in 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 plug-in 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.
+*/
index d7bc6be00fe91b7b8661fdf16bc05fc8f9539456..ae67008470b4f650441c7259741175b0f5710165 100644 (file)
@@ -1,36 +1,36 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/*! \page khui User Interface Topics\r
-\r
-    \section khui_contents Contents\r
-\r
-    - \subpage khui_actions\r
-    - \subpage khui_menus\r
-    - \subpage khui_context\r
-    - \subpage khui_htwnd\r
- */\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
+    - \subpage khui_htwnd
+ */
index 794aef6ca4e613e151d3daa9f5c5ec8376b54407..e46ee0840863858f6265d30783c9d15607fecef4 100644 (file)
@@ -1,30 +1,30 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/*! \page khui_menus Menus\r
-\r
- */\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
+
+ */
index fbfef6047ba6b68c7ca845001a63a6cf1bc8598d..f6fbd3f784ba28f81e81b14a2db14c5f10d9dc6d 100644 (file)
@@ -1,33 +1,33 @@
-#define IDH_WELCOME                1000\r
-#define IDH_WIN_MAIN               1001\r
-\r
-#define IDH_MENU_FILE              1501\r
-#define IDH_MENU_CRED              1502\r
-#define IDH_MENU_VIEW              1503\r
-#define IDH_MENU_OPTIONS           1504\r
-#define IDH_MENU_HELP              1505\r
-\r
-#define IDH_ACTION_PROPERTIES      2000\r
-#define IDH_ACTION_EXIT            2001\r
-#define IDH_ACTION_NEW_ID          2002\r
-#define IDH_ACTION_SET_DEF_ID      2003\r
-#define IDH_ACTION_SET_SRCH_ID     2004\r
-#define IDH_ACTION_DESTROY_ID      2005\r
-#define IDH_ACTION_RENEW_ID        2006\r
-#define IDH_ACTION_PASSWD_ID       2007\r
-#define IDH_ACTION_NEW_CRED        2008\r
-#define IDH_ACTION_CHOOSE_COLS     2009\r
-#define IDH_ACTION_DEBUG_WINDOW    2010\r
-#define IDH_ACTION_VIEW_REFRESH    2011\r
-#define IDH_ACTION_OPT_KHIM        2012\r
-#define IDH_ACTION_OPT_INIT        2013\r
-#define IDH_ACTION_OPT_NOTIF       2014\r
-\r
-#define IDH_NC_CREDWND             3000\r
-#define IDH_NC_OK                  3001\r
-#define IDH_NC_CANCEL              3002\r
-#define IDH_NC_HELP                3003\r
-#define IDH_NC_TABBUTTON           3004\r
-#define IDH_NC_ADVANCED            3005\r
-#define IDH_NC_TABMAIN             3006\r
-#define IDH_NC_SETDEF              3007\r
+#define IDH_WELCOME                1000
+#define IDH_WIN_MAIN               1001
+
+#define IDH_MENU_FILE              1501
+#define IDH_MENU_CRED              1502
+#define IDH_MENU_VIEW              1503
+#define IDH_MENU_OPTIONS           1504
+#define IDH_MENU_HELP              1505
+
+#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
+
+#define IDH_NC_CREDWND             3000
+#define IDH_NC_OK                  3001
+#define IDH_NC_CANCEL              3002
+#define IDH_NC_HELP                3003
+#define IDH_NC_TABBUTTON           3004
+#define IDH_NC_ADVANCED            3005
+#define IDH_NC_TABMAIN             3006
+#define IDH_NC_SETDEF              3007
index 6b81f4ca07befeb8a4555b5daa7c82bb56f46b7f..0d32356f690f32622c2cd8e2fc11d50d4933daed 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KHDEFS_H__\r
-#define __KHIMAIRA_KHDEFS_H__\r
-\r
-/*! \defgroup khdef Core definitions\r
-\r
-    Key type definitions used throughout NetIDMgr.\r
- */\r
-/*@{*/\r
-#include<stddef.h>\r
-#include<limits.h>\r
-#include<wchar.h>\r
-\r
-/*!\typedef khm_octet\r
-   \brief A byte (8 bit unsigned)*/\r
-\r
-/*!\typedef khm_int16\r
-   \brief A signed 16 bit quantity */\r
-\r
-/*!\typedef khm_ui_2\r
-   \brief An unsigned 16 bit quantity */\r
-\r
-/*!\typedef khm_int32\r
-   \brief A signed 32 bit quantity */\r
-\r
-/*!\typedef khm_ui_4\r
-   \brief An unsigned 32 bit quantity */\r
-\r
-/*!\typedef khm_int64\r
-   \brief A signed 64 bit quantity */\r
-\r
-/*!\typedef khm_ui_8\r
-   \brief An unsigned 64 bit quantity */\r
-\r
-typedef unsigned __int8  khm_octet;\r
-\r
-typedef __int16          khm_int16;\r
-typedef unsigned __int16 khm_ui_2;\r
-\r
-typedef __int32          khm_int32;\r
-typedef unsigned __int32 khm_ui_4;\r
-\r
-typedef __int64          khm_int64;\r
-typedef unsigned __int64 khm_ui_8;\r
-\r
-#define VALID_INT_BITS    INT_MAX\r
-#define VALID_UINT_BITS   UINT_MAX\r
-\r
-#define KHM_UINT32_MAX 4294967295\r
-\r
-#define KHM_INT32_MAX  2147483647\r
-/* this strange form is necessary since - is a unary operator, not a sign\r
-   indicator */\r
-#define KHM_INT32_MIN  (-KHM_INT32_MAX-1)\r
-\r
-#define KHM_UINT16_MAX 65535\r
-\r
-#define KHM_INT16_MAX 32767\r
-/* this strange form is necessary since - is a unary operator, not a sign\r
-   indicator */\r
-#define KHM_INT16_MIN  (-KHM_INT16_MAX-1)\r
-\r
-/*! \brief Generic handle type.\r
-\r
-    Handles in NetIDMgr are generic pointers.\r
-*/\r
-typedef void * khm_handle;\r
-\r
-/*! \brief The invalid handle\r
-\r
-    Just used to indicate that this handle does not point to anything useful.\r
-    Usually returned by a function that returns a handle as a signal that the\r
-    operation failed.\r
-*/\r
-#define KHM_INVALID_HANDLE ((khm_handle) NULL)\r
-\r
-/*! \brief Boolean.\r
-*/\r
-typedef khm_int32 khm_boolean;\r
-\r
-/*! \brief A size\r
- */\r
-typedef size_t khm_size;\r
-\r
-/*! \typedef ssize_t\r
-    \brief Signed size specifier\r
-\r
-    Just a signed version of size_t\r
- */\r
-\r
-#ifndef _SSIZE_T_DEFINED\r
-#ifdef  _WIN64\r
-typedef __int64    ssize_t;\r
-#else\r
-typedef _W64 int   ssize_t;\r
-#endif\r
-#define _SSIZE_T_DEFINED\r
-#endif \r
-\r
-typedef ssize_t khm_ssize;\r
-\r
-#if defined(_WIN64)\r
-typedef unsigned __int64 khm_wparm;\r
-/*TODO: is this enough? */\r
-typedef unsigned __int64 khm_lparm;\r
-#elif defined(_WIN32)\r
-typedef unsigned __int32 khm_wparm;\r
-typedef unsigned __int64 khm_lparm;\r
-#else\r
-#error khm_wparm and khm_lparm need to be defined for this platform\r
-#endif\r
-\r
-/*!\def KHMAPI \r
-   \brief Calling convention for NetIDMgr exported functions\r
-\r
-   The caling convention for all NetIDMgr exported functions is \b\r
-   __stdcall , unless otherwise noted.\r
- */\r
-\r
-/*!\def KHMEXP\r
-   \brief Export prefix for NetIDMgr exported functions\r
-\r
-   When compiling source that exports functions, those exported\r
-   function declarations will be done as follows:\r
-\r
-   \code\r
-   __declspec(dllexport) khm_int32 __stdcall function_name(arguments...);\r
-   \endcode\r
-\r
-   This eliminates the need for a separate exports definition file.\r
-   However, it doesn't preserve ordinals, but we aren't guaranteeing\r
-   that anyway.\r
-\r
-   On the other hand, if a particular function is going to be imported\r
-   from a DLL, it should declared as follows:\r
-\r
-   \code\r
-   __declspec(dllimport) khm_int32 __stdcall function_name(arguments...);\r
-   \endcode\r
-\r
-   This allows the compiler to properly instrument the import. If the\r
-   function is not declared this way, there will be a stub function\r
-   generated that will just jump to the proper import, generating\r
-   redundant instructions and wasting execution time.\r
-\r
-   This macro encapsulates the proper declaration specifier.\r
- */\r
-\r
-#ifdef _WIN32\r
-#define KHMAPI __stdcall\r
-\r
-#define KHMEXP_EXP __declspec(dllexport)\r
-#define KHMEXP_IMP __declspec(dllimport)\r
-\r
-#define KHMEXP KHMEXP_EXP\r
-#endif\r
-\r
-/* Generic permission values */\r
-/*! \brief Generic read permission or request */\r
-#define KHM_PERM_READ       0x100\r
-\r
-/*! \brief Generic write permission or request */\r
-#define KHM_PERM_WRITE      0x200\r
-\r
-/* Generic flags */\r
-/*! \brief Generic create request\r
-\r
-    For most lookup functions, specifying this flag indicates that if\r
-    the requested object is not found it should be created.\r
-*/\r
-#define KHM_FLAG_CREATE     0x1000\r
-\r
-/*! \brief Wrap to DWORD boundary\r
-\r
-    Returns the smallest integer greater than or equal to the\r
-    parameter that is a multiple of 4.\r
-    \r
-    \note Only use with positive integers. */\r
-#define UBOUND32(d) ((((d)-1)&~3) + 4)\r
-\r
-/*! \brief Offset a pointer by a number of bytes\r
-\r
-    Given a pointer, returns a void pointer that is a given number of\r
-    bytes offset from the pointer.\r
- */\r
-#define BYTEOFFSET(p,off) ((void *)(((char *) (p)) + (off)))\r
-\r
-/*! \brief Check for powers of 2\r
-\r
-    Return TRUE if the operand is a positive power of 2 or 0*/\r
-#define IS_POW2(d) ((d)>=0 && !((d) & ((d) - 1)))\r
-\r
-/*! \brief Wrap to upper bound based on start and step size\r
-\r
-    Return the smallest element in the series <tt>s, s+t, s+2*t,\r
-    s+3*t, ...</tt> that is greater than or equal to \c v.\r
-*/\r
-#define UBOUNDSS(v,start,step) (((v)<=(start))?(start):(start)+((((v)-((start)+1))/(step))+1)*(step))\r
-\r
-/* \brief Length of an array\r
-*/\r
-#define ARRAYLENGTH(x) (sizeof(x)/sizeof(x[0]))\r
-\r
-/*! \brief Generic version type*/\r
-typedef struct tag_khm_version {\r
-    khm_ui_2 major;     /*!< Major version number */\r
-    khm_ui_2 minor;     /*!< Minor version number */\r
-    khm_ui_2 patch;     /*!< Patch level */\r
-    khm_ui_2 aux;       /*!< Auxilary level (usually carries a build number) */\r
-} khm_version;\r
-\r
-/*@}*/\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
+ */
+
+#ifndef _SSIZE_T_DEFINED
+#ifdef  _WIN64
+typedef __int64    ssize_t;
+#else
+typedef _W64 int   ssize_t;
+#endif
+#define _SSIZE_T_DEFINED
+#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
index ae381ff56c56ffc2fa05be81629795a9322543ad..a8ee64505ff0a66f18aab03f05c93c5963876862 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/* Exported */\r
-#ifndef __KHIMAIRA_KHERROR_H\r
-#define __KHIMAIRA_KHERROR_H\r
-\r
-/*! \defgroup kherror NetIDMgr errors\r
-\r
-@{*/\r
-/*! \brief Base for error codes\r
-\r
-    NetIDMgr errors range from \a KHM_ERROR_BASE to KHM_ERROR_BASE +\r
-    KHM_ERROR_RANGE, with the exception of KHM_ERROR_SUCCESS and\r
-    KHM_ERROR_NONE.\r
-    */\r
-#define KHM_ERROR_BASE 0x40000000L\r
-\r
-/*! \brief Range for error codes\r
-\r
-    NetIDMgr errors range from \a KHM_ERROR_BASE to \r
-    KHM_ERROR_BASE + KHM_ERROR_RANGE.\r
-*/\r
-#define KHM_ERROR_RANGE 256L\r
-\r
-/*! \defgroup kherror_codes Error codes\r
-  @{*/\r
-\r
-/*! \brief No error */\r
-#define KHM_ERROR_NONE 0x00000000L\r
-\r
-/*! \brief Success. Same as \a KHM_ERROR_NONE */\r
-#define KHM_ERROR_SUCCESS KHM_ERROR_NONE\r
-\r
-/*! \brief The supplied name was invalid */\r
-#define KHM_ERROR_INVALID_NAME      (KHM_ERROR_BASE + 1)\r
-\r
-/*! \brief Too much data\r
-\r
-    A supplied buffer was invalid, was of insufficient size, or a\r
-    buffer was of a larger size than expected\r
- */\r
-#define KHM_ERROR_TOO_LONG          (KHM_ERROR_BASE + 2)\r
-\r
-/*! \brief One or more parameters supplied to a function were invalid */\r
-#define KHM_ERROR_INVALID_PARAM      (KHM_ERROR_BASE + 3)\r
-\r
-/*! \brief A duplicate.\r
-\r
-    Usually means that something that should have been unique was\r
-    found to be not.\r
- */\r
-#define KHM_ERROR_DUPLICATE         (KHM_ERROR_BASE + 4)\r
-\r
-/*! \brief An object was not found\r
-\r
-    An object referenced in a parameter was not found.\r
- */\r
-#define KHM_ERROR_NOT_FOUND         (KHM_ERROR_BASE + 5)\r
-\r
-/*! \brief The relevant subsystem is not ready\r
-\r
-    Indicates that initialization has not been completed for a\r
-    subsystem.\r
- */\r
-#define KHM_ERROR_NOT_READY         (KHM_ERROR_BASE + 6)\r
-\r
-/*! \brief No more resources\r
-\r
-    A limited resource has been exhausted.\r
- */\r
-#define KHM_ERROR_NO_RESOURCES      (KHM_ERROR_BASE + 7)\r
-\r
-/*! \brief Type mismatch\r
- */\r
-#define KHM_ERROR_TYPE_MISMATCH     (KHM_ERROR_BASE + 8)\r
-\r
-/*! \brief Already exists\r
-\r
-    Usually indicates that an exclusive create operation failed due to\r
-    the existence of a similar object.  Subtly different from\r
-    ::KHM_ERROR_DUPLICATE\r
- */\r
-#define KHM_ERROR_EXISTS            (KHM_ERROR_BASE + 9)\r
-\r
-/*! \brief Operation timed out\r
- */\r
-#define KHM_ERROR_TIMEOUT           (KHM_ERROR_BASE + 10)\r
-\r
-/*! \brief An EXIT message was received\r
- */\r
-#define KHM_ERROR_EXIT              (KHM_ERROR_BASE + 11)\r
-\r
-/*! \brief Unknown or unspecified error\r
- */\r
-#define KHM_ERROR_UNKNOWN           (KHM_ERROR_BASE + 12)\r
-\r
-/*! \brief General error\r
- */\r
-#define KHM_ERROR_GENERAL           KHM_ERROR_UNKNOWN\r
-\r
-/*! \brief An index was out of bounds\r
- */\r
-#define KHM_ERROR_OUT_OF_BOUNDS     (KHM_ERROR_BASE + 13)\r
-\r
-/*! \brief Object already deleted\r
-\r
-    One or more objects that were referenced were found to have been\r
-    already deleted.\r
- */\r
-#define KHM_ERROR_DELETED           (KHM_ERROR_BASE + 14)\r
-\r
-/*! \brief Invalid operation\r
-\r
-    The operation was not permitted to continue for some reason.\r
-    Usually because the necessary conditions for the operation haven't\r
-    been met yet or the operation can only be performed at certain\r
-    times during the execution of NetIDMgr.\r
- */\r
-#define KHM_ERROR_INVALID_OPERATION (KHM_ERROR_BASE + 15)\r
-\r
-/*! \brief Signature check failed\r
- */\r
-#define KHM_ERROR_INVALID_SIGNATURE (KHM_ERROR_BASE + 16)\r
-\r
-/*! \brief Not implemented yet\r
-\r
-    The operation that was attempted involved invoking functionality\r
-    that has not been implemented yet.\r
- */\r
-#define KHM_ERROR_NOT_IMPLEMENTED   (KHM_ERROR_BASE + 17)\r
-\r
-/*! \brief The objects were equivalent\r
- */\r
-#define KHM_ERROR_EQUIVALENT        (KHM_ERROR_BASE + 18)\r
-\r
-/*! \brief No provider exists to service the request\r
-*/\r
-#define KHM_ERROR_NO_PROVIDER       (KHM_ERROR_BASE + 19)\r
-\r
-/*! \brief The operation succeeded, but with errors\r
-*/\r
-#define KHM_ERROR_PARTIAL           (KHM_ERROR_BASE + 20)\r
-\r
-/*! \brief An incompatibility was found */\r
-#define KHM_ERROR_INCOMPATIBLE      (KHM_ERROR_BASE + 21)\r
-\r
-/*! \brief The operation was put on hold\r
-\r
-    A request was put on hold or postponed. */\r
-#define KHM_ERROR_HELD              (KHM_ERROR_BASE + 22)\r
-\r
-/*@}*/ /*kherror_codes*/\r
-\r
-/*! \brief Tests whether a return value indicates success */\r
-#define KHM_SUCCEEDED(rv) ((rv)==KHM_ERROR_NONE)\r
-\r
-/*! \brief Tests whether a return value indicates failure */\r
-#define KHM_FAILED(rv) ((rv)!=KHM_ERROR_NONE)\r
-\r
-/*@}*/\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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_PARAM      (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)
+
+/*! \brief An incompatibility was found */
+#define KHM_ERROR_INCOMPATIBLE      (KHM_ERROR_BASE + 21)
+
+/*! \brief The operation was put on hold
+
+    A request was put on hold or postponed. */
+#define KHM_ERROR_HELD              (KHM_ERROR_BASE + 22)
+
+/*@}*/ /*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
index 8bf43695b93ed98d84692d8e2e972d11e426af21..ab60ca37d4534527e493b2ff0a79eba8df050e59 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/* Not exported */\r
-#ifndef _KHIMAIRA_KHLIST_H\r
-#define _KHIMAIRA_KHLIST_H\r
-\r
-/* Note that most of these are "unsafe" macros.  Not for general use */\r
-\r
-/* LIFO lists */\r
-#define LDCL(type)                              \\r
-    type * next;                                \\r
-    type * prev\r
-\r
-#define LINIT(pe)                               \\r
-    do {                                        \\r
-    (pe)->next = NULL;                          \\r
-    (pe)->prev = NULL; } while(0)\r
-\r
-#define LPUSH(pph,pe)                           \\r
-    do {                                        \\r
-    (pe)->next = *pph;                          \\r
-    (pe)->prev = NULL;                          \\r
-    if(*(pph)) (*(pph))->prev = (pe);           \\r
-    (*(pph)) = (pe); } while(0)\r
-\r
-#define LPOP(pph,ppe)                           \\r
-    do {                                        \\r
-    *(ppe) = *(pph);                            \\r
-    if(*(pph)) *(pph) = (*(pph))->next;         \\r
-    if(*(pph)) (*(pph))->prev = NULL;           \\r
-    if(*(ppe)) (*(ppe))->next = NULL;           \\r
-    } while(0)\r
-\r
-#define LDELETE(pph,pe)                                 \\r
-    do {                                                \\r
-    if((pe)->prev) (pe)->prev->next = (pe)->next;       \\r
-    if((pe)->next) (pe)->next->prev = (pe)->prev;       \\r
-    if(*(pph) == (pe)) *(pph) = (pe)->next;             \\r
-    (pe)->next = (pe)->prev = NULL;                     \\r
-    } while(0)\r
-\r
-#define LEMPTY(pph) (*(pph) == NULL)\r
-\r
-#define LNEXT(pe) ((pe)?(pe)->next:NULL)\r
-\r
-#define LPREV(pe) ((pe)?(pe)->prev:NULL)\r
-\r
-/* Trees with LIFO child lists */\r
-#define TDCL(type)                              \\r
-    LDCL(type);                                 \\r
-    type * children;                            \\r
-    type * parent\r
-\r
-#define TINIT(pe)                               \\r
-    do {                                        \\r
-    (pe)->children = NULL;                      \\r
-    (pe)->parent = NULL; } while(0)\r
-\r
-#define TADDCHILD(pt,pe)                        \\r
-    do {                                        \\r
-    LPUSH(&((pt)->children),(pe));              \\r
-    (pe)->parent = (pt); } while(0)\r
-\r
-#define TFIRSTCHILD(pt) ((pt)?(pt)->children:NULL)\r
-\r
-#define TPOPCHILD(pt, ppe)                      \\r
-    do {                                        \\r
-    LPOP(&((pt)->children), ppe);               \\r
-    if(*(ppe)) (*(ppe))->parent = NULL;         \\r
-    } while(0)\r
-\r
-#define TDELCHILD(pt, pe)                       \\r
-    do {                                        \\r
-    LDELETE(&((pt)->children), (pe));           \\r
-    (pe)->parent = NULL; } while(0)\r
-\r
-#define TPARENT(pe) ((pe)?(pe)->parent:NULL)\r
-\r
-/* FIFO lists */\r
-#define QDCL(type)                              \\r
-    type * head;                                \\r
-    type * tail\r
-\r
-#define QINIT(pq)                               \\r
-    do {                                        \\r
-    (pq)->head = (pq)->tail = NULL;             \\r
-    } while(0)\r
-\r
-#define QPUT(pq, pe)                            \\r
-    do {                                        \\r
-    LPUSH(&(pq)->tail, (pe));                   \\r
-    if(!(pq)->head) (pq)->head = (pe);          \\r
-    } while(0)\r
-\r
-#define QPUSH(pq, pe)                           \\r
-    do {                                        \\r
-    (pe)->next = NULL;                          \\r
-    (pe)->prev = (pq)->head;                    \\r
-    if((pq)->head) (pq)->head->next = (pe);     \\r
-    if(!(pq)->tail) (pq)->tail = (pe);          \\r
-    (pq)->head = (pe);                          \\r
-    } while (0)\r
-\r
-#define QGET(pq, ppe)                                           \\r
-    do {                                                        \\r
-    *(ppe) = (pq)->head;                                        \\r
-    if(*(ppe)) {                                                \\r
-        (pq)->head = (*(ppe))->prev;                            \\r
-        if( (*(ppe))->prev ) (*(ppe))->prev->next = NULL;       \\r
-        (*(ppe))->prev = NULL;                                  \\r
-        if( (pq)->tail == *(ppe)) (pq)->tail = NULL;            \\r
-    }                                                           \\r
-    } while(0)\r
-\r
-#define QDEL(pq, pe)                                    \\r
-    do {                                                \\r
-        if((pq)->head == (pe)) (pq)->head = LPREV(pe);  \\r
-        LDELETE(&((pq)->tail), (pe));                   \\r
-    } while(0)\r
-\r
-\r
-#define QGETT(pq,ppe)                                           \\r
-    do {                                                        \\r
-    *(ppe) = (pq)->tail;                                        \\r
-    if(*(ppe)) {                                                \\r
-        (pq)->tail = (*(ppe))->next;                            \\r
-        if( (*(ppe))->next ) (*(ppe))->next->prev = NULL;       \\r
-        (*(ppe))->next = NULL;                                  \\r
-        if( (pq)->head == *(ppe)) (pq)->head = NULL;            \\r
-    }                                                           \\r
-    } while(0)\r
-\r
-#define QTOP(pq) ((pq)->head)\r
-#define QBOTTOM(pq) ((pq)->tail)\r
-#define QNEXT(pe) ((pe)->prev)\r
-#define QPREV(pe) ((pe)->next)\r
-\r
-#define QINSERT(pt, pre, pe)                    \\r
-    do {                                        \\r
-    if ((pre) == NULL ||                        \\r
-        QNEXT(pre) == NULL) { QPUT(pt, pe); }   \\r
-    else {                                      \\r
-        (pe)->prev = (pre)->prev;               \\r
-        (pe)->next = (pre);                     \\r
-        (pre)->prev->next = (pe);               \\r
-        (pre)->prev = (pe);                     \\r
-    }} while(0)\r
-\r
-/* Trees with FIFO child lists */\r
-#define TQDCL(type)                             \\r
-    LDCL(type);                                 \\r
-    QDCL(type);                                 \\r
-    type * parent\r
-\r
-#define TQINIT(pe)                              \\r
-    do {                                        \\r
-    LINIT(pe);                                  \\r
-    QINIT(pe);                                  \\r
-    (pe)->parent = NULL; } while(0)\r
-\r
-#define TQPUTCHILD(pt,pe)                       \\r
-    do {                                        \\r
-    QPUT((pt), (pe));                           \\r
-    (pe)->parent = (pt); } while(0)\r
-\r
-#define TQINSERT(pt, pre, pe)                   \\r
-    do {                                        \\r
-    QINSERT(pt, pre, pe);                       \\r
-    (pe)->parent = (pt); } while(0)\r
-\r
-#define TQGETCHILD(pt,ppe)                      \\r
-    do {                                        \\r
-    QGET(pt, ppe);                              \\r
-    if (*(ppe)) { *(ppe)->parent = NULL; }      \\r
-    } while(0)\r
-\r
-#define TQDELCHILD(pt, pe)                      \\r
-    do {                                        \\r
-    QDEL(pt, pe);                               \\r
-    (pe)->parent = NULL; } while(0)\r
-\r
-#define TQFIRSTCHILD(pt) ((pt)?QTOP(pt):NULL)\r
-\r
-#define TQNEXTCHILD(pe) QNEXT(pe)\r
-\r
-#define TQPREVCHILD(pe) QPREV(pe)\r
-\r
-#define TQPARENT(pe) ((pe)?(pe)->parent:NULL)\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 QPUSH(pq, pe)                           \
+    do {                                        \
+    (pe)->next = NULL;                          \
+    (pe)->prev = (pq)->head;                    \
+    if((pq)->head) (pq)->head->next = (pe);     \
+    if(!(pq)->tail) (pq)->tail = (pe);          \
+    (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)
+
+#define QINSERT(pt, pre, pe)                    \
+    do {                                        \
+    if ((pre) == NULL ||                        \
+        QNEXT(pre) == NULL) { QPUT(pt, pe); }   \
+    else {                                      \
+        (pe)->prev = (pre)->prev;               \
+        (pe)->next = (pre);                     \
+        (pre)->prev->next = (pe);               \
+        (pre)->prev = (pe);                     \
+    }} while(0)
+
+/* Trees with FIFO child lists */
+#define TQDCL(type)                             \
+    LDCL(type);                                 \
+    QDCL(type);                                 \
+    type * parent
+
+#define TQINIT(pe)                              \
+    do {                                        \
+    LINIT(pe);                                  \
+    QINIT(pe);                                  \
+    (pe)->parent = NULL; } while(0)
+
+#define TQPUTCHILD(pt,pe)                       \
+    do {                                        \
+    QPUT((pt), (pe));                           \
+    (pe)->parent = (pt); } while(0)
+
+#define TQINSERT(pt, pre, pe)                   \
+    do {                                        \
+    QINSERT(pt, pre, pe);                       \
+    (pe)->parent = (pt); } while(0)
+
+#define TQGETCHILD(pt,ppe)                      \
+    do {                                        \
+    QGET(pt, ppe);                              \
+    if (*(ppe)) { *(ppe)->parent = NULL; }      \
+    } while(0)
+
+#define TQDELCHILD(pt, pe)                      \
+    do {                                        \
+    QDEL(pt, pe);                               \
+    (pe)->parent = NULL; } while(0)
+
+#define TQFIRSTCHILD(pt) ((pt)?QTOP(pt):NULL)
+
+#define TQNEXTCHILD(pe) QNEXT(pe)
+
+#define TQPREVCHILD(pe) QPREV(pe)
+
+#define TQPARENT(pe) ((pe)?(pe)->parent:NULL)
+
+#endif
index 77397f90b52b48695e1925b849e579b75820e6ca..cfb43f94e3f460e01acb97d87a6c8aabb9869342 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KHMSGTYPES_H\r
-#define __KHIMAIRA_KHMSGTYPES_H\r
-\r
-/*! \addtogroup kmq\r
-@{*/\r
-/*! \defgroup kmq_msg Message Types\r
-@{*/\r
-\r
-/*! \name Global message types\r
-@{*/\r
-\r
-/*! \brief System messages.\r
-\r
-    All subscribers are subscribed to the system message class by default.\r
-\r
-    \see \ref kmq_msg_system\r
-*/\r
-#define KMSG_SYSTEM     0\r
-\r
-/*! \brief Ad-hoc messages.\r
-\r
-    These are messages that are sent through add hoc publishers and\r
-    subscribers.\r
-*/\r
-#define KMSG_ADHOC      1\r
-\r
-/*! \brief NetIDMgr Credentials Database messages\r
-\r
-    These messages notify subscribers of events related to the\r
-    credentials database, such as the registration, unregistration and\r
-    modification of identities, attributes, attribute types and\r
-    credential types.  It also provides notifications of changes to\r
-    the root crednetial set.\r
-\r
-    \see \ref kmq_msg_kcdb\r
-*/\r
-#define KMSG_KCDB       2\r
-\r
-/*! \brief NetIDMgr Module Manager messages\r
\r
-    \see \ref kmq_msg_kmm\r
-*/\r
-#define KMSG_KMM        3\r
-\r
-/*! \brief NetIDMgr Credential messages\r
-\r
-    Notifications of crednetial events.  These are the most important\r
-    events that a credentials provider should respond to.  The\r
-    notifications provide co-oridination between credential providers\r
-    for performing basic credentials management tasks such as\r
-    obtaining new credentials for an identity, deleting credentials\r
-    for an identity, obtaining or deleting credentials of a particular\r
-    type for an identity etc.\r
-\r
-    \see \ref cred_msgs\r
-    \see \ref kmq_msg_cred\r
- */\r
-#define KMSG_CRED       4\r
-\r
-/*! \brief Action list messages\r
-\r
-    Notifications of changes in action state and firing of custom\r
-    actions.\r
-\r
-    \see \ref kmq_msg_act\r
- */\r
-#define KMSG_ACT        5\r
-\r
-/*! \brief Alert messages\r
-\r
-    Notifier is the component which displays alerts and error messages\r
-    when the NetIDMgr window is normally in operation and which\r
-    displays balloon prompts when the window is minimized to alert the\r
-    user to important messages such as credentials expiring etc.\r
-\r
-    \note This is an internal message class.  Components that are not\r
-        the notifier should not be subscribing to alert messages.\r
-\r
-    \see \ref kmq_msg_alert\r
- */\r
-#define KMSG_ALERT      6\r
-\r
-/*! \brief Identity messages\r
-\r
-    These are messages that are sent to the identity provider.  These\r
-    are generally dispatched through a specific subscription object\r
-    and are not broadcast.\r
-\r
-    \see \ref kmq_msg_ident\r
- */\r
-#define KMSG_IDENT      7\r
-\r
-/*! \brief Base message type ID for customized message types\r
- */\r
-#define KMSGBASE_USER   16\r
-\r
-/*@}*/\r
-\r
-/*! \defgroup kmq_msg_system KMSG_SYSTEM subtypes \r
-@{*/\r
-/*! \brief Generic initialization message\r
-\r
-    This message is used by specific components to signal that the\r
-    recipient is to perform initialization tasks.  As a convention,\r
-    the recipient should return KHM_ERROR_SUCCESS if it successfully\r
-    performed the initlization tasks or some other value if it failed\r
-    to do so.  Failure to successfully initialize is usually taken to\r
-    mean that the recipient component is not able to perform its\r
-    function.\r
-\r
-    Usually this is the first message to be received by the recipient.\r
-\r
-    \see \ref pi_pt_cred_init\r
- */\r
-#define KMSG_SYSTEM_INIT    1\r
-\r
-/*! \brief Generic uninitialization message\r
-\r
-    Used by specific components to signal that the recipient should\r
-    perform uninitilization tasks in preparation of termination.  The\r
-    return value of this message is not used.\r
-\r
-    Usually this is the last message to be received by the recipient.\r
-\r
-    \see \ref pi_pt_cred_exit\r
- */\r
-#define KMSG_SYSTEM_EXIT    2\r
-\r
-/*! \brief Message completion\r
-\r
-    This is an internal message\r
- */\r
-#define KMSG_SYSTEM_COMPLETION 3\r
-/*@}*/\r
-\r
-/*! \defgroup kmq_msg_kcdb KMSG_KCDB subtypes \r
-@{*/\r
-#define KMSG_KCDB_IDENT     1\r
-#define KMSG_KCDB_CREDTYPE  2\r
-#define KMSG_KCDB_ATTRIB    3\r
-#define KMSG_KCDB_TYPE      4\r
-\r
-/*! \brief Generic credentials request\r
-\r
-    \see ::kcdb_cred_request for more information\r
- */\r
-#define KMSG_KCDB_REQUEST   256\r
-/*@}*/\r
-\r
-/*! \defgroup kmq_msg_kmm KMSG_KMM subtypes\r
-@{*/\r
-#define KMSG_KMM_I_REG      1\r
-\r
-#define KMSG_KMM_I_DONE     2\r
-/*@}*/\r
-\r
-/*! \defgroup kmq_msg_act KMSG_ACT subtypes\r
-  @{*/\r
-\r
-/*! \brief One or more actions changed state\r
-\r
-    This message is sent in response to a call to\r
-    khui_enable_actions() or khui_enable_action() and indicates that\r
-    one or more actions have changed their state.\r
- */\r
-#define KMSG_ACT_ENABLE     1\r
-\r
-/*! \brief One or more actions changed check state\r
-\r
-    Sent in response to khui_check_radio_action() or\r
-    khui_check_action() and indicates that one or more actions have\r
-    either been checked or unchecked.\r
- */\r
-#define KMSG_ACT_CHECK      2\r
-\r
-/*! \brief Refresh action states\r
-\r
-    Sent after a batch of modifications were made to action states.\r
- */\r
-#define KMSG_ACT_REFRESH    3\r
-\r
-/*! \brief A new action was created\r
-\r
-    Sent when a new custom action was created.  The \a uparam\r
-    parameter of the message contains the identifier of the newly\r
-    created action.\r
-*/\r
-#define KMSG_ACT_NEW        4\r
-\r
-/*! \brief A custom action was deleted\r
-\r
-    Sent after a custom action is deleted.  The \a uparam parameter of\r
-    the message contains the identifier of the deleted action.\r
- */\r
-#define KMSG_ACT_DELETE     5\r
-\r
-/*! \brief A custom action has been activated\r
-\r
-    When a custom action is activated, then the listener of that\r
-    custom action receives this message.  Note that only the listener\r
-    for that custom action will receive this notification.\r
-\r
-    \a uparam of the message is set to the identifier of the custom\r
-    action.\r
- */\r
-#define KMSG_ACT_ACTIVATE   6\r
-\r
-/*! \brief Internal */\r
-#define KMSG_ACT_BEGIN_CMDLINE       128\r
-\r
-/*! \brief Internal */\r
-#define KMSG_ACT_CONTINUE_CMDLINE    129\r
-\r
-/*! \brief Internal */\r
-#define KMSG_ACT_SYNC_CFG            130\r
-\r
-/*! \brief Internal */\r
-#define KMSG_ACT_END_CMDLINE         131\r
-\r
-/*@}*/\r
-\r
-/*! \defgroup kmq_msg_cred KMSG_CRED subtypes\r
-  @{*/\r
-/*! \brief Root credential set changed\r
-    \r
-    This message is issued when the root credential set successfully\r
-    collected credentials from another credential set.\r
-\r
-    \a uparam of the message is set to a bitmask indicating the change\r
-    that occured.  It is a combination of ::KCDB_DELTA_ADD,\r
-    ::KCDB_DELTA_DEL and ::KCDB_DELTA_MODIFY.\r
- */\r
-#define KMSG_CRED_ROOTDELTA 1\r
-\r
-/*! \brief Re-enumerate credentials\r
-\r
-    A notice to all credential providers to re-enumerate their\r
-    respective credentials.\r
-\r
-    \note May be sent to individual credential subscriptions.\r
- */\r
-#define KMSG_CRED_REFRESH   2\r
-\r
-/*! \brief Change the password\r
-\r
-    This message notifies credentials providers that a password change\r
-    request has been received.\r
-\r
-    A plug-in handling this message that wishes to participate in the\r
-    password change operation is expected to add a\r
-    ::khui_new_creds_by_type to the list of participants in the\r
-    ::khui_new_creds structure by calling khui_cw_add_type().\r
-\r
-    The password change operation requires user interaction.  Any\r
-    plug-ins that are participating in the operation need to provide a\r
-    user-interface.\r
-\r
-    Message parameters:\r
-    - \b vparam : pointer to a ::khui_new_creds structure\r
-\r
-    \see khui_cw_add_type(), ::khui_new_creds, ::khui_new_creds_by_type\r
- */\r
-#define KMSG_CRED_PASSWORD  16\r
-\r
-/*! \brief Initiate the process of obtaining new credentials\r
-\r
-    The UI sends this message to start the process of obtaining new\r
-    credentials.  See \ref cred_acq for more information about\r
-    handling this message.\r
-\r
-    A plug-in handling this message that wishes to participate in the\r
-    new credentials acquisition operation is expected to add a\r
-    ::khui_new_creds_by_type to hte list of participants in the\r
-    ::khui_new_creds structure by calling khui_cw_add_type().\r
-\r
-    Message parameters:\r
-    - \b vparam : pointer to a ::khui_new_creds structure\r
-\r
-    \see \ref cred_acq, khui_cw_add_type(), ::khui_new_creds,\r
-    ::khui_new_creds_by_type\r
- */\r
-#define KMSG_CRED_NEW_CREDS 17\r
-\r
-/*! \brief Renew credentials\r
-\r
-    This is a notification sent to individual credentials providers\r
-    that a specified identity's credentials should be renewed.\r
-\r
-    A plug-in handling this message that wishes to participate in the\r
-    renew credentials operation is expected to add a\r
-    ::khui_new_creds_by_type to the list of participants in the\r
-    ::khui_new_creds structure by calling khui_cw_add_type().\r
-\r
-    Message parameters:\r
-    - \b vparam : Pointer to a khui_new_creds object\r
-\r
-    \see khui_cw_add_type(), ::khui_new_creds,\r
-    ::khui_new_creds_by_type\r
- */\r
-#define KMSG_CRED_RENEW_CREDS       18\r
-\r
-/*! \brief Dialog setup\r
-\r
-    Once ::KMSG_CRED_NEW_CREDS has been responded to by all the\r
-    credential types, the UI creates the dialog windows using the data\r
-    supplied in the ::khui_new_creds_by_type structures and issues\r
-    this message.  Each credentials provider is expected to respond by\r
-    finalizing dialog creation operations.\r
-\r
-    Message parameters:\r
-    - \b vparam : pointer to a ::khui_new_creds structure\r
-\r
-    \note May be sent to individual credential subscriptions.\r
- */\r
-#define KMSG_CRED_DIALOG_SETUP      19\r
-\r
-/*! \brief Dialog pre-start\r
-\r
-    Sent after all the credentials providers have responded to\r
-    ::KMSG_CRED_DIALOG_SETUP and all the initialization has been\r
-    completed.  Credentials providers are expected to respond to this\r
-    message by loading any default data into the dialog controls for\r
-    each credential type.\r
-\r
-    Message parameters:\r
-    - \b vparam : pointer to a ::khui_new_creds structure\r
-\r
-    \note May be sent to individual credential subscriptions.\r
- */\r
-#define KMSG_CRED_DIALOG_PRESTART   20\r
-\r
-/*! \brief Dialog start\r
-\r
-    A notification that the dialog is now in progress.\r
-\r
-    Message parameters:\r
-    - \b vparam : pointer to a ::khui_new_creds structure\r
-\r
-    \note May be sent to individual credential subscriptions.\r
- */\r
-#define KMSG_CRED_DIALOG_START      21\r
-\r
-/*! \brief The primary identity of the new credentials dialog has changed\r
-\r
-    This message is not sent out by the UI, but is reserved here for\r
-    use by individual credentials providers.  The message may be sent\r
-    from the dialog procedure to the plugin.\r
-\r
-    Message parameters:\r
-    - \b vparam : pointer to a ::khui_new_creds structure\r
-\r
-    \note Be careful when sending this message.  All messages that are\r
-        not sent by the system should not be sent via broadcast.\r
-        Instead, create a subscription using kmq_create_subscription()\r
-        for the individual plugin that you want to send the message\r
-        and use one of the per-subscription message functions to send\r
-        the actual message.\r
- */\r
-#define KMSG_CRED_DIALOG_NEW_IDENTITY 22\r
-\r
-/*! \brief New credentials options have changed.\r
-\r
-    This message is not sent out by the UI, but is reserved here for\r
-    use by individual credentials providers.  The message may be sent\r
-    from the dialog procedure to the plugin.\r
-\r
-    Message parameters:\r
-    - \b vparam : pointer to a ::khui_new_creds structure\r
-\r
-    \note Be careful when sending this message.  All messages that are\r
-        not sent by the system should not be sent via broadcast.\r
-        Instead, create a subscription using kmq_create_subscription()\r
-        for the individual plugin that you want to send the message\r
-        and use one of the per-subscription message functions to send\r
-        the actual message.\r
- */\r
-#define KMSG_CRED_DIALOG_NEW_OPTIONS  23\r
-\r
-/*! \brief Process dialog\r
-\r
-    Sent to all the credential providers to look at the contents of\r
-    the given ::khui_new_creds structure and do any required\r
-    processing.\r
-\r
-    If the \a result field in the structure is set to\r
-    ::KHUI_NC_RESULT_PROCESS, then new credentials should be\r
-    obtained using the given data.\r
-\r
-    Set the \a response field in the structure to indicate how the UI\r
-    should proceed from here.\r
-\r
-    Message parameters:\r
-    - \b vparam : pointer to a ::khui_new_creds structure\r
-\r
-    \note May be sent to individual credential subscriptions.\r
- */\r
-#define KMSG_CRED_PROCESS             24\r
-\r
-/*! \brief End a credentials acquisition operation\r
-\r
-    A notification that the credentials acquisition operation has\r
-    ended.\r
-\r
-    Message parameters:\r
-    - \b vparam : pointer to a ::khui_new_creds structure\r
-\r
-    \note May be sent to individual credential subscriptions.\r
- */\r
-#define KMSG_CRED_END                 25\r
-\r
-/*! \brief Import credentials from the operating system\r
-\r
-    Notification to all credentials providers to import any available\r
-    credentials from the operating system.\r
-\r
-    Message parameters:\r
-    - This message does not have any parameters\r
-*/\r
-#define KMSG_CRED_IMPORT              26\r
-\r
-/*! \brief Destroy credentials\r
-\r
-    Notification that the specified credentials should be destroyed.\r
-    Once this message has completed processing a ::KMSG_CRED_REFRESH\r
-    message will be issued.\r
-\r
-    The credentials that should be destroyed are specified by a\r
-    ::khui_action_context structure.  The context that should be used\r
-    is the selection context.  Hence, the credentials that must be\r
-    destroyed are the ones lised in the credential set (\a credset).\r
-\r
-    Message parameters:\r
-\r
-    - \b upram : Unused. Zero.\r
-\r
-    - \b vparam : pointer to a ::khui_action_context structure which\r
-      describes which credentials need to be destroyed.\r
-\r
- */\r
-#define KMSG_CRED_DESTROY_CREDS     32\r
-\r
-#if 0\r
-/*! \brief Parse an identity\r
-\r
-    \note May be sent to individual credential subscriptions.\r
- */\r
-#define KMSG_CRED_IDENT_PARSE       65\r
-#endif\r
-\r
-/*! \brief A property page is being launced\r
-\r
-    Handlers of this message should determine whether or not they\r
-    should participate in the property sheet and if so, add a\r
-    ::khui_property_page structure to the property sheet.\r
-\r
-    Message parameters:\r
-    - \b vparam : pointer to a ::khui_property_sheet structure\r
- */\r
-#define KMSG_CRED_PP_BEGIN          128\r
-\r
-/*! \brief A property page is about to be created\r
-\r
-    Message parameters:\r
-    - \b vparam : pointer to a ::khui_property_sheet structure\r
-\r
-    \note This message is merely a notification that the property\r
-        sheet is being created.  Handlers should not modify the state\r
-        of the property sheet or pages at this time.\r
- */\r
-#define KMSG_CRED_PP_PRECREATE      129\r
-\r
-/*! \brief A property page has finished processing\r
-\r
-    Handlers of this message should remove any ::khui_property_page\r
-    structures they added when processing ::KMSG_CRED_PP_BEGIN.\r
-\r
-    Message parameters:\r
-    - \b vparam : pointer to a ::khui_property_sheet structure\r
- */\r
-#define KMSG_CRED_PP_END            130\r
-\r
-/*! \brief A property page has been destroyed\r
-\r
-    Message parameters:\r
-    - \b vparam : pointer to a ::khui_property_sheet structure\r
-\r
-    \note This is a notification that the property sheet processing\r
-        has been completed and that the property sheet data structures\r
-        should be freed.  Any property page data structures should\r
-        have already been freed while processing KMSG_CRED_PP_END.\r
-        The validity of the ::khui_property_sheet structure should not\r
-        be relied upon while processing this message.\r
- */\r
-#define KMSG_CRED_PP_DESTROY        131\r
-\r
-/*! \brief An IP address change occurred\r
-\r
-    There are no parameters for this message.  The NetIDMgr\r
-    application handles this message and depending on configuration,\r
-    posts message for the individual credentials providers to either\r
-    obtain new credentials or renew old ones.\r
- */\r
-#define KMSG_CRED_ADDR_CHANGE        140\r
-\r
-/*! \brief Check if a KMSG_CRED subtype is a credentials acquisition message\r
-\r
-    Dialog messages are those that deal with the new or initial\r
-    credentials acquisition dialog, from initial announcement to\r
-    dialog completion.\r
-\r
-    Currently, the dialog messages are:\r
-    - ::KMSG_CRED_NEW_CREDS\r
-    - ::KMSG_CRED_RENEW_CREDS\r
-    - ::KMSG_CRED_DIALOG_SETUP\r
-    - ::KMSG_CRED_DIALOG_PRESTART\r
-    - ::KMSG_CRED_DIALOG_START\r
-    - ::KMSG_CRED_DIALOG_NEW_IDENTITY\r
-    - ::KMSG_CRED_DIALOG_NEW_OPTIONS\r
-    - ::KMSG_CRED_PROCESS\r
-    - ::KMSG_CRED_END\r
-\r
-    All dialog message numbers are allocated in a contigous block.\r
-\r
-    Note that while ::KMSG_CRED_PROCESS and ::KMSG_CRED_END are not\r
-    specific to dialogs, they are still included in this predicate\r
-    because they are also part of the dialog message sequence.\r
- */\r
-#define IS_CRED_ACQ_MSG(msg) ((msg) >= 16 && (msg) <=31)\r
-\r
-/*@}*/ /* /KMSG_CRED subtypes */ \r
-\r
-/*! \defgroup kmq_msg_alert KMSG_ALERT Subtypes \r
-  @{*/\r
-\r
-/*! \brief Show an alert\r
-\r
-    Message parameters:\r
-    - \b vparam : held pointer to a ::khui_alert object\r
-\r
-    \note The ::khui_alert object will be released when the processing\r
-        of this message completes.\r
- */\r
-#define KMSG_ALERT_SHOW 1\r
-\r
-/*! \brief Add an alert to the alert queue\r
-\r
-    Message parameters:\r
-    - \b vparam : held pointer to a ::khui_alert object\r
-\r
-    \note the ::khui_alert object will be released when the queued\r
-        messages are displayed.\r
- */\r
-#define KMSG_ALERT_QUEUE 2\r
-\r
-/*! \brief Show the next queued alert\r
-\r
-    There are no message parameters\r
- */\r
-#define KMSG_ALERT_SHOW_QUEUED 3\r
-\r
-/*! \brief Check if there are any queued messages and, if so, update the statusbar\r
-\r
-    There are no message parameters\r
- */\r
-#define KMSG_ALERT_CHECK_QUEUE 4\r
-\r
-/*! \brief Show a modal alert\r
-\r
-    Message parameters:\r
-    - \b vparam : held pointer to a ::khui_alert object.\r
-\r
-    \note the ::khui_alert object will be released when the queued\r
-        messages are displayed.\r
- */\r
-#define KMSG_ALERT_SHOW_MODAL 5\r
-\r
-/*@}*/\r
-\r
-/*! \defgroup kmq_msg_ident KMSG_IDENT Subtypes\r
-  @{*/\r
-\r
-/*! \brief Initialize and start the identity provider\r
-\r
-\r
-    Sent by the KCDB to notify the identity provider that it is now\r
-    the current identity provider.\r
-\r
-    Note that unlike regular plugins, an identity provider can be\r
-    loaded and inert (not provide any services).  Also, the user may\r
-    switch between multiple identity providers on the fly.\r
- */\r
-#define KMSG_IDENT_INIT                 1\r
-\r
-/*! \brief Stop the identity provider\r
-\r
-    Sent by the KCDB as notificaton that the identity provider is no\r
-    longer the current provider.\r
- */\r
-#define KMSG_IDENT_EXIT                 2\r
-\r
-/*! \brief Check if an identity name is valid\r
-\r
-    This message is sent to the identity provider to verify the syntax\r
-    of an identity name.  Note that only the syntax of the name is to\r
-    be verfied and not the actual physical existence of said identity.\r
-\r
-    Message parameters:\r
-\r
-    - \b vparam : pointer to ::kcdb_ident_name_xfer object.  The\r
-        name to be validated will be in the \a name_src member.  The\r
-        buffer will be NULL terminated with a maximum limit of\r
-        KCDB_IDENT_MAXCCH_NAME characters including the terminating\r
-        NULL, consisting only of characters in KCDB_IDENT_VALID_CHARS\r
-        The \a result member should be set to one of the following\r
-        depending on the result of the validation:\r
-\r
-        - KHM_ERROR_SUCCESS : The name was valid\r
-        - KHM_ERROR_INVALID_NAME : The name was invalid\r
- */\r
-#define KMSG_IDENT_VALIDATE_NAME        3\r
-\r
-/*! \brief Check if an identity is valid\r
-\r
-    Sent to the identity provider to verify the validity of the given\r
-    identity.  The provider should verify that the identity exists and\r
-    is in a state where it can be actively used.\r
-\r
-    Depending on the result of the validation, the flags of the\r
-    identity should be updated.\r
-\r
-    Message parameters:\r
-    - \b vparam : Handle to an identity cast as a void pointer.\r
- */\r
-#define KMSG_IDENT_VALIDATE_IDENTITY    4\r
-\r
-/*! \brief Canonicalize identity name\r
-\r
-    The identity provider will be given a name, which it should put in\r
-    canonical form, adjusting case and any character replacement or\r
-    doing any relevant expansions if applicable, and place it in the\r
-    supplied buffer.\r
-\r
-    Message parameters:\r
-\r
-    - \b vparam : Pointer to a ::kcdb_ident_name_xfer structure\r
-          which provides the identity name to canonicalize in the \a\r
-          name_src member, and the buffer to store the canonical name\r
-          in the \a name_dest member.  The \a name_dest buffer is\r
-          guaranteed to be at least KCDB_IDENT_MAXCCH_NAME characters\r
-          in size.\r
-\r
-    If the name cannot be canonicalized for some reason, the\r
-    destination buffer should be set to a zero-length string and the\r
-    \a result member of the ::kcdb_ident_name_xfer structure should be\r
-    set to the error code.  If the destination buffer is set to a\r
-    zero-length string and \a result is KHM_ERROR_SUCCESS, then the\r
-    original name provided in \a name_src is assumed to be already in\r
-    canonical form.\r
- */\r
-#define KMSG_IDENT_CANON_NAME           5\r
-\r
-/*! \brief Compare names\r
-\r
-    Compare two identity names.  The names that are given aren't\r
-    guaranteed to be in canonical form.  The return value should be\r
-    akin to strcmp().\r
-\r
-    Message parameters: \r
-\r
-    - \b vparam : A pointer to a ::kcdb_ident_name_xfer structure.\r
-        The \a name_src member points at the first name, and the \a\r
-        name_alt member specifies the second name.  The result of the\r
-        comparison should be place in \a result.\r
- */\r
-#define KMSG_IDENT_COMPARE_NAME         6\r
-\r
-/*! \brief Set the default identity\r
-\r
-    Set or unset the default identity.  To set the default identity,\r
-    the \a uparam parameter will be set to a non-zero value and a\r
-    handle to the identity will be specified in \a vparam.  To unset\r
-    the default identity (i.e. not have a default identity), a zero\r
-    value will be specified in \a uparam and no identities will be\r
-    specified in \a vparam.\r
-\r
-    When setting a default identity, the identity provider will\r
-    receive this message prior to the ::KCDB_IDENT_FLAG_DEFAULT bit\r
-    being set or reset on any identity.  It should return\r
-    KHM_ERROR_SUCCESS if the requested operation can be performed.\r
-    Returning any other value will abort the operation and will leave\r
-    the default identity unchanged.\r
-\r
-    When resetting the default identity, this message should be\r
-    treated only as a notification.\r
-\r
-    Message parameters:\r
-\r
-    - \a uparam : Is non-zero if an identity is being made default.  If\r
-      this is zero, then identity should be the default.\r
-\r
-    - \a vparam : A handle to the identity to be made default if \a\r
-      uparam is non-zero.  NULL otherwise.\r
-\r
-    Return value:\r
-\r
-    - KHM_ERROR_SUCCESS : The identity should be marked as default\r
-    - Any other value : The identity should not be marked as default\r
-\r
- */\r
-#define KMSG_IDENT_SET_DEFAULT          7\r
-\r
-/*! \brief Set an identity as searchable\r
-\r
-    Set or reset the searchable bit on an identity.  If the \a uparam\r
-    parameter is non-zero, then the searchable bit is being set.\r
-    Otherwise it is being reset.  The identity provider should return\r
-    KHM_ERROR_SUCCESS in order to indicate that the identity should be\r
-    marked as searchable.  Any other value will result in the\r
-    searchable bit being reset on the identity.\r
-\r
-    Message parameters:\r
-\r
-    - \a uparam : Is non-zero if the searchable bit is being set.  Zero\r
-      otherwise.\r
-\r
-    - \a vparam : Handle to the identity\r
-\r
-    Return value:\r
-\r
-    - KHM_ERROR_SUCCESS: The identity should be marked as searchable\r
-    - Any other value : The identity should not be marked as default\r
- */\r
-#define KMSG_IDENT_SET_SEARCHABLE       8\r
-\r
-/*! \brief Get information about an identity\r
-\r
- */\r
-#define KMSG_IDENT_GET_INFO             9\r
-\r
-/*! \brief Enumerate known and accessible identities\r
- */\r
-#define KMSG_IDENT_ENUM_KNOWN           10\r
-\r
-/*! \brief Update information about an identity\r
- */\r
-#define KMSG_IDENT_UPDATE               11\r
-\r
-/*! \brief Retrieve the user interface callback function\r
-\r
-    When obtaining new credentials, the user interface needs to obtain\r
-    a callback function which will provide identity selection\r
-    controls.\r
-\r
-    Message parameters:\r
-\r
-    - \a uparam : Not used\r
-\r
-    - \a vparam : pointer to a ::khui_ident_new_creds_cb which will\r
-         receive the call back.\r
- */\r
-#define KMSG_IDENT_GET_UI_CALLBACK      12\r
-\r
-/*! \brief Notification of the creation of an identity\r
-\r
-    This should be considered just a notification.  The identit\r
-    provider does not have an opportunity to veto the creation of an\r
-    identity whose name has been found to be valid.  However, when\r
-    handing this notification, the identity provider can:\r
-\r
-    - Change the flags of the identity and/or marking the identity as\r
-      invalid.\r
-\r
-    - Change the default identity.\r
-\r
-    Note that this notification is sent before the general :;KMSG_KCDB\r
-    notification of the identity creation is sent.\r
-\r
-    Message parameters:\r
-\r
-    - \a uparam : Not used.\r
-\r
-    - \p vparam : handle to the identity\r
- */\r
-#define KMSG_IDENT_NOTIFY_CREATE        13\r
-\r
-/*@}*/ /* /KMSG_IDENT subtypes */\r
-\r
-/*@}*/ /* / message types */\r
-/*@}*/ /* / kmq */\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 and firing of custom
+    actions.
+
+    \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
+
+/*! \brief A new action was created
+
+    Sent when a new custom action was created.  The \a uparam
+    parameter of the message contains the identifier of the newly
+    created action.
+*/
+#define KMSG_ACT_NEW        4
+
+/*! \brief A custom action was deleted
+
+    Sent after a custom action is deleted.  The \a uparam parameter of
+    the message contains the identifier of the deleted action.
+ */
+#define KMSG_ACT_DELETE     5
+
+/*! \brief A custom action has been activated
+
+    When a custom action is activated, then the listener of that
+    custom action receives this message.  Note that only the listener
+    for that custom action will receive this notification.
+
+    \a uparam of the message is set to the identifier of the custom
+    action.
+ */
+#define KMSG_ACT_ACTIVATE   6
+
+/*! \brief Internal */
+#define KMSG_ACT_BEGIN_CMDLINE       128
+
+/*! \brief Internal */
+#define KMSG_ACT_CONTINUE_CMDLINE    129
+
+/*! \brief Internal */
+#define KMSG_ACT_SYNC_CFG            130
+
+/*! \brief Internal */
+#define KMSG_ACT_END_CMDLINE         131
+
+/*@}*/
+
+/*! \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.
+
+    A plug-in handling this message that wishes to participate in the
+    password change operation is expected to add a
+    ::khui_new_creds_by_type to the list of participants in the
+    ::khui_new_creds structure by calling khui_cw_add_type().
+
+    The password change operation requires user interaction.  Any
+    plug-ins that are participating in the operation need to provide a
+    user-interface.
+
+    Message parameters:
+    - \b vparam : pointer to a ::khui_new_creds structure
+
+    \see khui_cw_add_type(), ::khui_new_creds, ::khui_new_creds_by_type
+ */
+#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.
+
+    A plug-in handling this message that wishes to participate in the
+    new credentials acquisition operation is expected to add a
+    ::khui_new_creds_by_type to hte list of participants in the
+    ::khui_new_creds structure by calling khui_cw_add_type().
+
+    Message parameters:
+    - \b vparam : pointer to a ::khui_new_creds structure
+
+    \see \ref cred_acq, khui_cw_add_type(), ::khui_new_creds,
+    ::khui_new_creds_by_type
+ */
+#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.
+
+    A plug-in handling this message that wishes to participate in the
+    renew credentials operation is expected to add a
+    ::khui_new_creds_by_type to the list of participants in the
+    ::khui_new_creds structure by calling khui_cw_add_type().
+
+    Message parameters:
+    - \b vparam : Pointer to a khui_new_creds object
+
+    \see khui_cw_add_type(), ::khui_new_creds,
+    ::khui_new_creds_by_type
+ */
+#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 : pointer 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_PROCESS, 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
+
+    Handlers of this message should determine whether or not they
+    should participate in the property sheet and if so, add a
+    ::khui_property_page structure to the property sheet.
+
+    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
+
+    Handlers of this message should remove any ::khui_property_page
+    structures they added when processing ::KMSG_CRED_PP_BEGIN.
+
+    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
+
+    \note This is a notification that the property sheet processing
+        has been completed and that the property sheet data structures
+        should be freed.  Any property page data structures should
+        have already been freed while processing KMSG_CRED_PP_END.
+        The validity of the ::khui_property_sheet structure should not
+        be relied upon while processing this message.
+ */
+#define KMSG_CRED_PP_DESTROY        131
+
+/*! \brief An IP address change occurred
+
+    There are no parameters for this message.  The NetIDMgr
+    application handles this message and depending on configuration,
+    posts message for the individual credentials providers to either
+    obtain new credentials or renew old ones.
+ */
+#define KMSG_CRED_ADDR_CHANGE        140
+
+/*! \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_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
+
+/*! \brief Add an alert to the alert queue
+
+    Message parameters:
+    - \b vparam : held pointer to a ::khui_alert object
+
+    \note the ::khui_alert object will be released when the queued
+        messages are displayed.
+ */
+#define KMSG_ALERT_QUEUE 2
+
+/*! \brief Show the next queued alert
+
+    There are no message parameters
+ */
+#define KMSG_ALERT_SHOW_QUEUED 3
+
+/*! \brief Check if there are any queued messages and, if so, update the statusbar
+
+    There are no message parameters
+ */
+#define KMSG_ALERT_CHECK_QUEUE 4
+
+/*! \brief Show a modal alert
+
+    Message parameters:
+    - \b vparam : held pointer to a ::khui_alert object.
+
+    \note the ::khui_alert object will be released when the queued
+        messages are displayed.
+ */
+#define KMSG_ALERT_SHOW_MODAL 5
+
+/*@}*/
+
+/*! \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
index 94e188ff1c3e8d581f07ad32dfc0be812899e9d5..cc680fcadc095784f9480e6896d6e3b29d7c061c 100644 (file)
@@ -1,43 +1,43 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __NETIDMGR_H\r
-#define __NETIDMGR_H\r
-\r
-#include "khdefs.h"\r
-\r
-#include "utils.h"\r
-#include "khuidefs.h"\r
-#include "kmq.h"\r
-#include "khmsgtypes.h"\r
-#include "kcreddb.h"\r
-#include "kherr.h"\r
-#include "kherror.h"\r
-#include "kconfig.h"\r
-#include "kmm.h"\r
-#include "kplugin.h"\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 __NETIDMGR_H
+#define __NETIDMGR_H
+
+#include "khdefs.h"
+
+#include "utils.h"
+#include "khuidefs.h"
+#include "kmq.h"
+#include "khmsgtypes.h"
+#include "kcreddb.h"
+#include "kherr.h"
+#include "kherror.h"
+#include "kconfig.h"
+#include "kmm.h"
+#include "kplugin.h"
+
+#endif
index 60984358fbda1625f191a0e0db8311106335b6ad..6c7ac8e47c4b9fbb874fbc7d6ffe55cb9d70007e 100644 (file)
-/*\r
-* Copyright (c) 2005 Massachusetts Institute of Technology\r
-*\r
-* Permission is hereby granted, free of charge, to any person\r
-* obtaining a copy of this software and associated documentation\r
-* files (the "Software"), to deal in the Software without\r
-* restriction, including without limitation the rights to use, copy,\r
-* modify, merge, publish, distribute, sublicense, and/or sell copies\r
-* of the Software, and to permit persons to whom the Software is\r
-* furnished to do so, subject to the following conditions:\r
-*\r
-* The above copyright notice and this permission notice shall be\r
-* included in all copies or substantial portions of the Software.\r
-*\r
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
-* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
-* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
-* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
-* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
-* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
-* SOFTWARE.\r
-*/\r
-\r
-/* $Id$ */\r
-\r
-#include<shlwapi.h>\r
-#include<kconfiginternal.h>\r
-#include<netidmgr_intver.h>\r
-#include<assert.h>\r
-\r
-kconf_conf_space * conf_root = NULL;\r
-kconf_handle * conf_handles = NULL;\r
-kconf_handle * conf_free_handles = NULL;\r
-\r
-CRITICAL_SECTION cs_conf_global;\r
-CRITICAL_SECTION cs_conf_handle;\r
-LONG conf_init = 0;\r
-LONG conf_status = 0;\r
-\r
-void init_kconf(void) {\r
-    if(InterlockedIncrement(&conf_init) == 1L) {\r
-        /* we are the first */\r
-        InitializeCriticalSection(&cs_conf_global);\r
-        EnterCriticalSection(&cs_conf_global);\r
-        conf_root = khcint_create_empty_space();\r
-        conf_root->name = PWCSDUP(L"Root");\r
-        conf_root->regpath = PWCSDUP(CONFIG_REGPATHW);\r
-        conf_root->refcount++;\r
-        conf_status = 1;\r
-        InitializeCriticalSection(&cs_conf_handle);\r
-        LeaveCriticalSection(&cs_conf_global);\r
-    }\r
-    /* else assume we are already initialized */\r
-}\r
-\r
-void exit_kconf(void) {\r
-    if(khc_is_config_running()) {\r
-        kconf_handle * h;\r
-\r
-        EnterCriticalSection(&cs_conf_global);\r
-\r
-        conf_init = 0;\r
-        conf_status = 0;\r
-\r
-        khcint_free_space(conf_root);\r
-\r
-        LeaveCriticalSection(&cs_conf_global);\r
-        DeleteCriticalSection(&cs_conf_global);\r
-\r
-        EnterCriticalSection(&cs_conf_handle);\r
-        while(conf_free_handles) {\r
-            LPOP(&conf_free_handles, &h);\r
-            if(h) {\r
-                PFREE(h);\r
-            }\r
-        }\r
-\r
-        while(conf_handles) {\r
-            LPOP(&conf_handles, &h);\r
-            if(h) {\r
-                PFREE(h);\r
-            }\r
-        }\r
-        LeaveCriticalSection(&cs_conf_handle);\r
-        DeleteCriticalSection(&cs_conf_handle);\r
-    }\r
-}\r
-\r
-#if defined(DEBUG) && (defined(KH_BUILD_PRIVATE) || defined(KH_BUILD_SPECIAL))\r
-\r
-#include<stdio.h>\r
-\r
-static void\r
-khcint_dump_space(FILE * f, kconf_conf_space * sp) {\r
-\r
-    kconf_conf_space * sc;\r
-\r
-    fprintf(f, "c12\t[%S]\t[%S]\t%d\t0x%x\tWin(%s|%s)|%s\n",\r
-            ((sp->regpath) ? sp->regpath : L"!No Reg path"),\r
-            sp->name,\r
-            (int) sp->refcount,\r
-            (int) sp->flags,\r
-            ((sp->regkey_user)? "HKCU" : ""),\r
-            ((sp->regkey_machine)? "HKLM" : ""),\r
-            ((sp->schema)? "Schema" : ""));\r
-\r
-\r
-    sc = TFIRSTCHILD(sp);\r
-    while(sc) {\r
-\r
-        khcint_dump_space(f, sc);\r
-\r
-        sc = LNEXT(sc);\r
-    }\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khcint_dump_handles(FILE * f) {\r
-    if (khc_is_config_running()) {\r
-        kconf_handle * h, * sh;\r
-\r
-        EnterCriticalSection(&cs_conf_handle);\r
-        EnterCriticalSection(&cs_conf_global);\r
-\r
-        fprintf(f, "c00\t*** Active handles ***\n");\r
-        fprintf(f, "c01\tHandle\tName\tFlags\tRegpath\n");\r
-\r
-        h = conf_handles;\r
-        while(h) {\r
-            kconf_conf_space * sp;\r
-\r
-            sp = h->space;\r
-\r
-            if (!khc_is_handle(h) || sp == NULL) {\r
-\r
-                fprintf(f, "c02\t!!INVALID HANDLE!!\n");\r
-\r
-            } else {\r
-\r
-                fprintf(f, "c02\t0x%p\t[%S]\t0x%x\t[%S]\n",\r
-                        h,\r
-                        sp->name,\r
-                        h->flags,\r
-                        sp->regpath);\r
-\r
-                sh = khc_shadow(h);\r
-\r
-                while(sh) {\r
-\r
-                    sp = sh->space;\r
-\r
-                    if (!khc_is_handle(sh) || sp == NULL) {\r
-\r
-                        fprintf(f, "c02\t0x%p:Shadow:0x%p\t[!!INVALID HANDLE!!]\n",\r
-                                h, sh);\r
-\r
-                    } else {\r
-\r
-                        fprintf(f, "c02\t0x%p:Shadow:0x%p,[%S]\t0x%x\t[%S]\n",\r
-                                h, sh,\r
-                                sp->name,\r
-                                sh->flags,\r
-                                sp->regpath);\r
-\r
-                    }\r
-\r
-                    sh = khc_shadow(sh);\r
-                }\r
-\r
-            }\r
-\r
-            h = LNEXT(h);\r
-        }\r
-\r
-        fprintf(f, "c03\t------  End ---------\n");\r
-\r
-        fprintf(f, "c10\t*** Active Configuration Spaces ***\n");\r
-        fprintf(f, "c11\tReg path\tName\tRefcount\tFlags\tLayers\n");\r
-\r
-        khcint_dump_space(f, conf_root);\r
-\r
-        fprintf(f, "c13\t------  End ---------\n");\r
-\r
-        LeaveCriticalSection(&cs_conf_global);\r
-        LeaveCriticalSection(&cs_conf_handle);\r
-\r
-    } else {\r
-        fprintf(f, "c00\t------- KHC Configuration not running -------\n");\r
-    }\r
-}\r
-\r
-#endif\r
-\r
-/* obtains cs_conf_handle/cs_conf_global */\r
-kconf_handle * \r
-khcint_handle_from_space(kconf_conf_space * s, khm_int32 flags)\r
-{\r
-    kconf_handle * h;\r
-\r
-    EnterCriticalSection(&cs_conf_handle);\r
-    LPOP(&conf_free_handles, &h);\r
-    if(!h) {\r
-        h = PMALLOC(sizeof(kconf_handle));\r
-        assert(h != NULL);\r
-    }\r
-    ZeroMemory((void *) h, sizeof(kconf_handle));\r
-\r
-    h->magic = KCONF_HANDLE_MAGIC;\r
-    khcint_space_hold(s);\r
-    h->space = s;\r
-    h->flags = flags;\r
-\r
-    LPUSH(&conf_handles, h);\r
-    LeaveCriticalSection(&cs_conf_handle);\r
-\r
-    return h;\r
-}\r
-\r
-/* obtains cs_conf_handle/cs_conf_global */\r
-void \r
-khcint_handle_free(kconf_handle * h)\r
-{\r
-    kconf_handle * lower;\r
-\r
-    EnterCriticalSection(&cs_conf_handle);\r
-#ifdef DEBUG\r
-    /* check if the handle is actually in use */\r
-    {\r
-        kconf_handle * a;\r
-        a = conf_handles;\r
-        while(a) {\r
-            if(h == a)\r
-                break;\r
-            a = LNEXT(a);\r
-        }\r
-\r
-        if(a == NULL) {\r
-            DebugBreak();\r
-\r
-            /* hmm.  the handle was not in the in-use list */\r
-            LeaveCriticalSection(&cs_conf_handle);\r
-            return;\r
-        }\r
-    }\r
-#endif\r
-    while(h) {\r
-        LDELETE(&conf_handles, h);\r
-        if(h->space) {\r
-            khcint_space_release(h->space);\r
-            h->space = NULL;\r
-        }\r
-        lower = h->lower;\r
-        h->magic = 0;\r
-        LPUSH(&conf_free_handles, h);\r
-        h = lower;\r
-    }\r
-    LeaveCriticalSection(&cs_conf_handle);\r
-}\r
-\r
-/* obains cs_conf_handle/cs_conf_global */\r
-kconf_handle * \r
-khcint_handle_dup(kconf_handle * o)\r
-{\r
-    kconf_handle * h;\r
-    kconf_handle * r;\r
-\r
-    r = khcint_handle_from_space(o->space, o->flags);\r
-    h = r;\r
-\r
-    while(o->lower) {\r
-        h->lower = khcint_handle_from_space(o->lower->space, o->lower->flags);\r
-\r
-        o = o->lower;\r
-        h = h->lower;\r
-    }\r
-\r
-    return r;\r
-}\r
-\r
-/* obtains cs_conf_global */\r
-void \r
-khcint_space_hold(kconf_conf_space * s) {\r
-    EnterCriticalSection(&cs_conf_global);\r
-    s->refcount ++;\r
-    LeaveCriticalSection(&cs_conf_global);\r
-}\r
-\r
-/* called with cs_conf_global */\r
-void\r
-khcint_try_free_space(kconf_conf_space * s) {\r
-\r
-    if (TFIRSTCHILD(s) == NULL &&\r
-        s->refcount == 0 &&\r
-        s->schema == NULL) {\r
-\r
-        kconf_conf_space * p;\r
-\r
-        p = TPARENT(s);\r
-\r
-        if (p == NULL)\r
-            return;\r
-\r
-        TDELCHILD(p, s);\r
-\r
-        khcint_free_space(s);\r
-\r
-        khcint_try_free_space(p);\r
-    }\r
-}\r
-\r
-/* obtains cs_conf_global */\r
-void \r
-khcint_space_release(kconf_conf_space * s) {\r
-    khm_int32 l;\r
-\r
-    EnterCriticalSection(&cs_conf_global);\r
-\r
-    l = -- s->refcount;\r
-    if (l == 0) {\r
-        if(s->regkey_machine)\r
-            RegCloseKey(s->regkey_machine);\r
-        if(s->regkey_user)\r
-            RegCloseKey(s->regkey_user);\r
-        s->regkey_machine = NULL;\r
-        s->regkey_user = NULL;\r
-\r
-        if (s->flags &\r
-            (KCONF_SPACE_FLAG_DELETE_M |\r
-             KCONF_SPACE_FLAG_DELETE_U)) {\r
-            khcint_remove_space(s, s->flags);\r
-        } else {\r
-#ifdef USE_TRY_FREE\r
-            /* even if the refcount is zero, we shouldn't free a\r
-               configuration space just yet since that doesn't play\r
-               well with the configuration space enumeration mechanism\r
-               which expects the spaces to dangle around if there is a\r
-               corresponding registry key or schema. */\r
-            khcint_try_free_space(s);\r
-#endif\r
-        }\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_conf_global);\r
-}\r
-\r
-/* case sensitive replacement for RegOpenKeyEx */\r
-LONG \r
-khcint_RegOpenKeyEx(HKEY hkey, LPCWSTR sSubKey, DWORD ulOptions,\r
-                    REGSAM samDesired, PHKEY phkResult) {\r
-    int i;\r
-    wchar_t sk_name[KCONF_MAXCCH_NAME];\r
-    FILETIME ft;\r
-    size_t cch;\r
-    HKEY hkp = NULL;\r
-    const wchar_t * t;\r
-    LONG rv = ERROR_SUCCESS;\r
-\r
-    hkp = hkey;\r
-    t = sSubKey;\r
-\r
-    /* check for case insensitive prefix first */\r
-    if (!_wcsnicmp(sSubKey, CONFIG_REGPATHW, ARRAYLENGTH(CONFIG_REGPATHW) - 1)) {\r
-        HKEY hkt;\r
-\r
-        t = sSubKey + (ARRAYLENGTH(CONFIG_REGPATHW) - 1);\r
-\r
-#ifdef DEBUG\r
-        assert(*t == L'\0' || *t == L'\\');\r
-#endif\r
-\r
-        rv = RegOpenKeyEx(hkp,\r
-                          CONFIG_REGPATHW,\r
-                          ulOptions,\r
-                          samDesired,\r
-                          &hkt);\r
-\r
-        if (rv != ERROR_SUCCESS)\r
-            return rv;\r
-\r
-        if (*t == L'\0') {\r
-            *phkResult = hkt;\r
-            return rv;\r
-        }\r
-\r
-        t++;\r
-        hkp = hkt;\r
-    }\r
-\r
-    /* descend down the components of the subkey */\r
-    while(TRUE) {\r
-        wchar_t * slash;\r
-        HKEY hkt;\r
-\r
-        slash = wcschr(t, L'\\');\r
-        if (slash == NULL)\r
-            break;\r
-\r
-        if (FAILED(StringCchCopyN(sk_name, ARRAYLENGTH(sk_name),\r
-                                  t, slash - t))) {\r
-            rv = ERROR_CANTOPEN;\r
-            goto _cleanup;\r
-        }\r
-\r
-        sk_name[slash - t] = L'\0';\r
-        t = slash+1;\r
-\r
-        if (khcint_RegOpenKeyEx(hkp, sk_name, ulOptions, samDesired, &hkt) ==\r
-            ERROR_SUCCESS) {\r
-\r
-            if (hkp != hkey)\r
-                RegCloseKey(hkp);\r
-            hkp = hkt;\r
-\r
-        } else {\r
-\r
-            rv = ERROR_CANTOPEN;\r
-            goto _cleanup;\r
-\r
-        }\r
-    }\r
-\r
-    /* by now hkp is a handle to the parent of the last component in\r
-       the subkey.  t is a pointer to the last component. */\r
-\r
-    if (FAILED(StringCchLength(t, KCONF_MAXCCH_NAME, &cch))) {\r
-        rv = ERROR_CANTOPEN;\r
-        goto _cleanup;\r
-    }\r
-\r
-    /* go through and find the case sensitive match for the key */\r
-\r
-    for (i=0; ;i++) {\r
-        LONG l;\r
-        DWORD dw;\r
-\r
-        dw = ARRAYLENGTH(sk_name);\r
-        l = RegEnumKeyEx(hkp, i, sk_name, &dw,\r
-                         NULL, NULL, NULL, &ft);\r
-\r
-        if (l != ERROR_SUCCESS) {\r
-            rv = ERROR_CANTOPEN;\r
-            goto _cleanup;\r
-        }\r
-\r
-        if (!(wcsncmp(sk_name, t, cch))) {\r
-            /* bingo! ?? */\r
-            if (cch < KCONF_MAXCCH_NAME &&\r
-                (sk_name[cch] == L'\0' ||\r
-                 sk_name[cch] == L'~')) {\r
-                rv = RegOpenKeyEx(hkp, sk_name, ulOptions,\r
-                                  samDesired, phkResult);\r
-                goto _cleanup;\r
-            }\r
-        }\r
-    }\r
-\r
- _cleanup:\r
-    if (hkp != hkey && hkp != NULL)\r
-        RegCloseKey(hkp);\r
-\r
-    return rv;\r
-}\r
-\r
-/*! \internal\r
-\r
- \note This function is not a good replacement for RegDeleteKey since\r
-     it deletes all the subkeys in addition to the key being deleted.\r
- */\r
-LONG\r
-khcint_RegDeleteKey(HKEY hKey,\r
-                    LPCWSTR lpSubKey) {\r
-    int i;\r
-    wchar_t sk_name[KCONF_MAXCCH_NAME];\r
-    FILETIME ft;\r
-    size_t cch;\r
-    LONG rv = ERROR_SUCCESS;\r
-\r
-    /* go through and find the case sensitive match for the key */\r
-\r
-    if (FAILED(StringCchLength(lpSubKey, KCONF_MAXCCH_NAME, &cch)))\r
-        return ERROR_BADKEY;\r
-\r
-    for (i=0; ;i++) {\r
-        LONG l;\r
-        DWORD dw;\r
-\r
-        dw = ARRAYLENGTH(sk_name);\r
-        l = RegEnumKeyEx(hKey, i, sk_name, &dw,\r
-                         NULL, NULL, NULL, &ft);\r
-\r
-        if (l != ERROR_SUCCESS) {\r
-            rv = ERROR_BADKEY;\r
-            goto _cleanup;\r
-        }\r
-\r
-        if (!(wcsncmp(sk_name, lpSubKey, cch))) {\r
-            /* bingo! ?? */\r
-            if ((sk_name[cch] == L'\0' ||\r
-                 sk_name[cch] == L'~')) {\r
-\r
-                /* instead of calling RegDeleteKey we call SHDeleteKey\r
-                   because we want to blow off all the subkeys as\r
-                   well.  This is different from the behavior of\r
-                   RegDeleteKey making khcint_RegDeleteKey not a very\r
-                   good case sensitive replacement for\r
-                   RegDeleteKey. */\r
-\r
-                rv = SHDeleteKey(hKey, sk_name);\r
-                goto _cleanup;\r
-            }\r
-        }\r
-    }\r
-\r
- _cleanup:\r
-    return rv;\r
-}\r
-\r
-LONG\r
-khcint_RegCreateKeyEx(HKEY hKey,\r
-                      LPCWSTR lpSubKey,\r
-                      DWORD Reserved,\r
-                      LPWSTR lpClass,\r
-                      DWORD dwOptions,\r
-                      REGSAM samDesired,\r
-                      LPSECURITY_ATTRIBUTES lpSecurityAttributes,\r
-                      PHKEY phkResult,\r
-                      LPDWORD lpdwDisposition) {\r
-    LONG l;\r
-    int i;\r
-    long index = 0;\r
-    wchar_t sk_name[KCONF_MAXCCH_NAME]; /* hard limit in Windows */\r
-    FILETIME ft;\r
-    size_t cch;\r
-    const wchar_t * t;\r
-    LONG rv = ERROR_SUCCESS;\r
-    HKEY hkp = NULL;\r
-\r
-    hkp = hKey;\r
-    t = lpSubKey;\r
-\r
-    /* check for case insensitive prefix first */\r
-    if (!_wcsnicmp(lpSubKey, CONFIG_REGPATHW, ARRAYLENGTH(CONFIG_REGPATHW) - 1)) {\r
-        HKEY hkt;\r
-\r
-        t = lpSubKey + (ARRAYLENGTH(CONFIG_REGPATHW) - 1);\r
-\r
-#ifdef DEBUG\r
-        assert(*t == L'\0' || *t == L'\\');\r
-#endif\r
-\r
-        rv = RegCreateKeyEx(hkp,\r
-                            CONFIG_REGPATHW,\r
-                            Reserved,\r
-                            lpClass,\r
-                            dwOptions,\r
-                            samDesired,\r
-                            lpSecurityAttributes,\r
-                            &hkt,\r
-                            lpdwDisposition);\r
-\r
-        if (rv != ERROR_SUCCESS)\r
-            return rv;\r
-\r
-        if (*t == L'\0') {\r
-            *phkResult = hkt;\r
-            return rv;\r
-        }\r
-\r
-        t++;\r
-        hkp = hkt;\r
-    }\r
-\r
-    while(TRUE) {\r
-        wchar_t * slash;\r
-        HKEY hkt;\r
-\r
-        slash = wcschr(t, L'\\');\r
-        if (slash == NULL)\r
-            break;\r
-\r
-        if (FAILED(StringCchCopyN(sk_name, ARRAYLENGTH(sk_name),\r
-                                  t, slash - t))) {\r
-            rv = ERROR_CANTOPEN;\r
-            goto _cleanup;\r
-        }\r
-\r
-        sk_name[slash - t] = L'\0';\r
-        t = slash+1;\r
-\r
-        if (khcint_RegOpenKeyEx(hkp, sk_name, 0, samDesired, &hkt) ==\r
-            ERROR_SUCCESS) {\r
-\r
-            if (hkp != hKey)\r
-                RegCloseKey(hkp);\r
-            hkp = hkt;\r
-        } else {\r
-\r
-            rv = RegCreateKeyEx(hKey,\r
-                                lpSubKey,\r
-                                Reserved,\r
-                                lpClass,\r
-                                dwOptions,\r
-                                samDesired,\r
-                                lpSecurityAttributes,\r
-                                phkResult,\r
-                                lpdwDisposition);\r
-            goto _cleanup;\r
-        }\r
-    }\r
-\r
-    if (FAILED(StringCchLength(t, KCONF_MAXCCH_NAME, &cch))) {\r
-        rv = ERROR_CANTOPEN;\r
-        goto _cleanup;\r
-    }\r
-\r
-    for (i=0; ;i++) {\r
-        DWORD dw;\r
-\r
-        dw = ARRAYLENGTH(sk_name);\r
-        l = RegEnumKeyEx(hkp, i, sk_name, &dw,\r
-                         NULL, NULL, NULL, &ft);\r
-\r
-        if (l != ERROR_SUCCESS)\r
-            break;\r
-\r
-        if (!(wcsncmp(sk_name, t, cch))) {\r
-            /* bingo! ?? */\r
-            if (sk_name[cch] == L'\0' ||\r
-                sk_name[cch] == L'~') {\r
-                l = RegOpenKeyEx(hkp, sk_name, 0,\r
-                                 samDesired, phkResult);\r
-                if (l == ERROR_SUCCESS && lpdwDisposition)\r
-                    *lpdwDisposition = REG_OPENED_EXISTING_KEY;\r
-                rv = l;\r
-                goto _cleanup;\r
-            }\r
-        }\r
-\r
-        if (!_wcsnicmp(sk_name, t, cch) &&\r
-            (sk_name[cch] == L'\0' ||\r
-             sk_name[cch] == L'~')) {\r
-            long new_idx;\r
-\r
-            if (sk_name[cch] == L'\0')\r
-                new_idx = 1;\r
-            else if (cch + 1 < KCONF_MAXCCH_NAME)\r
-                new_idx = wcstol(sk_name + (cch + 1), NULL, 10);\r
-            else\r
-                return ERROR_BUFFER_OVERFLOW;\r
-\r
-            assert(new_idx > 0);\r
-\r
-            if (new_idx > index)\r
-                index = new_idx;\r
-        }\r
-    }\r
-\r
-    if (index != 0) {\r
-        if (FAILED(StringCbPrintf(sk_name, sizeof(sk_name),\r
-                                  L"%s~%d", t, index)))\r
-            return ERROR_BUFFER_OVERFLOW;\r
-    } else {\r
-        StringCbCopy(sk_name, sizeof(sk_name), t);\r
-    }\r
-\r
-    rv = RegCreateKeyEx(hkp,\r
-                        sk_name,\r
-                        Reserved,\r
-                        lpClass,\r
-                        dwOptions,\r
-                        samDesired,\r
-                        lpSecurityAttributes,\r
-                        phkResult,\r
-                        lpdwDisposition);\r
-\r
- _cleanup:\r
-\r
-    if (hkp != hKey && hkp != NULL)\r
-        RegCloseKey(hkp);\r
-\r
-    return rv;\r
-}\r
-\r
-/* obtains cs_conf_global */\r
-HKEY \r
-khcint_space_open_key(kconf_conf_space * s, khm_int32 flags) {\r
-    HKEY hk = NULL;\r
-    int nflags = 0;\r
-    DWORD disp;\r
-    if(flags & KCONF_FLAG_MACHINE) {\r
-        if(s->regkey_machine)\r
-            return s->regkey_machine;\r
-        if((khcint_RegOpenKeyEx(HKEY_LOCAL_MACHINE, s->regpath, 0, \r
-                                KEY_READ | KEY_WRITE, &hk) != \r
-            ERROR_SUCCESS) && \r
-           !(flags & KHM_PERM_WRITE)) {\r
-\r
-            if(khcint_RegOpenKeyEx(HKEY_LOCAL_MACHINE, s->regpath, 0, \r
-                                   KEY_READ, &hk) == ERROR_SUCCESS) {\r
-                nflags = KHM_PERM_READ;\r
-            }\r
-\r
-        }\r
-        if(!hk && (flags & KHM_FLAG_CREATE)) {\r
-\r
-            khcint_RegCreateKeyEx(HKEY_LOCAL_MACHINE, \r
-                                  s->regpath, \r
-                                  0,\r
-                                  NULL,\r
-                                  REG_OPTION_NON_VOLATILE,\r
-                                  KEY_READ | KEY_WRITE,\r
-                                  NULL,\r
-                                  &hk,\r
-                                  &disp);\r
-        }\r
-        if(hk) {\r
-            EnterCriticalSection(&cs_conf_global);\r
-            s->regkey_machine = hk;\r
-            s->regkey_machine_flags = nflags;\r
-            LeaveCriticalSection(&cs_conf_global);\r
-        }\r
-\r
-        return hk;\r
-    } else {\r
-        if(s->regkey_user)\r
-            return s->regkey_user;\r
-        if((khcint_RegOpenKeyEx(HKEY_CURRENT_USER, s->regpath, 0, \r
-                                KEY_READ | KEY_WRITE, &hk) != \r
-            ERROR_SUCCESS) && \r
-           !(flags & KHM_PERM_WRITE)) {\r
-            if(khcint_RegOpenKeyEx(HKEY_CURRENT_USER, s->regpath, 0, \r
-                                   KEY_READ, &hk) == ERROR_SUCCESS) {\r
-                nflags = KHM_PERM_READ;\r
-            }\r
-        }\r
-        if(!hk && (flags & KHM_FLAG_CREATE)) {\r
-            khcint_RegCreateKeyEx(HKEY_CURRENT_USER, \r
-                                  s->regpath, 0, NULL,\r
-                                  REG_OPTION_NON_VOLATILE,\r
-                                  KEY_READ | KEY_WRITE,\r
-                                  NULL, &hk, &disp);\r
-        }\r
-        if(hk) {\r
-            EnterCriticalSection(&cs_conf_global);\r
-            s->regkey_user = hk;\r
-            s->regkey_user_flags = nflags;\r
-            LeaveCriticalSection(&cs_conf_global);\r
-        }\r
-\r
-        return hk;\r
-    }\r
-}\r
-\r
-/* obtains cs_conf_handle/cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_shadow_space(khm_handle upper, khm_handle lower)\r
-{\r
-    kconf_handle * h;\r
-\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    if(!khc_is_handle(upper)) {\r
-#ifdef DEBUG\r
-        DebugBreak();\r
-#endif\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    h = (kconf_handle *) upper;\r
-\r
-    EnterCriticalSection(&cs_conf_handle);\r
-    if(h->lower) {\r
-        EnterCriticalSection(&cs_conf_global);\r
-        khcint_handle_free(h->lower);\r
-        LeaveCriticalSection(&cs_conf_global);\r
-        h->lower = NULL;\r
-    }\r
-\r
-    if(khc_is_handle(lower)) {\r
-        kconf_handle * l;\r
-        kconf_handle * lc;\r
-\r
-        l = (kconf_handle *) lower;\r
-        lc = khcint_handle_dup(l);\r
-        h->lower = lc;\r
-    }\r
-    LeaveCriticalSection(&cs_conf_handle);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* no locks */\r
-kconf_conf_space * \r
-khcint_create_empty_space(void) {\r
-    kconf_conf_space * r;\r
-\r
-    r = PMALLOC(sizeof(kconf_conf_space));\r
-    assert(r != NULL);\r
-    ZeroMemory(r,sizeof(kconf_conf_space));\r
-\r
-    return r;\r
-}\r
-\r
-/* called with cs_conf_global */\r
-void \r
-khcint_free_space(kconf_conf_space * r) {\r
-    kconf_conf_space * c;\r
-\r
-    if(!r)\r
-        return;\r
-\r
-    TPOPCHILD(r, &c);\r
-    while(c) {\r
-        khcint_free_space(c);\r
-        TPOPCHILD(r, &c);\r
-    }\r
-\r
-    if(r->name)\r
-        PFREE(r->name);\r
-\r
-    if(r->regpath)\r
-        PFREE(r->regpath);\r
-\r
-    if(r->regkey_machine)\r
-        RegCloseKey(r->regkey_machine);\r
-\r
-    if(r->regkey_user)\r
-        RegCloseKey(r->regkey_user);\r
-\r
-    PFREE(r);\r
-}\r
-\r
-/* obtains cs_conf_global */\r
-khm_int32 \r
-khcint_open_space(kconf_conf_space * parent, \r
-                  const wchar_t * sname, size_t n_sname, \r
-                  khm_int32 flags, kconf_conf_space **result) {\r
-    kconf_conf_space * p;\r
-    kconf_conf_space * c;\r
-    HKEY pkey = NULL;\r
-    HKEY ckey = NULL;\r
-    wchar_t buf[KCONF_MAXCCH_NAME];\r
-    size_t cb_regpath = 0;\r
-\r
-    if(!parent)\r
-        p = conf_root;\r
-    else\r
-        p = parent;\r
-\r
-    if(n_sname >= KCONF_MAXCCH_NAME || n_sname <= 0)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    StringCchCopyN(buf, ARRAYLENGTH(buf), sname, n_sname);\r
-\r
-    /* see if there is already a config space by this name. if so,\r
-       return it.  Note that if the configuration space is specified\r
-       in a schema, we would find it here. */\r
-    EnterCriticalSection(&cs_conf_global);\r
-    c = TFIRSTCHILD(p);\r
-    while(c) {\r
-        if(c->name && !wcscmp(c->name, buf))\r
-            break;\r
-\r
-        c = LNEXT(c);\r
-    }\r
-    LeaveCriticalSection(&cs_conf_global);\r
-\r
-    if(c) {\r
-\r
-        if (c->flags & KCONF_SPACE_FLAG_DELETED) {\r
-            if (flags & KHM_FLAG_CREATE) {\r
-                c->flags &= ~(KCONF_SPACE_FLAG_DELETED |\r
-                              KCONF_SPACE_FLAG_DELETE_M |\r
-                              KCONF_SPACE_FLAG_DELETE_U);\r
-            } else {\r
-                *result = NULL;\r
-                return KHM_ERROR_NOT_FOUND;\r
-            }\r
-        }\r
-\r
-        khcint_space_hold(c);\r
-        *result = c;\r
-        return KHM_ERROR_SUCCESS;\r
-    }\r
-\r
-    if(!(flags & KHM_FLAG_CREATE)) {\r
-\r
-        /* we are not creating the space, so it must exist in the form of a\r
-        registry key in HKLM or HKCU.  If it existed as a schema, we\r
-        would have already retured it above. */\r
-        \r
-        if (flags & KCONF_FLAG_USER)\r
-            pkey = khcint_space_open_key(p, KHM_PERM_READ | KCONF_FLAG_USER);\r
-\r
-        if((!pkey ||\r
-            (khcint_RegOpenKeyEx(pkey, buf, 0, KEY_READ, &ckey) !=\r
-             ERROR_SUCCESS))\r
-           && (flags & KCONF_FLAG_MACHINE)) {\r
-\r
-            pkey = khcint_space_open_key(p, KHM_PERM_READ | KCONF_FLAG_MACHINE);\r
-            if(!pkey ||\r
-               (khcint_RegOpenKeyEx(pkey, buf, 0, KEY_READ, &ckey) !=\r
-                ERROR_SUCCESS)) {\r
-                *result = NULL;\r
-\r
-                return KHM_ERROR_NOT_FOUND;\r
-            }\r
-        }\r
-\r
-        if(ckey) {\r
-            RegCloseKey(ckey);\r
-            ckey = NULL;\r
-        }\r
-    }\r
-\r
-    c = khcint_create_empty_space();\r
-    \r
-    /*SAFE: buf: is of known length < KCONF_MAXCCH_NAME */\r
-    c->name = PWCSDUP(buf);\r
-\r
-    /*SAFE: p->regpath: is valid since it was set using this same\r
-      function. */\r
-    /*SAFE: buf: see above */\r
-    cb_regpath = (wcslen(p->regpath) + wcslen(buf) + 2) * sizeof(wchar_t);\r
-    c->regpath = PMALLOC(cb_regpath);\r
-\r
-    assert(c->regpath != NULL);\r
-\r
-    /*SAFE: c->regpath: allocated above to be big enough */\r
-    /*SAFE: p->regpath: see above */\r
-    StringCbCopy(c->regpath, cb_regpath, p->regpath);\r
-    StringCbCat(c->regpath, cb_regpath, L"\\");\r
-\r
-    /*SAFE: buf: see above */\r
-    StringCbCat(c->regpath, cb_regpath, buf);\r
-\r
-    khcint_space_hold(c);\r
-\r
-    EnterCriticalSection(&cs_conf_global);\r
-    TADDCHILD(p,c);\r
-    LeaveCriticalSection(&cs_conf_global);\r
-\r
-    *result = c;\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* obtains cs_conf_handle/cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_open_space(khm_handle parent, const wchar_t * cspace, khm_int32 flags, \r
-               khm_handle * result) {\r
-    kconf_handle * h;\r
-    kconf_conf_space * p;\r
-    kconf_conf_space * c = NULL;\r
-    size_t cbsize;\r
-    const wchar_t * str;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if(!khc_is_config_running()) {\r
-        return KHM_ERROR_NOT_READY;\r
-    }\r
-\r
-    if(!result || (parent && !khc_is_handle(parent))) {\r
-#ifdef DEBUG\r
-        DebugBreak();\r
-#endif\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    if(!parent)\r
-        p = conf_root;\r
-    else {\r
-        h = (kconf_handle *) parent;\r
-        p = khc_space_from_handle(parent);\r
-    }\r
-\r
-    khcint_space_hold(p);\r
-\r
-    /* if none of these flags are specified, make it seem like all of\r
-       them were */\r
-    if(!(flags & KCONF_FLAG_USER) &&\r
-        !(flags & KCONF_FLAG_MACHINE) &&\r
-        !(flags & KCONF_FLAG_SCHEMA))\r
-        flags |= KCONF_FLAG_USER | KCONF_FLAG_MACHINE | KCONF_FLAG_SCHEMA;\r
-\r
-    if(cspace == NULL) {\r
-        *result = (khm_handle) khcint_handle_from_space(p, flags);\r
-        khcint_space_release(p);\r
-        return KHM_ERROR_SUCCESS;\r
-    }\r
-\r
-    if(FAILED(StringCbLength(cspace, KCONF_MAXCB_PATH, &cbsize))) {\r
-        khcint_space_release(p);\r
-        *result = NULL;\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    str = cspace;\r
-    while(TRUE) {\r
-        const wchar_t * end = NULL;\r
-\r
-        if (!(flags & KCONF_FLAG_NOPARSENAME)) {\r
-\r
-            end = wcschr(str, L'\\'); /* safe because cspace was\r
-                                     validated above */\r
-        }\r
-\r
-        if(!end) {\r
-            if(flags & KCONF_FLAG_TRAILINGVALUE) {\r
-                /* we are at the value component */\r
-                c = p;\r
-                khcint_space_hold(c);\r
-                break;\r
-            } else\r
-                end = str + wcslen(str);  /* safe because cspace was\r
-                                             validated above */\r
-        }\r
-\r
-        rv = khcint_open_space(p, str, end - str, flags, &c);\r
-\r
-        if(KHM_SUCCEEDED(rv) && (*end == L'\\')) {\r
-            khcint_space_release(p);\r
-            p = c;\r
-            c = NULL;\r
-            str = end+1;\r
-        }\r
-        else\r
-            break;\r
-    }\r
-\r
-    khcint_space_release(p);\r
-    if(KHM_SUCCEEDED(rv)) {\r
-        *result = khcint_handle_from_space(c, flags);\r
-    } else\r
-        *result = NULL;\r
-\r
-    if (c)\r
-        khcint_space_release(c);\r
-\r
-    return rv;\r
-}\r
-\r
-/* obtains cs_conf_handle/cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_close_space(khm_handle csp) {\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    if(!khc_is_handle(csp)) {\r
-#ifdef DEBUG\r
-        DebugBreak();\r
-#endif\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    khcint_handle_free((kconf_handle *) csp);\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* obtains cs_conf_handle/cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_read_string(khm_handle pconf, \r
-                const wchar_t * pvalue, \r
-                wchar_t * buf, \r
-                khm_size * bufsize) \r
-{\r
-    kconf_conf_space * c;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    do {\r
-        HKEY hku = NULL;\r
-        HKEY hkm = NULL;\r
-        const wchar_t * value = NULL;\r
-        int free_space = 0;\r
-        khm_handle conf = NULL;\r
-        DWORD size;\r
-        DWORD type;\r
-        LONG hr;\r
-\r
-        int i;\r
-\r
-        if((value = wcsrchr(pvalue, L'\\')) != NULL) {\r
-\r
-            if(KHM_FAILED(khc_open_space(\r
-                pconf, \r
-                pvalue, \r
-                KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), \r
-                &conf)))\r
-                goto _shadow;\r
-\r
-            free_space = 1;\r
-\r
-            if (value) {\r
-                value++;\r
-            } else {\r
-#ifdef DEBUG\r
-                assert(FALSE);\r
-#endif\r
-            }\r
-        } else {\r
-            value = pvalue;\r
-            conf = pconf;\r
-            free_space = 0;\r
-        }\r
-\r
-        if(!khc_is_handle(conf))\r
-            goto _shadow;\r
-\r
-        c = khc_space_from_handle(conf);\r
-\r
-        if(khc_is_user_handle(conf))\r
-            hku = khcint_space_open_key(c, KHM_PERM_READ);\r
-\r
-        if(khc_is_machine_handle(conf))\r
-            hkm = khcint_space_open_key(c, KHM_PERM_READ | KCONF_FLAG_MACHINE);\r
-\r
-        size = (DWORD) *bufsize;\r
-        if(hku) {\r
-            hr = RegQueryValueEx(hku, value, NULL, &type, (LPBYTE) buf, &size);\r
-            if(hr == ERROR_SUCCESS) {\r
-                if(type != REG_SZ) {\r
-                    rv = KHM_ERROR_TYPE_MISMATCH;\r
-                    goto _exit;\r
-                }\r
-                else {\r
-                    *bufsize = size;\r
-                    /* if buf==NULL, RegQueryValueEx will return success and just return the\r
-                       required buffer size in 'size' */\r
-                    rv = (buf)? KHM_ERROR_SUCCESS: KHM_ERROR_TOO_LONG;\r
-                    goto _exit;\r
-                }\r
-            } else {\r
-                if(hr == ERROR_MORE_DATA) {\r
-                    *bufsize = size;\r
-                    rv = KHM_ERROR_TOO_LONG;\r
-                    goto _exit;\r
-                }\r
-            }\r
-        }\r
-\r
-        size = (DWORD) *bufsize;\r
-        if(hkm) {\r
-            hr = RegQueryValueEx(hkm, value, NULL, &type, (LPBYTE) buf, &size);\r
-            if(hr == ERROR_SUCCESS) {\r
-                if(type != REG_SZ) {\r
-                    rv = KHM_ERROR_TYPE_MISMATCH;\r
-                    goto _exit;\r
-                }\r
-                else {\r
-                    *bufsize = size;\r
-                    rv = (buf)? KHM_ERROR_SUCCESS: KHM_ERROR_TOO_LONG;\r
-                    goto _exit;\r
-                }\r
-            } else {\r
-                if(hr == ERROR_MORE_DATA) {\r
-                    *bufsize = size;\r
-                    rv = KHM_ERROR_TOO_LONG;\r
-                    goto _exit;\r
-                }\r
-            }\r
-        }\r
-\r
-        if(c->schema && khc_is_schema_handle(conf)) {\r
-            for(i=0;i<c->nSchema;i++) {\r
-                if(c->schema[i].type == KC_STRING && !wcscmp(value, c->schema[i].name)) {\r
-                    /* found it */\r
-                    size_t cbsize = 0;\r
-\r
-                    if(!c->schema[i].value) {\r
-                        rv = KHM_ERROR_NOT_FOUND;\r
-                        goto _exit;\r
-                    }\r
-\r
-                    if(FAILED(StringCbLength((wchar_t *) c->schema[i].value, KCONF_MAXCB_STRING, &cbsize))) {\r
-                        rv = KHM_ERROR_NOT_FOUND;\r
-                        goto _exit;\r
-                    }\r
-                    cbsize += sizeof(wchar_t);\r
-\r
-                    if(!buf || *bufsize < cbsize) {\r
-                        *bufsize = cbsize;\r
-                        rv = KHM_ERROR_TOO_LONG;\r
-                        goto _exit;\r
-                    }\r
-\r
-                    StringCbCopy(buf, *bufsize, (wchar_t *) c->schema[i].value);\r
-                    *bufsize = cbsize;\r
-                    rv = KHM_ERROR_SUCCESS;\r
-                    goto _exit;\r
-                }\r
-            }\r
-        }\r
-\r
-_shadow:\r
-        if(free_space && conf)\r
-            khc_close_space(conf);\r
-\r
-        if(khc_is_shadowed(pconf)) {\r
-            pconf = khc_shadow(pconf);\r
-            continue;\r
-        } else {\r
-            rv = KHM_ERROR_NOT_FOUND;\r
-            break;\r
-        }\r
-\r
-_exit:\r
-        if(free_space && conf)\r
-            khc_close_space(conf);\r
-        break;\r
-\r
-    } while(TRUE);\r
-\r
-    return rv;\r
-}\r
-\r
-/* obtains cs_conf_handle/cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_read_int32(khm_handle pconf, const wchar_t * pvalue, khm_int32 * buf) {\r
-    kconf_conf_space * c;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    if(!buf || !pvalue)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    do {\r
-        DWORD size;\r
-        DWORD type;\r
-        LONG hr;\r
-        HKEY hku = NULL;\r
-        HKEY hkm = NULL;\r
-\r
-        const wchar_t * value = NULL;\r
-        int free_space = 0;\r
-        khm_handle conf = NULL;\r
-\r
-        int i;\r
-\r
-        if((value = wcsrchr(pvalue, L'\\')) != NULL) {\r
-            if(KHM_FAILED(khc_open_space(\r
-                pconf, \r
-                pvalue, \r
-                KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), \r
-                &conf)))\r
-                goto _shadow;\r
-            free_space = 1;\r
-\r
-            if (value) {\r
-                value++;\r
-            } else {\r
-#ifdef DEBUG\r
-                assert(FALSE);\r
-#endif\r
-            }\r
-        } else {\r
-            value = pvalue;\r
-            conf = pconf;\r
-            free_space = 0;\r
-        }\r
-\r
-        if(!khc_is_handle(conf) || !buf)\r
-            goto _shadow;\r
-\r
-        c = khc_space_from_handle(conf);\r
-\r
-        if(khc_is_user_handle(conf))\r
-            hku = khcint_space_open_key(c, KHM_PERM_READ);\r
-\r
-        if(khc_is_machine_handle(conf))\r
-            hkm = khcint_space_open_key(c, KHM_PERM_READ | KCONF_FLAG_MACHINE);\r
-\r
-        size = sizeof(DWORD);\r
-        if(hku) {\r
-            hr = RegQueryValueEx(hku, value, NULL, &type, (LPBYTE) buf, &size);\r
-            if(hr == ERROR_SUCCESS) {\r
-                if(type != REG_DWORD) {\r
-                    rv = KHM_ERROR_TYPE_MISMATCH;\r
-                    goto _exit;\r
-                }\r
-                else {\r
-                    rv = KHM_ERROR_SUCCESS;\r
-                    goto _exit;\r
-                }\r
-            }\r
-        }\r
-\r
-        size = sizeof(DWORD);\r
-        if(hkm) {\r
-            hr = RegQueryValueEx(hkm, value, NULL, &type, (LPBYTE) buf, &size);\r
-            if(hr == ERROR_SUCCESS) {\r
-                if(type != REG_DWORD) {\r
-                    rv= KHM_ERROR_TYPE_MISMATCH;\r
-                    goto _exit;\r
-                }\r
-                else {\r
-                    rv=  KHM_ERROR_SUCCESS;\r
-                    goto _exit;\r
-                }\r
-            }\r
-        }\r
-\r
-        if(c->schema && khc_is_schema_handle(conf)) {\r
-            for(i=0;i<c->nSchema;i++) {\r
-                if(c->schema[i].type == KC_INT32 && !wcscmp(value, c->schema[i].name)) {\r
-                    *buf = (khm_int32) c->schema[i].value;\r
-                    rv = KHM_ERROR_SUCCESS;\r
-                    goto _exit;\r
-                }\r
-            }\r
-        }\r
-_shadow:\r
-        if(free_space && conf)\r
-            khc_close_space(conf);\r
-\r
-        if(khc_is_shadowed(pconf)) {\r
-            pconf = khc_shadow(pconf);\r
-            continue;\r
-        } else {\r
-            rv = KHM_ERROR_NOT_FOUND;\r
-            break;\r
-        }\r
-_exit:\r
-        if(free_space && conf)\r
-            khc_close_space(conf);\r
-        break;\r
-    }\r
-    while(TRUE);\r
-\r
-    return rv;\r
-}\r
-\r
-/* obtains cs_conf_handle/cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_read_int64(khm_handle pconf, const wchar_t * pvalue, khm_int64 * buf) {\r
-    kconf_conf_space * c;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    do {\r
-        DWORD size;\r
-        DWORD type;\r
-        LONG hr;\r
-        HKEY hku = NULL;\r
-        HKEY hkm = NULL;\r
-\r
-        const wchar_t * value = NULL;\r
-        int free_space = 0;\r
-        khm_handle conf = NULL;\r
-\r
-        int i;\r
-\r
-        if((value = wcsrchr(pvalue, L'\\')) != NULL) {\r
-            if(KHM_FAILED(khc_open_space(\r
-                pconf, \r
-                pvalue, \r
-                KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), \r
-                &conf)))\r
-                goto _shadow;\r
-            free_space = 1;\r
-\r
-            if (value) {\r
-                value++;\r
-            } else {\r
-#ifdef DEBUG\r
-                assert(FALSE);\r
-#endif\r
-            }\r
-        } else {\r
-            value = pvalue;\r
-            conf = pconf;\r
-            free_space = 0;\r
-        }\r
-\r
-        if(!khc_is_handle(conf) || !buf)\r
-            goto _shadow;\r
-\r
-        c = khc_space_from_handle(conf);\r
-\r
-        if(khc_is_user_handle(conf))\r
-            hku = khcint_space_open_key(c, KHM_PERM_READ);\r
-\r
-        if(khc_is_machine_handle(conf))\r
-            hkm = khcint_space_open_key(c, KHM_PERM_READ | KCONF_FLAG_MACHINE);\r
-\r
-        size = sizeof(khm_int64);\r
-        if(hku) {\r
-            hr = RegQueryValueEx(hku, value, NULL, &type, (LPBYTE) buf, &size);\r
-            if(hr == ERROR_SUCCESS) {\r
-                if(type != REG_QWORD) {\r
-                    rv= KHM_ERROR_TYPE_MISMATCH;\r
-                    goto _exit;\r
-                }\r
-                else {\r
-                    rv = KHM_ERROR_SUCCESS;\r
-                    goto _exit;\r
-                }\r
-            }\r
-        }\r
-\r
-        size = sizeof(khm_int64);\r
-        if(hkm) {\r
-            hr = RegQueryValueEx(hkm, value, NULL, &type, (LPBYTE) buf, &size);\r
-            if(hr == ERROR_SUCCESS) {\r
-                if(type != REG_QWORD) {\r
-                    rv = KHM_ERROR_TYPE_MISMATCH;\r
-                    goto _exit;\r
-                }\r
-                else {\r
-                    rv = KHM_ERROR_SUCCESS;\r
-                    goto _exit;\r
-                }\r
-            }\r
-        }\r
-\r
-        if(c->schema && khc_is_schema_handle(conf)) {\r
-            for(i=0;i<c->nSchema;i++) {\r
-                if(c->schema[i].type == KC_INT64 && !wcscmp(value, c->schema[i].name)) {\r
-                    *buf = (khm_int64) c->schema[i].value;\r
-                    rv = KHM_ERROR_SUCCESS;\r
-                    goto _exit;\r
-                }\r
-            }\r
-        }\r
-\r
-_shadow:\r
-        if(free_space && conf)\r
-            khc_close_space(conf);\r
-        if(khc_is_shadowed(pconf)) {\r
-            pconf = khc_shadow(pconf);\r
-            continue;\r
-        } else {\r
-            rv = KHM_ERROR_NOT_FOUND;\r
-            break;\r
-        }\r
-\r
-_exit:\r
-        if(free_space && conf)\r
-            khc_close_space(conf);\r
-        break;\r
-\r
-    } while(TRUE);\r
-    return rv;\r
-}\r
-\r
-/* obtaincs cs_conf_handle/cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_read_binary(khm_handle pconf, const wchar_t * pvalue, \r
-                void * buf, khm_size * bufsize) {\r
-    kconf_conf_space * c;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    do {\r
-        DWORD size;\r
-        DWORD type;\r
-        LONG hr;\r
-        HKEY hku = NULL;\r
-        HKEY hkm = NULL;\r
-\r
-        const wchar_t * value = NULL;\r
-        int free_space = 0;\r
-        khm_handle conf = NULL;\r
-\r
-        if((value = wcsrchr(pvalue, L'\\')) != NULL) {\r
-            if(KHM_FAILED(khc_open_space(\r
-                pconf, \r
-                pvalue, \r
-                KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), \r
-                &conf)))\r
-                goto _shadow;\r
-            free_space = 1;\r
-\r
-            if (value) {\r
-                value++;\r
-            } else {\r
-#ifdef DEBUG\r
-                assert(FALSE);\r
-#endif\r
-            }\r
-        } else {\r
-            value = pvalue;\r
-            conf = pconf;\r
-            free_space = 0;\r
-        }\r
-\r
-        if(!khc_is_handle(conf))\r
-            goto _shadow;\r
-\r
-        c = khc_space_from_handle(conf);\r
-\r
-        if(khc_is_user_handle(conf))\r
-            hku = khcint_space_open_key(c, KHM_PERM_READ);\r
-\r
-        if(khc_is_machine_handle(conf))\r
-            hkm = khcint_space_open_key(c, KHM_PERM_READ | KCONF_FLAG_MACHINE);\r
-\r
-        size = (DWORD) *bufsize;\r
-        if(hku) {\r
-            hr = RegQueryValueEx(hku, value, NULL, &type, (LPBYTE) buf, &size);\r
-            if(hr == ERROR_SUCCESS) {\r
-                if(type != REG_BINARY) {\r
-                    rv = KHM_ERROR_TYPE_MISMATCH;\r
-                    goto _exit;\r
-                }\r
-                else {\r
-                    *bufsize = size;\r
-                    rv =  KHM_ERROR_SUCCESS;\r
-                    goto _exit;\r
-                }\r
-            } else {\r
-                if(hr == ERROR_MORE_DATA) {\r
-                    *bufsize = size;\r
-                    rv = KHM_ERROR_TOO_LONG;\r
-                    goto _exit;\r
-                }\r
-            }\r
-        }\r
-\r
-        size = (DWORD) *bufsize;\r
-        if(hkm) {\r
-            hr = RegQueryValueEx(hkm, value, NULL, &type, (LPBYTE) buf, &size);\r
-            if(hr == ERROR_SUCCESS) {\r
-                if(type != REG_BINARY) {\r
-                    rv = KHM_ERROR_TYPE_MISMATCH;\r
-                    goto _exit;\r
-                }\r
-                else {\r
-                    *bufsize = size;\r
-                    rv = KHM_ERROR_SUCCESS;\r
-                    goto _exit;\r
-                }\r
-            } else {\r
-                if(hr == ERROR_MORE_DATA) {\r
-                    *bufsize = size;\r
-                    rv = KHM_ERROR_TOO_LONG;\r
-                    goto _exit;\r
-                }\r
-            }\r
-        }\r
-\r
-        /* binary values aren't supported in schema */\r
-_shadow:\r
-        if(free_space && conf)\r
-            khc_close_space(conf);\r
-        if(khc_is_shadowed(pconf)) {\r
-            pconf = khc_shadow(pconf);\r
-            continue;\r
-        } else {\r
-            rv = KHM_ERROR_NOT_FOUND;\r
-            break;\r
-        }\r
-\r
-_exit:\r
-        if(free_space && conf)\r
-            khc_close_space(conf);\r
-        break;\r
-\r
-    }while (TRUE);\r
-\r
-    return rv;\r
-}\r
-\r
-/* obtains cs_conf_handle/cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_write_string(khm_handle pconf, \r
-                 const wchar_t * pvalue, \r
-                 wchar_t * buf) \r
-{\r
-    HKEY pk = NULL;\r
-    kconf_conf_space * c;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    LONG hr;\r
-    size_t cbsize;\r
-    const wchar_t * value = NULL;\r
-    int free_space = 0;\r
-    khm_handle conf = NULL;\r
-\r
-\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    if(pconf && !khc_is_machine_handle(pconf) && !khc_is_user_handle(pconf))\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-\r
-    if(FAILED(StringCbLength(buf, KCONF_MAXCB_STRING, &cbsize))) {\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit;\r
-    }\r
-\r
-    cbsize += sizeof(wchar_t);\r
-\r
-    if (khc_handle_flags(pconf) & KCONF_FLAG_WRITEIFMOD) {\r
-        wchar_t tmpbuf[512];\r
-        wchar_t * buffer;\r
-        size_t tmpsize = cbsize;\r
-        khm_boolean is_equal = FALSE;\r
-\r
-        if (cbsize <= sizeof(tmpbuf)) {\r
-            buffer = tmpbuf;\r
-        } else {\r
-            buffer = PMALLOC(cbsize);\r
-        }\r
-\r
-        if (KHM_SUCCEEDED(khc_read_string(pconf, pvalue, buffer, &tmpsize)) &&\r
-            tmpsize == cbsize) {\r
-            if (khc_handle_flags(pconf) & KCONF_FLAG_IFMODCI)\r
-                is_equal = !_wcsicmp(buffer, buf);\r
-            else\r
-                is_equal = !wcscmp(buffer, buf);\r
-        }\r
-\r
-        if (buffer != tmpbuf)\r
-            PFREE(buffer);\r
-\r
-        if (is_equal) {\r
-            return KHM_ERROR_SUCCESS;\r
-        }\r
-    }\r
-\r
-    if((value = wcsrchr(pvalue, L'\\')) != NULL) {\r
-        if(KHM_FAILED(khc_open_space(pconf, pvalue, \r
-                                     KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), \r
-                                     &conf)))\r
-            return KHM_ERROR_INVALID_PARAM;\r
-        free_space = 1;\r
-\r
-        if (value) {\r
-            value ++;\r
-        } else {\r
-#ifdef DEBUG\r
-            assert(FALSE);\r
-#endif\r
-        }\r
-    } else {\r
-        value = pvalue;\r
-        conf = pconf;\r
-        free_space = 0;\r
-    }\r
-\r
-    if(!khc_is_handle(conf) || !buf) {\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit;\r
-    }\r
-\r
-    c = khc_space_from_handle(conf);\r
-\r
-    if(khc_is_user_handle(conf)) {\r
-        pk = khcint_space_open_key(c, KHM_PERM_WRITE | KHM_FLAG_CREATE);\r
-    } else {\r
-        pk = khcint_space_open_key(c, KHM_PERM_WRITE | KCONF_FLAG_MACHINE | KHM_FLAG_CREATE);\r
-    }\r
-\r
-    hr = RegSetValueEx(pk, value, 0, REG_SZ, (LPBYTE) buf, (DWORD) cbsize);\r
-\r
-    if(hr != ERROR_SUCCESS)\r
-        rv = KHM_ERROR_INVALID_OPERATION;\r
-\r
-_exit:\r
-    if(free_space)\r
-        khc_close_space(conf);\r
-    return rv;\r
-}\r
-\r
-/* obtaincs cs_conf_handle/cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_write_int32(khm_handle pconf, \r
-                const wchar_t * pvalue, \r
-                khm_int32 buf) \r
-{\r
-    HKEY pk = NULL;\r
-    kconf_conf_space * c;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    LONG hr;\r
-    const wchar_t * value = NULL;\r
-    int free_space = 0;\r
-    khm_handle conf = NULL;\r
-\r
-\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    if(pconf && !khc_is_machine_handle(pconf) && !khc_is_user_handle(pconf))\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-\r
-    if (khc_handle_flags(pconf) & KCONF_FLAG_WRITEIFMOD) {\r
-        khm_int32 tmpvalue;\r
-\r
-        if (KHM_SUCCEEDED(khc_read_int32(pconf, pvalue, &tmpvalue)) &&\r
-            tmpvalue == buf) {\r
-            return KHM_ERROR_SUCCESS;\r
-        }\r
-    }\r
-\r
-    if((value = wcsrchr(pvalue, L'\\')) != NULL) {\r
-        if(KHM_FAILED(khc_open_space(\r
-            pconf, \r
-            pvalue, \r
-            KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), \r
-            &conf)))\r
-            return KHM_ERROR_INVALID_PARAM;\r
-        free_space = 1;\r
-\r
-        if (value) {\r
-            value ++;\r
-        } else {\r
-#ifdef DEBUG\r
-            assert(FALSE);\r
-#endif\r
-        }\r
-    } else {\r
-        value = pvalue;\r
-        conf = pconf;\r
-        free_space = 0;\r
-    }\r
-\r
-    if(!khc_is_handle(conf))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    c = khc_space_from_handle( conf);\r
-\r
-    if(khc_is_user_handle(conf)) {\r
-        pk = khcint_space_open_key(c, KHM_PERM_WRITE | KHM_FLAG_CREATE);\r
-    } else {\r
-        pk = khcint_space_open_key(c, KHM_PERM_WRITE | KCONF_FLAG_MACHINE | KHM_FLAG_CREATE);\r
-    }\r
-\r
-    hr = RegSetValueEx(pk, value, 0, REG_DWORD, (LPBYTE) &buf, sizeof(khm_int32));\r
-\r
-    if(hr != ERROR_SUCCESS)\r
-        rv = KHM_ERROR_INVALID_OPERATION;\r
-\r
-    if(free_space)\r
-        khc_close_space(conf);\r
-\r
-    return rv;\r
-}\r
-\r
-/* obtains cs_conf_handle/cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_write_int64(khm_handle pconf, const wchar_t * pvalue, khm_int64 buf) {\r
-    HKEY pk = NULL;\r
-    kconf_conf_space * c;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    LONG hr;\r
-    const wchar_t * value = NULL;\r
-    int free_space = 0;\r
-    khm_handle conf = NULL;\r
-\r
-\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    if(pconf && !khc_is_machine_handle(pconf) && !khc_is_user_handle(pconf))\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-\r
-    if (khc_handle_flags(pconf) & KCONF_FLAG_WRITEIFMOD) {\r
-        khm_int64 tmpvalue;\r
-\r
-        if (KHM_SUCCEEDED(khc_read_int64(pconf, pvalue, &tmpvalue)) &&\r
-            tmpvalue == buf) {\r
-            return KHM_ERROR_SUCCESS;\r
-        }\r
-    }\r
-\r
-    if((value = wcsrchr(pvalue, L'\\')) != NULL) {\r
-        if(KHM_FAILED(khc_open_space(\r
-            pconf, \r
-            pvalue, \r
-            KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), \r
-            &conf)))\r
-            return KHM_ERROR_INVALID_PARAM;\r
-        free_space = 1;\r
-\r
-        if (value) {\r
-            value ++;\r
-        } else {\r
-#ifdef DEBUG\r
-            assert(FALSE);\r
-#endif\r
-        }\r
-    } else {\r
-        value = pvalue;\r
-        conf = pconf;\r
-        free_space = 0;\r
-    }\r
-\r
-    if(!khc_is_handle(conf))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    c = khc_space_from_handle( conf);\r
-\r
-    if(khc_is_user_handle(conf)) {\r
-        pk = khcint_space_open_key(c, KHM_PERM_WRITE | KHM_FLAG_CREATE);\r
-    } else {\r
-        pk = khcint_space_open_key(c, KHM_PERM_WRITE | KCONF_FLAG_MACHINE | KHM_FLAG_CREATE);\r
-    }\r
-\r
-    hr = RegSetValueEx(pk, value, 0, REG_QWORD, (LPBYTE) &buf, sizeof(khm_int64));\r
-\r
-    if(hr != ERROR_SUCCESS)\r
-        rv = KHM_ERROR_INVALID_OPERATION;\r
-\r
-    if(free_space)\r
-        khc_close_space(conf);\r
-\r
-    return rv;\r
-}\r
-\r
-/* obtains cs_conf_handle/cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_write_binary(khm_handle pconf, \r
-                 const wchar_t * pvalue, \r
-                 void * buf, khm_size bufsize) {\r
-    HKEY pk = NULL;\r
-    kconf_conf_space * c;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    LONG hr;\r
-    const wchar_t * value = NULL;\r
-    int free_space = 0;\r
-    khm_handle conf = NULL;\r
-\r
-\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    if(pconf && !khc_is_machine_handle(pconf) && !khc_is_user_handle(pconf))\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-\r
-    if((value = wcsrchr(pvalue, L'\\')) != NULL) {\r
-        if(KHM_FAILED(khc_open_space(\r
-            pconf, \r
-            pvalue, \r
-            KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), \r
-            &conf)))\r
-            return KHM_ERROR_INVALID_PARAM;\r
-        free_space = 1;\r
-\r
-        if (value) {\r
-            value ++;\r
-        } else {\r
-#ifdef DEBUG\r
-            assert(FALSE);\r
-#endif\r
-        }\r
-    } else {\r
-        value = pvalue;\r
-        conf = pconf;\r
-        free_space = 0;\r
-    }\r
-\r
-    if(!khc_is_handle(conf))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    c = khc_space_from_handle(conf);\r
-\r
-    if(khc_is_user_handle(conf)) {\r
-        pk = khcint_space_open_key(c, KHM_PERM_WRITE | KHM_FLAG_CREATE);\r
-    } else {\r
-        pk = khcint_space_open_key(c, KHM_PERM_WRITE | KCONF_FLAG_MACHINE | KHM_FLAG_CREATE);\r
-    }\r
-\r
-    hr = RegSetValueEx(pk, value, 0, REG_BINARY, buf, (DWORD) bufsize);\r
-\r
-    if(hr != ERROR_SUCCESS)\r
-        rv = KHM_ERROR_INVALID_OPERATION;\r
-\r
-    if(free_space)\r
-        khc_close_space(conf);\r
-\r
-    return rv;\r
-}\r
-\r
-/* no locks */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_get_config_space_name(khm_handle conf, \r
-                          wchar_t * buf, khm_size * bufsize) {\r
-    kconf_conf_space * c;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    if(!khc_is_handle(conf))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    c = khc_space_from_handle(conf);\r
-\r
-    if(!c->name) {\r
-        if(buf && *bufsize > 0)\r
-            buf[0] = L'\0';\r
-        else {\r
-            *bufsize = sizeof(wchar_t);\r
-            rv = KHM_ERROR_TOO_LONG;\r
-        }\r
-    } else {\r
-        size_t cbsize;\r
-\r
-        if(FAILED(StringCbLength(c->name, KCONF_MAXCB_NAME, &cbsize)))\r
-            return KHM_ERROR_UNKNOWN;\r
-\r
-        cbsize += sizeof(wchar_t);\r
-\r
-        if(!buf || cbsize > *bufsize) {\r
-            *bufsize = cbsize;\r
-            rv = KHM_ERROR_TOO_LONG;\r
-        } else {\r
-            StringCbCopy(buf, *bufsize, c->name);\r
-            *bufsize = cbsize;\r
-        }\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-/* obtains cs_conf_handle/cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_get_config_space_parent(khm_handle conf, khm_handle * parent) {\r
-    kconf_conf_space * c;\r
-\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    if(!khc_is_handle(conf))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    c = khc_space_from_handle(conf);\r
-\r
-    if(c == conf_root || c->parent == conf_root)\r
-        *parent = NULL;\r
-    else\r
-        *parent = khcint_handle_from_space(c->parent, khc_handle_flags(conf));\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* obtains cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_get_type(khm_handle conf, const wchar_t * value) {\r
-    HKEY hkm = NULL;\r
-    HKEY hku = NULL;\r
-    kconf_conf_space * c;\r
-    khm_int32 rv;\r
-    LONG hr = ERROR_SUCCESS;\r
-    DWORD type = 0;\r
-\r
-    if(!khc_is_config_running())\r
-        return KC_NONE;\r
-\r
-    if(!khc_is_handle(conf))\r
-        return KC_NONE;\r
-\r
-    c = khc_space_from_handle(conf);\r
-\r
-    if(!khc_is_machine_handle(conf))\r
-        hku = khcint_space_open_key(c, KHM_PERM_READ);\r
-    hkm = khcint_space_open_key(c, KHM_PERM_READ | KCONF_FLAG_MACHINE);\r
-\r
-    if(hku)\r
-        hr = RegQueryValueEx(hku, value, NULL, &type, NULL, NULL);\r
-    if(!hku || hr != ERROR_SUCCESS)\r
-        hr = RegQueryValueEx(hkm, value, NULL, &type, NULL, NULL);\r
-    if(((!hku && !hkm) || hr != ERROR_SUCCESS) && c->schema) {\r
-        int i;\r
-\r
-        for(i=0; i<c->nSchema; i++) {\r
-            if(!wcscmp(c->schema[i].name, value)) {\r
-                return c->schema[i].type;\r
-            }\r
-        }\r
-\r
-        return KC_NONE;\r
-    }\r
-\r
-    switch(type) {\r
-        case REG_MULTI_SZ:\r
-        case REG_SZ:\r
-            rv = KC_STRING;\r
-            break;\r
-        case REG_DWORD:\r
-            rv = KC_INT32;\r
-            break;\r
-        case REG_QWORD:\r
-            rv = KC_INT64;\r
-            break;\r
-        case REG_BINARY:\r
-            rv = KC_BINARY;\r
-            break;\r
-        default:\r
-            rv = KC_NONE;\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-/* obtains cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_value_exists(khm_handle conf, const wchar_t * value) {\r
-    HKEY hku = NULL;\r
-    HKEY hkm = NULL;\r
-    kconf_conf_space * c;\r
-    khm_int32 rv = 0;\r
-    DWORD t;\r
-    int i;\r
-\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    if(!khc_is_handle(conf))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    do {\r
-        c = khc_space_from_handle(conf);\r
-\r
-        if (khc_is_user_handle(conf))\r
-            hku = khcint_space_open_key(c, KHM_PERM_READ);\r
-        if (khc_is_machine_handle(conf))\r
-            hkm = khcint_space_open_key(c, KHM_PERM_READ | KCONF_FLAG_MACHINE);\r
-\r
-        if(hku && (RegQueryValueEx(hku, value, NULL, &t, NULL, NULL) == ERROR_SUCCESS))\r
-            rv |= KCONF_FLAG_USER;\r
-        if(hkm && (RegQueryValueEx(hkm, value, NULL, &t, NULL, NULL) == ERROR_SUCCESS))\r
-            rv |= KCONF_FLAG_MACHINE;\r
-\r
-        if(c->schema && khc_is_schema_handle(conf)) {\r
-            for(i=0; i<c->nSchema; i++) {\r
-                if(!wcscmp(c->schema[i].name, value)) {\r
-                    rv |= KCONF_FLAG_SCHEMA;\r
-                    break;\r
-                }\r
-            }\r
-        }\r
-\r
-        /* if the value is not found at this level and the handle is\r
-           shadowed, try the next level down. */\r
-        if (rv == 0 && khc_is_shadowed(conf))\r
-            conf = khc_shadow(conf);\r
-        else\r
-            break;\r
-    } while (conf);\r
-\r
-    return rv;\r
-}\r
-\r
-/* obtains cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI\r
-khc_remove_value(khm_handle conf, const wchar_t * value, khm_int32 flags) {\r
-    HKEY hku = NULL;\r
-    HKEY hkm = NULL;\r
-    kconf_conf_space * c;\r
-    khm_int32 rv = KHM_ERROR_NOT_FOUND;\r
-    DWORD t;\r
-    LONG l;\r
-\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    if(!khc_is_handle(conf))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    c = khc_space_from_handle(conf);\r
-\r
-    if(!khc_is_machine_handle(conf))\r
-        hku = khcint_space_open_key(c, KHM_PERM_READ);\r
-    hkm = khcint_space_open_key(c, KHM_PERM_READ | KCONF_FLAG_MACHINE);\r
-\r
-    if((flags == 0 ||\r
-        (flags & KCONF_FLAG_USER)) &&\r
-       hku && (RegQueryValueEx(hku, value, NULL, \r
-                               &t, NULL, NULL) == ERROR_SUCCESS)) {\r
-        l = RegDeleteValue(hku, value);\r
-        if (l == ERROR_SUCCESS)\r
-            rv = KHM_ERROR_SUCCESS;\r
-        else\r
-            rv = KHM_ERROR_UNKNOWN;\r
-    }\r
-    if((flags == 0 ||\r
-        (flags & KCONF_FLAG_MACHINE)) &&\r
-       hkm && (RegQueryValueEx(hkm, value, NULL, \r
-                               &t, NULL, NULL) == ERROR_SUCCESS)) {\r
-        l = RegDeleteValue(hkm, value);\r
-        if (l == ERROR_SUCCESS)\r
-            rv = (rv == KHM_ERROR_UNKNOWN)?KHM_ERROR_PARTIAL: \r
-                KHM_ERROR_SUCCESS;\r
-        else\r
-            rv = (rv == KHM_ERROR_SUCCESS)?KHM_ERROR_PARTIAL:\r
-                KHM_ERROR_UNKNOWN;\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-/* called with cs_conf_global held */\r
-khm_int32\r
-khcint_remove_space(kconf_conf_space * c, khm_int32 flags) {\r
-    kconf_conf_space * cc;\r
-    kconf_conf_space * cn;\r
-    kconf_conf_space * p;\r
-\r
-    /* TODO: if this is the last child space and the parent is marked\r
-       for deletion, delete the parent as well. */\r
-\r
-    p = TPARENT(c);\r
-\r
-    /* We don't allow deleting top level keys.  They are\r
-       predefined. */\r
-#ifdef DEBUG\r
-    assert(p);\r
-#endif\r
-    if (!p)\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-\r
-    cc = TFIRSTCHILD(c);\r
-    while (cc) {\r
-        cn = LNEXT(cc);\r
-\r
-        khcint_remove_space(cc, flags);\r
-\r
-        cc = cn;\r
-    }\r
-\r
-    cc = TFIRSTCHILD(c);\r
-    if (!cc && c->refcount == 0) {\r
-        TDELCHILD(p, c);\r
-        khcint_free_space(c);\r
-    } else {\r
-        c->flags |= (flags &\r
-                     (KCONF_SPACE_FLAG_DELETE_M |\r
-                      KCONF_SPACE_FLAG_DELETE_U));\r
-\r
-        /* if all the registry spaces have been marked as deleted and\r
-           there is no schema, we should mark the space as deleted as\r
-           well.  Note that ideally we only need to check for stores\r
-           which have data corresponding to this configuration space,\r
-           but this is a bit problematic since we don't monitor the\r
-           registry for changes. */\r
-        if ((c->flags &\r
-             (KCONF_SPACE_FLAG_DELETE_M |\r
-              KCONF_SPACE_FLAG_DELETE_U)) ==\r
-            (KCONF_SPACE_FLAG_DELETE_M |\r
-             KCONF_SPACE_FLAG_DELETE_U) &&\r
-            (!c->schema || c->nSchema == 0))\r
-\r
-            c->flags |= KCONF_SPACE_FLAG_DELETED;\r
-    }\r
-\r
-    if (c->regpath && p->regpath) {\r
-        HKEY hk;\r
-\r
-        if (flags & KCONF_SPACE_FLAG_DELETE_U) {\r
-            hk = khcint_space_open_key(p, KCONF_FLAG_USER);\r
-\r
-            if (hk)\r
-                khcint_RegDeleteKey(hk, c->name);\r
-        }\r
-        if (flags & KCONF_SPACE_FLAG_DELETE_M) {\r
-            hk = khcint_space_open_key(p, KCONF_FLAG_MACHINE);\r
-\r
-            if (hk)\r
-                khcint_RegDeleteKey(hk, c->name);\r
-        }\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* obtains cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI\r
-khc_remove_space(khm_handle conf) {\r
-\r
-    /*\r
-       - mark this space as well as all child spaces as\r
-         'delete-on-close' using flags.  Mark should indicate which\r
-         repository to delete the space from. (user/machine)\r
-\r
-       - When each subspace is released, check if it has been marked\r
-         for deletion.  If so, delete the marked spaces as well as\r
-         removing the space from kconf space tree.\r
-\r
-       - When removing a subspace from a space, check if the parent\r
-         space has any children left.  If there are none, check if the\r
-         parent space is also marked for deletion.\r
-    */\r
-    kconf_conf_space * c;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    khm_int32 flags = 0;\r
-\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    if(!khc_is_handle(conf))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    c = khc_space_from_handle(conf);\r
-\r
-    EnterCriticalSection(&cs_conf_global);\r
-\r
-    if (khc_is_machine_handle(conf))\r
-        flags |= KCONF_SPACE_FLAG_DELETE_M;\r
-    if (khc_is_user_handle(conf))\r
-        flags |= KCONF_SPACE_FLAG_DELETE_U;\r
-\r
-    rv = khcint_remove_space(c, flags);\r
-\r
-    LeaveCriticalSection(&cs_conf_global);\r
-\r
-    return rv;\r
-}\r
-\r
-/* no locks */\r
-khm_boolean \r
-khcint_is_valid_name(wchar_t * name)\r
-{\r
-    size_t cbsize;\r
-    if(FAILED(StringCbLength(name, KCONF_MAXCB_NAME, &cbsize)))\r
-        return FALSE;\r
-    return TRUE;\r
-}\r
-\r
-/* no locks */\r
-khm_int32 \r
-khcint_validate_schema(const kconf_schema * schema,\r
-                       int begin,\r
-                       int *end)\r
-{\r
-    int i;\r
-    int state = 0;\r
-    int end_found = 0;\r
-\r
-    i=begin;\r
-    while(!end_found) {\r
-        switch(state) {\r
-            case 0: /* initial.  this record should start a config space */\r
-                if(!khcint_is_valid_name(schema[i].name) ||\r
-                    schema[i].type != KC_SPACE)\r
-                    return KHM_ERROR_INVALID_PARAM;\r
-                state = 1;\r
-                break;\r
-\r
-            case 1: /* we are inside a config space, in the values area */\r
-                if(!khcint_is_valid_name(schema[i].name))\r
-                    return KHM_ERROR_INVALID_PARAM;\r
-                if(schema[i].type == KC_SPACE) {\r
-                    if(KHM_FAILED(khcint_validate_schema(schema, i, &i)))\r
-                        return KHM_ERROR_INVALID_PARAM;\r
-                    state = 2;\r
-                } else if(schema[i].type == KC_ENDSPACE) {\r
-                    end_found = 1;\r
-                    if(end)\r
-                        *end = i;\r
-                } else {\r
-                    if(schema[i].type != KC_STRING &&\r
-                        schema[i].type != KC_INT32 &&\r
-                        schema[i].type != KC_INT64 &&\r
-                        schema[i].type != KC_BINARY)\r
-                        return KHM_ERROR_INVALID_PARAM;\r
-                }\r
-                break;\r
-\r
-            case 2: /* we are inside a config space, in the subspace area */\r
-                if(schema[i].type == KC_SPACE) {\r
-                    if(KHM_FAILED(khcint_validate_schema(schema, i, &i)))\r
-                        return KHM_ERROR_INVALID_PARAM;\r
-                } else if(schema[i].type == KC_ENDSPACE) {\r
-                    end_found = 1;\r
-                    if(end)\r
-                        *end = i;\r
-                } else {\r
-                    return KHM_ERROR_INVALID_PARAM;\r
-                }\r
-                break;\r
-\r
-            default:\r
-                /* unreachable */\r
-                return KHM_ERROR_INVALID_PARAM;\r
-        }\r
-        i++;\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* obtains cs_conf_handle/cs_conf_global; called with cs_conf_global */\r
-khm_int32 \r
-khcint_load_schema_i(khm_handle parent, const kconf_schema * schema, \r
-                     int begin, int * end)\r
-{\r
-    int i;\r
-    int state = 0;\r
-    int end_found = 0;\r
-    kconf_conf_space * thisconf = NULL;\r
-    khm_handle h = NULL;\r
-\r
-    i=begin;\r
-    while(!end_found) {\r
-        switch(state) {\r
-            case 0: /* initial.  this record should start a config space */\r
-                LeaveCriticalSection(&cs_conf_global);\r
-                if(KHM_FAILED(khc_open_space(parent, schema[i].name, \r
-                                             KHM_FLAG_CREATE, &h))) {\r
-                    EnterCriticalSection(&cs_conf_global);\r
-                    return KHM_ERROR_INVALID_PARAM;\r
-                }\r
-                EnterCriticalSection(&cs_conf_global);\r
-                thisconf = khc_space_from_handle(h);\r
-                thisconf->schema = schema + (begin + 1);\r
-                thisconf->nSchema = 0;\r
-                state = 1;\r
-                break;\r
-\r
-            case 1: /* we are inside a config space, in the values area */\r
-                if(schema[i].type == KC_SPACE) {\r
-                    thisconf->nSchema = i - (begin + 1);\r
-                    if(KHM_FAILED(khcint_load_schema_i(h, schema, i, &i)))\r
-                        return KHM_ERROR_INVALID_PARAM;\r
-                    state = 2;\r
-                } else if(schema[i].type == KC_ENDSPACE) {\r
-                    thisconf->nSchema = i - (begin + 1);\r
-                    end_found = 1;\r
-                    if(end)\r
-                        *end = i;\r
-                    LeaveCriticalSection(&cs_conf_global);\r
-                    khc_close_space(h);\r
-                    EnterCriticalSection(&cs_conf_global);\r
-                }\r
-                break;\r
-\r
-            case 2: /* we are inside a config space, in the subspace area */\r
-                if(schema[i].type == KC_SPACE) {\r
-                    if(KHM_FAILED(khcint_load_schema_i(h, schema, i, &i)))\r
-                        return KHM_ERROR_INVALID_PARAM;\r
-                } else if(schema[i].type == KC_ENDSPACE) {\r
-                    end_found = 1;\r
-                    if(end)\r
-                        *end = i;\r
-                    LeaveCriticalSection(&cs_conf_global);\r
-                    khc_close_space(h);\r
-                    EnterCriticalSection(&cs_conf_global);\r
-                } else {\r
-                    return KHM_ERROR_INVALID_PARAM;\r
-                }\r
-                break;\r
-\r
-            default:\r
-                /* unreachable */\r
-                return KHM_ERROR_INVALID_PARAM;\r
-        }\r
-        i++;\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* obtains cs_conf_handle/cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_load_schema(khm_handle conf, const kconf_schema * schema)\r
-{\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    if(conf && !khc_is_handle(conf))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(KHM_FAILED(khcint_validate_schema(schema, 0, NULL)))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_conf_global);\r
-    rv = khcint_load_schema_i(conf, schema, 0, NULL);        \r
-    LeaveCriticalSection(&cs_conf_global);\r
-\r
-    return rv;\r
-}\r
-\r
-/* obtains cs_conf_handle/cs_conf_global; called with cs_conf_global */\r
-khm_int32 \r
-khcint_unload_schema_i(khm_handle parent, const kconf_schema * schema, \r
-                       int begin, int * end)\r
-{\r
-    int i;\r
-    int state = 0;\r
-    int end_found = 0;\r
-    kconf_conf_space * thisconf = NULL;\r
-    khm_handle h = NULL;\r
-\r
-    i=begin;\r
-    while(!end_found) {\r
-        switch(state) {\r
-            case 0: /* initial.  this record should start a config space */\r
-                LeaveCriticalSection(&cs_conf_global);\r
-                if(KHM_FAILED(khc_open_space(parent, schema[i].name, 0, &h))) {\r
-                    EnterCriticalSection(&cs_conf_global);\r
-                    return KHM_ERROR_INVALID_PARAM;\r
-                }\r
-                EnterCriticalSection(&cs_conf_global);\r
-                thisconf = khc_space_from_handle(h);\r
-                if(thisconf->schema == (schema + (begin + 1))) {\r
-                    thisconf->schema = NULL;\r
-                    thisconf->nSchema = 0;\r
-                }\r
-                state = 1;\r
-                break;\r
-\r
-            case 1: /* we are inside a config space, in the values area */\r
-                if(schema[i].type == KC_SPACE) {\r
-                    if(KHM_FAILED(khcint_unload_schema_i(h, schema, i, &i)))\r
-                        return KHM_ERROR_INVALID_PARAM;\r
-                    state = 2;\r
-                } else if(schema[i].type == KC_ENDSPACE) {\r
-                    end_found = 1;\r
-                    if(end)\r
-                        *end = i;\r
-                    LeaveCriticalSection(&cs_conf_global);\r
-                    khc_close_space(h);\r
-                    EnterCriticalSection(&cs_conf_global);\r
-                }\r
-                break;\r
-\r
-            case 2: /* we are inside a config space, in the subspace area */\r
-                if(schema[i].type == KC_SPACE) {\r
-                    if(KHM_FAILED(khcint_unload_schema_i(h, schema, i, &i)))\r
-                        return KHM_ERROR_INVALID_PARAM;\r
-                } else if(schema[i].type == KC_ENDSPACE) {\r
-                    end_found = 1;\r
-                    if(end)\r
-                        *end = i;\r
-                    LeaveCriticalSection(&cs_conf_global);\r
-                    khc_close_space(h);\r
-                    EnterCriticalSection(&cs_conf_global);\r
-                } else {\r
-                    return KHM_ERROR_INVALID_PARAM;\r
-                }\r
-                break;\r
-\r
-            default:\r
-                /* unreachable */\r
-                return KHM_ERROR_INVALID_PARAM;\r
-        }\r
-        i++;\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* obtains cs_conf_handle/cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_unload_schema(khm_handle conf, const kconf_schema * schema)\r
-{\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    if(conf && !khc_is_handle(conf))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(KHM_FAILED(khcint_validate_schema(schema, 0, NULL)))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_conf_global);\r
-    rv = khcint_unload_schema_i(conf, schema, 0, NULL);\r
-    LeaveCriticalSection(&cs_conf_global);\r
-\r
-    return rv;\r
-}\r
-\r
-/* obtaincs cs_conf_handle/cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_enum_subspaces(khm_handle conf,\r
-                   khm_handle prev,\r
-                   khm_handle * next)\r
-{\r
-    kconf_conf_space * s;\r
-    kconf_conf_space * c;\r
-    kconf_conf_space * p;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    if(!khc_is_handle(conf) || next == NULL ||\r
-        (prev != NULL && !khc_is_handle(prev)))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    s = khc_space_from_handle(conf);\r
-\r
-    if(prev == NULL) {\r
-        /* first off, we enumerate all the registry spaces regardless of\r
-        whether the handle is applicable for some registry space or not.\r
-        See notes for khc_begin_enum_subspaces() for reasons as to why\r
-        this is done (notes are in kconfig.h)*/\r
-\r
-        /* go through the user hive first */\r
-        {\r
-            HKEY hk_conf;\r
-\r
-            hk_conf = khcint_space_open_key(s, 0);\r
-            if(hk_conf) {\r
-                wchar_t name[KCONF_MAXCCH_NAME];\r
-                khm_handle h;\r
-                int idx;\r
-\r
-                idx = 0;\r
-                while(RegEnumKey(hk_conf, idx, \r
-                                 name, ARRAYLENGTH(name)) == ERROR_SUCCESS) {\r
-                    wchar_t * tilde;\r
-                    tilde = wcschr(name, L'~');\r
-                    if (tilde)\r
-                        *tilde = 0;\r
-                    if(KHM_SUCCEEDED(khc_open_space(conf, name, 0, &h)))\r
-                        khc_close_space(h);\r
-                    idx++;\r
-                }\r
-            }\r
-        }\r
-\r
-        /* go through the machine hive next */\r
-        {\r
-            HKEY hk_conf;\r
-\r
-            hk_conf = khcint_space_open_key(s, KCONF_FLAG_MACHINE);\r
-            if(hk_conf) {\r
-                wchar_t name[KCONF_MAXCCH_NAME];\r
-                khm_handle h;\r
-                int idx;\r
-\r
-                idx = 0;\r
-                while(RegEnumKey(hk_conf, idx, \r
-                                 name, ARRAYLENGTH(name)) == ERROR_SUCCESS) {\r
-                    wchar_t * tilde;\r
-                    tilde = wcschr(name, L'~');\r
-                    if (tilde)\r
-                        *tilde = 0;\r
-\r
-                    if(KHM_SUCCEEDED(khc_open_space(conf, name, \r
-                                                    KCONF_FLAG_MACHINE, &h)))\r
-                        khc_close_space(h);\r
-                    idx++;\r
-                }\r
-            }\r
-        }\r
-\r
-        /* don't need to go through schema, because that was already\r
-        done when the schema was loaded. */\r
-    }\r
-\r
-    /* at last we are now ready to return the results */\r
-    EnterCriticalSection(&cs_conf_global);\r
-    if(prev == NULL) {\r
-        c = TFIRSTCHILD(s);\r
-        rv = KHM_ERROR_SUCCESS;\r
-    } else {\r
-        p = khc_space_from_handle(prev);\r
-        if(TPARENT(p) == s)\r
-            c = LNEXT(p);\r
-        else\r
-            c = NULL;\r
-    }\r
-    LeaveCriticalSection(&cs_conf_global);\r
-\r
-    if(prev != NULL)\r
-        khc_close_space(prev);\r
-\r
-    if(c) {\r
-        *next = khcint_handle_from_space(c, khc_handle_flags(conf));\r
-        rv = KHM_ERROR_SUCCESS;\r
-    } else {\r
-        *next = NULL;\r
-        rv = KHM_ERROR_NOT_FOUND;\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-/* obtains cs_conf_handle/cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_write_multi_string(khm_handle conf, const wchar_t * value, wchar_t * buf)\r
-{\r
-    size_t cb;\r
-    wchar_t vbuf[KCONF_MAXCCH_STRING];\r
-    wchar_t *tb;\r
-    khm_int32 rv;\r
-\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-    if(!khc_is_handle(conf) || buf == NULL || value == NULL)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(multi_string_to_csv(NULL, &cb, buf) != KHM_ERROR_TOO_LONG)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if (cb < sizeof(vbuf))\r
-        tb = vbuf;\r
-    else\r
-        tb = PMALLOC(cb);\r
-\r
-    assert(tb != NULL);\r
-\r
-    multi_string_to_csv(tb, &cb, buf);\r
-    rv = khc_write_string(conf, value, tb);\r
-\r
-    if (tb != vbuf)\r
-        PFREE(tb);\r
-    return rv;\r
-}\r
-\r
-/* obtains cs_conf_handle/cs_conf_global */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_read_multi_string(khm_handle conf, const wchar_t * value, \r
-                      wchar_t * buf, khm_size * bufsize)\r
-{\r
-    wchar_t vbuf[KCONF_MAXCCH_STRING];\r
-    wchar_t * tb;\r
-    khm_size cbbuf;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if(!khc_is_config_running())\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    if(!bufsize)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    rv = khc_read_string(conf, value, NULL, &cbbuf);\r
-    if(rv != KHM_ERROR_TOO_LONG)\r
-        return rv;\r
-\r
-    if (cbbuf < sizeof(vbuf))\r
-        tb = vbuf;\r
-    else\r
-        tb = PMALLOC(cbbuf);\r
-\r
-    assert(tb != NULL);\r
-\r
-    rv = khc_read_string(conf, value, tb, &cbbuf);\r
-\r
-    if(KHM_FAILED(rv))\r
-        goto _exit;\r
-\r
-    rv = csv_to_multi_string(buf, bufsize, tb);\r
-\r
-_exit:\r
-    if (tb != vbuf)\r
-        PFREE(tb);\r
-\r
-    return rv;\r
-}\r
+/*
+* Copyright (c) 2005 Massachusetts Institute of Technology
+*
+* Permission is hereby granted, free of charge, to any person
+* obtaining a copy of this software and associated documentation
+* files (the "Software"), to deal in the Software without
+* restriction, including without limitation the rights to use, copy,
+* modify, merge, publish, distribute, sublicense, and/or sell copies
+* of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+* ACTION OF 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<shlwapi.h>
+#include<kconfiginternal.h>
+#include<netidmgr_intver.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 = khcint_create_empty_space();
+        conf_root->name = PWCSDUP(L"Root");
+        conf_root->regpath = PWCSDUP(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;
+
+        khcint_free_space(conf_root);
+
+        LeaveCriticalSection(&cs_conf_global);
+        DeleteCriticalSection(&cs_conf_global);
+
+        EnterCriticalSection(&cs_conf_handle);
+        while(conf_free_handles) {
+            LPOP(&conf_free_handles, &h);
+            if(h) {
+                PFREE(h);
+            }
+        }
+
+        while(conf_handles) {
+            LPOP(&conf_handles, &h);
+            if(h) {
+                PFREE(h);
+            }
+        }
+        LeaveCriticalSection(&cs_conf_handle);
+        DeleteCriticalSection(&cs_conf_handle);
+    }
+}
+
+#if defined(DEBUG) && (defined(KH_BUILD_PRIVATE) || defined(KH_BUILD_SPECIAL))
+
+#include<stdio.h>
+
+static void
+khcint_dump_space(FILE * f, kconf_conf_space * sp) {
+
+    kconf_conf_space * sc;
+
+    fprintf(f, "c12\t[%S]\t[%S]\t%d\t0x%x\tWin(%s|%s)|%s\n",
+            ((sp->regpath) ? sp->regpath : L"!No Reg path"),
+            sp->name,
+            (int) sp->refcount,
+            (int) sp->flags,
+            ((sp->regkey_user)? "HKCU" : ""),
+            ((sp->regkey_machine)? "HKLM" : ""),
+            ((sp->schema)? "Schema" : ""));
+
+
+    sc = TFIRSTCHILD(sp);
+    while(sc) {
+
+        khcint_dump_space(f, sc);
+
+        sc = LNEXT(sc);
+    }
+}
+
+KHMEXP void KHMAPI
+khcint_dump_handles(FILE * f) {
+    if (khc_is_config_running()) {
+        kconf_handle * h, * sh;
+
+        EnterCriticalSection(&cs_conf_handle);
+        EnterCriticalSection(&cs_conf_global);
+
+        fprintf(f, "c00\t*** Active handles ***\n");
+        fprintf(f, "c01\tHandle\tName\tFlags\tRegpath\n");
+
+        h = conf_handles;
+        while(h) {
+            kconf_conf_space * sp;
+
+            sp = h->space;
+
+            if (!khc_is_handle(h) || sp == NULL) {
+
+                fprintf(f, "c02\t!!INVALID HANDLE!!\n");
+
+            } else {
+
+                fprintf(f, "c02\t0x%p\t[%S]\t0x%x\t[%S]\n",
+                        h,
+                        sp->name,
+                        h->flags,
+                        sp->regpath);
+
+                sh = khc_shadow(h);
+
+                while(sh) {
+
+                    sp = sh->space;
+
+                    if (!khc_is_handle(sh) || sp == NULL) {
+
+                        fprintf(f, "c02\t0x%p:Shadow:0x%p\t[!!INVALID HANDLE!!]\n",
+                                h, sh);
+
+                    } else {
+
+                        fprintf(f, "c02\t0x%p:Shadow:0x%p,[%S]\t0x%x\t[%S]\n",
+                                h, sh,
+                                sp->name,
+                                sh->flags,
+                                sp->regpath);
+
+                    }
+
+                    sh = khc_shadow(sh);
+                }
+
+            }
+
+            h = LNEXT(h);
+        }
+
+        fprintf(f, "c03\t------  End ---------\n");
+
+        fprintf(f, "c10\t*** Active Configuration Spaces ***\n");
+        fprintf(f, "c11\tReg path\tName\tRefcount\tFlags\tLayers\n");
+
+        khcint_dump_space(f, conf_root);
+
+        fprintf(f, "c13\t------  End ---------\n");
+
+        LeaveCriticalSection(&cs_conf_global);
+        LeaveCriticalSection(&cs_conf_handle);
+
+    } else {
+        fprintf(f, "c00\t------- KHC Configuration not running -------\n");
+    }
+}
+
+#endif
+
+/* obtains cs_conf_handle/cs_conf_global */
+kconf_handle * 
+khcint_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 = PMALLOC(sizeof(kconf_handle));
+        assert(h != NULL);
+    }
+    ZeroMemory((void *) h, sizeof(kconf_handle));
+
+    h->magic = KCONF_HANDLE_MAGIC;
+    khcint_space_hold(s);
+    h->space = s;
+    h->flags = flags;
+
+    LPUSH(&conf_handles, h);
+    LeaveCriticalSection(&cs_conf_handle);
+
+    return h;
+}
+
+/* obtains cs_conf_handle/cs_conf_global */
+void 
+khcint_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();
+
+            /* hmm.  the handle was not in the in-use list */
+            LeaveCriticalSection(&cs_conf_handle);
+            return;
+        }
+    }
+#endif
+    while(h) {
+        LDELETE(&conf_handles, h);
+        if(h->space) {
+            khcint_space_release(h->space);
+            h->space = NULL;
+        }
+        lower = h->lower;
+        h->magic = 0;
+        LPUSH(&conf_free_handles, h);
+        h = lower;
+    }
+    LeaveCriticalSection(&cs_conf_handle);
+}
+
+/* obains cs_conf_handle/cs_conf_global */
+kconf_handle * 
+khcint_handle_dup(kconf_handle * o)
+{
+    kconf_handle * h;
+    kconf_handle * r;
+
+    r = khcint_handle_from_space(o->space, o->flags);
+    h = r;
+
+    while(o->lower) {
+        h->lower = khcint_handle_from_space(o->lower->space, o->lower->flags);
+
+        o = o->lower;
+        h = h->lower;
+    }
+
+    return r;
+}
+
+/* obtains cs_conf_global */
+void 
+khcint_space_hold(kconf_conf_space * s) {
+    EnterCriticalSection(&cs_conf_global);
+    s->refcount ++;
+    LeaveCriticalSection(&cs_conf_global);
+}
+
+/* called with cs_conf_global */
+void
+khcint_try_free_space(kconf_conf_space * s) {
+
+    if (TFIRSTCHILD(s) == NULL &&
+        s->refcount == 0 &&
+        s->schema == NULL) {
+
+        kconf_conf_space * p;
+
+        p = TPARENT(s);
+
+        if (p == NULL)
+            return;
+
+        TDELCHILD(p, s);
+
+        khcint_free_space(s);
+
+        khcint_try_free_space(p);
+    }
+}
+
+/* obtains cs_conf_global */
+void 
+khcint_space_release(kconf_conf_space * s) {
+    khm_int32 l;
+
+    EnterCriticalSection(&cs_conf_global);
+
+    l = -- s->refcount;
+    if (l == 0) {
+        if(s->regkey_machine)
+            RegCloseKey(s->regkey_machine);
+        if(s->regkey_user)
+            RegCloseKey(s->regkey_user);
+        s->regkey_machine = NULL;
+        s->regkey_user = NULL;
+
+        if (s->flags &
+            (KCONF_SPACE_FLAG_DELETE_M |
+             KCONF_SPACE_FLAG_DELETE_U)) {
+            khcint_remove_space(s, s->flags);
+        } else {
+#ifdef USE_TRY_FREE
+            /* even if the refcount is zero, we shouldn't free a
+               configuration space just yet since that doesn't play
+               well with the configuration space enumeration mechanism
+               which expects the spaces to dangle around if there is a
+               corresponding registry key or schema. */
+            khcint_try_free_space(s);
+#endif
+        }
+    }
+
+    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 = NULL;
+    const wchar_t * t;
+    LONG rv = ERROR_SUCCESS;
+
+    hkp = hkey;
+    t = sSubKey;
+
+    /* check for case insensitive prefix first */
+    if (!_wcsnicmp(sSubKey, CONFIG_REGPATHW, ARRAYLENGTH(CONFIG_REGPATHW) - 1)) {
+        HKEY hkt;
+
+        t = sSubKey + (ARRAYLENGTH(CONFIG_REGPATHW) - 1);
+
+#ifdef DEBUG
+        assert(*t == L'\0' || *t == L'\\');
+#endif
+
+        rv = RegOpenKeyEx(hkp,
+                          CONFIG_REGPATHW,
+                          ulOptions,
+                          samDesired,
+                          &hkt);
+
+        if (rv != ERROR_SUCCESS)
+            return rv;
+
+        if (*t == L'\0') {
+            *phkResult = hkt;
+            return rv;
+        }
+
+        t++;
+        hkp = hkt;
+    }
+
+    /* descend down the components of the subkey */
+    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;
+}
+
+/*! \internal
+
+ \note This function is not a good replacement for RegDeleteKey since
+     it deletes all the subkeys in addition to the key being deleted.
+ */
+LONG
+khcint_RegDeleteKey(HKEY hKey,
+                    LPCWSTR lpSubKey) {
+    int i;
+    wchar_t sk_name[KCONF_MAXCCH_NAME];
+    FILETIME ft;
+    size_t cch;
+    LONG rv = ERROR_SUCCESS;
+
+    /* go through and find the case sensitive match for the key */
+
+    if (FAILED(StringCchLength(lpSubKey, KCONF_MAXCCH_NAME, &cch)))
+        return ERROR_BADKEY;
+
+    for (i=0; ;i++) {
+        LONG l;
+        DWORD dw;
+
+        dw = ARRAYLENGTH(sk_name);
+        l = RegEnumKeyEx(hKey, i, sk_name, &dw,
+                         NULL, NULL, NULL, &ft);
+
+        if (l != ERROR_SUCCESS) {
+            rv = ERROR_BADKEY;
+            goto _cleanup;
+        }
+
+        if (!(wcsncmp(sk_name, lpSubKey, cch))) {
+            /* bingo! ?? */
+            if ((sk_name[cch] == L'\0' ||
+                 sk_name[cch] == L'~')) {
+
+                /* instead of calling RegDeleteKey we call SHDeleteKey
+                   because we want to blow off all the subkeys as
+                   well.  This is different from the behavior of
+                   RegDeleteKey making khcint_RegDeleteKey not a very
+                   good case sensitive replacement for
+                   RegDeleteKey. */
+
+                rv = SHDeleteKey(hKey, sk_name);
+                goto _cleanup;
+            }
+        }
+    }
+
+ _cleanup:
+    return rv;
+}
+
+LONG
+khcint_RegCreateKeyEx(HKEY hKey,
+                      LPCWSTR lpSubKey,
+                      DWORD Reserved,
+                      LPWSTR 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;
+
+    /* check for case insensitive prefix first */
+    if (!_wcsnicmp(lpSubKey, CONFIG_REGPATHW, ARRAYLENGTH(CONFIG_REGPATHW) - 1)) {
+        HKEY hkt;
+
+        t = lpSubKey + (ARRAYLENGTH(CONFIG_REGPATHW) - 1);
+
+#ifdef DEBUG
+        assert(*t == L'\0' || *t == L'\\');
+#endif
+
+        rv = RegCreateKeyEx(hkp,
+                            CONFIG_REGPATHW,
+                            Reserved,
+                            lpClass,
+                            dwOptions,
+                            samDesired,
+                            lpSecurityAttributes,
+                            &hkt,
+                            lpdwDisposition);
+
+        if (rv != ERROR_SUCCESS)
+            return rv;
+
+        if (*t == L'\0') {
+            *phkResult = hkt;
+            return rv;
+        }
+
+        t++;
+        hkp = hkt;
+    }
+
+    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;
+}
+
+/* obtains cs_conf_global */
+HKEY 
+khcint_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;
+    }
+}
+
+/* obtains cs_conf_handle/cs_conf_global */
+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)) {
+#ifdef DEBUG
+        DebugBreak();
+#endif
+        return KHM_ERROR_INVALID_PARAM;
+    }
+
+    h = (kconf_handle *) upper;
+
+    EnterCriticalSection(&cs_conf_handle);
+    if(h->lower) {
+        EnterCriticalSection(&cs_conf_global);
+        khcint_handle_free(h->lower);
+        LeaveCriticalSection(&cs_conf_global);
+        h->lower = NULL;
+    }
+
+    if(khc_is_handle(lower)) {
+        kconf_handle * l;
+        kconf_handle * lc;
+
+        l = (kconf_handle *) lower;
+        lc = khcint_handle_dup(l);
+        h->lower = lc;
+    }
+    LeaveCriticalSection(&cs_conf_handle);
+
+    return KHM_ERROR_SUCCESS;
+}
+
+/* no locks */
+kconf_conf_space * 
+khcint_create_empty_space(void) {
+    kconf_conf_space * r;
+
+    r = PMALLOC(sizeof(kconf_conf_space));
+    assert(r != NULL);
+    ZeroMemory(r,sizeof(kconf_conf_space));
+
+    return r;
+}
+
+/* called with cs_conf_global */
+void 
+khcint_free_space(kconf_conf_space * r) {
+    kconf_conf_space * c;
+
+    if(!r)
+        return;
+
+    TPOPCHILD(r, &c);
+    while(c) {
+        khcint_free_space(c);
+        TPOPCHILD(r, &c);
+    }
+
+    if(r->name)
+        PFREE(r->name);
+
+    if(r->regpath)
+        PFREE(r->regpath);
+
+    if(r->regkey_machine)
+        RegCloseKey(r->regkey_machine);
+
+    if(r->regkey_user)
+        RegCloseKey(r->regkey_user);
+
+    PFREE(r);
+}
+
+/* obtains cs_conf_global */
+khm_int32 
+khcint_open_space(kconf_conf_space * parent, 
+                  const 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];
+    size_t cb_regpath = 0;
+
+    if(!parent)
+        p = conf_root;
+    else
+        p = parent;
+
+    if(n_sname >= KCONF_MAXCCH_NAME || n_sname <= 0)
+        return KHM_ERROR_INVALID_PARAM;
+
+    StringCchCopyN(buf, ARRAYLENGTH(buf), sname, n_sname);
+
+    /* 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) {
+
+        if (c->flags & KCONF_SPACE_FLAG_DELETED) {
+            if (flags & KHM_FLAG_CREATE) {
+                c->flags &= ~(KCONF_SPACE_FLAG_DELETED |
+                              KCONF_SPACE_FLAG_DELETE_M |
+                              KCONF_SPACE_FLAG_DELETE_U);
+            } else {
+                *result = NULL;
+                return KHM_ERROR_NOT_FOUND;
+            }
+        }
+
+        khcint_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 = khcint_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 = khcint_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 = khcint_create_empty_space();
+    
+    /*SAFE: buf: is of known length < KCONF_MAXCCH_NAME */
+    c->name = PWCSDUP(buf);
+
+    /*SAFE: p->regpath: is valid since it was set using this same
+      function. */
+    /*SAFE: buf: see above */
+    cb_regpath = (wcslen(p->regpath) + wcslen(buf) + 2) * sizeof(wchar_t);
+    c->regpath = PMALLOC(cb_regpath);
+
+    assert(c->regpath != NULL);
+
+    /*SAFE: c->regpath: allocated above to be big enough */
+    /*SAFE: p->regpath: see above */
+    StringCbCopy(c->regpath, cb_regpath, p->regpath);
+    StringCbCat(c->regpath, cb_regpath, L"\\");
+
+    /*SAFE: buf: see above */
+    StringCbCat(c->regpath, cb_regpath, buf);
+
+    khcint_space_hold(c);
+
+    EnterCriticalSection(&cs_conf_global);
+    TADDCHILD(p,c);
+    LeaveCriticalSection(&cs_conf_global);
+
+    *result = c;
+    return KHM_ERROR_SUCCESS;
+}
+
+/* obtains cs_conf_handle/cs_conf_global */
+KHMEXP khm_int32 KHMAPI 
+khc_open_space(khm_handle parent, const wchar_t * cspace, khm_int32 flags, 
+               khm_handle * result) {
+    kconf_handle * h;
+    kconf_conf_space * p;
+    kconf_conf_space * c = NULL;
+    size_t cbsize;
+    const 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))) {
+#ifdef DEBUG
+        DebugBreak();
+#endif
+        return KHM_ERROR_INVALID_PARAM;
+    }
+
+    if(!parent)
+        p = conf_root;
+    else {
+        h = (kconf_handle *) parent;
+        p = khc_space_from_handle(parent);
+    }
+
+    khcint_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) {
+        *result = (khm_handle) khcint_handle_from_space(p, flags);
+        khcint_space_release(p);
+        return KHM_ERROR_SUCCESS;
+    }
+
+    if(FAILED(StringCbLength(cspace, KCONF_MAXCB_PATH, &cbsize))) {
+        khcint_space_release(p);
+        *result = NULL;
+        return KHM_ERROR_INVALID_PARAM;
+    }
+
+    str = cspace;
+    while(TRUE) {
+        const wchar_t * end = NULL;
+
+        if (!(flags & KCONF_FLAG_NOPARSENAME)) {
+
+            end = wcschr(str, L'\\'); /* safe because cspace was
+                                     validated above */
+        }
+
+        if(!end) {
+            if(flags & KCONF_FLAG_TRAILINGVALUE) {
+                /* we are at the value component */
+                c = p;
+                khcint_space_hold(c);
+                break;
+            } else
+                end = str + wcslen(str);  /* safe because cspace was
+                                             validated above */
+        }
+
+        rv = khcint_open_space(p, str, end - str, flags, &c);
+
+        if(KHM_SUCCEEDED(rv) && (*end == L'\\')) {
+            khcint_space_release(p);
+            p = c;
+            c = NULL;
+            str = end+1;
+        }
+        else
+            break;
+    }
+
+    khcint_space_release(p);
+    if(KHM_SUCCEEDED(rv)) {
+        *result = khcint_handle_from_space(c, flags);
+    } else
+        *result = NULL;
+
+    if (c)
+        khcint_space_release(c);
+
+    return rv;
+}
+
+/* obtains cs_conf_handle/cs_conf_global */
+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)) {
+#ifdef DEBUG
+        DebugBreak();
+#endif
+        return KHM_ERROR_INVALID_PARAM;
+    }
+
+    khcint_handle_free((kconf_handle *) csp);
+    return KHM_ERROR_SUCCESS;
+}
+
+/* obtains cs_conf_handle/cs_conf_global */
+KHMEXP khm_int32 KHMAPI 
+khc_read_string(khm_handle pconf, 
+                const 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;
+        const wchar_t * value = NULL;
+        int free_space = 0;
+        khm_handle conf = NULL;
+        DWORD size;
+        DWORD type;
+        LONG hr;
+
+        int i;
+
+        if((value = wcsrchr(pvalue, L'\\')) != NULL) {
+
+            if(KHM_FAILED(khc_open_space(
+                pconf, 
+                pvalue, 
+                KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), 
+                &conf)))
+                goto _shadow;
+
+            free_space = 1;
+
+            if (value) {
+                value++;
+            } else {
+#ifdef DEBUG
+                assert(FALSE);
+#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 = khcint_space_open_key(c, KHM_PERM_READ);
+
+        if(khc_is_machine_handle(conf))
+            hkm = khcint_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;
+}
+
+/* obtains cs_conf_handle/cs_conf_global */
+KHMEXP khm_int32 KHMAPI 
+khc_read_int32(khm_handle pconf, const 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_PARAM;
+
+    do {
+        DWORD size;
+        DWORD type;
+        LONG hr;
+        HKEY hku = NULL;
+        HKEY hkm = NULL;
+
+        const wchar_t * value = NULL;
+        int free_space = 0;
+        khm_handle conf = NULL;
+
+        int i;
+
+        if((value = wcsrchr(pvalue, L'\\')) != NULL) {
+            if(KHM_FAILED(khc_open_space(
+                pconf, 
+                pvalue, 
+                KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), 
+                &conf)))
+                goto _shadow;
+            free_space = 1;
+
+            if (value) {
+                value++;
+            } else {
+#ifdef DEBUG
+                assert(FALSE);
+#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 = khcint_space_open_key(c, KHM_PERM_READ);
+
+        if(khc_is_machine_handle(conf))
+            hkm = khcint_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;
+}
+
+/* obtains cs_conf_handle/cs_conf_global */
+KHMEXP khm_int32 KHMAPI 
+khc_read_int64(khm_handle pconf, const 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;
+
+        const wchar_t * value = NULL;
+        int free_space = 0;
+        khm_handle conf = NULL;
+
+        int i;
+
+        if((value = wcsrchr(pvalue, L'\\')) != NULL) {
+            if(KHM_FAILED(khc_open_space(
+                pconf, 
+                pvalue, 
+                KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), 
+                &conf)))
+                goto _shadow;
+            free_space = 1;
+
+            if (value) {
+                value++;
+            } else {
+#ifdef DEBUG
+                assert(FALSE);
+#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 = khcint_space_open_key(c, KHM_PERM_READ);
+
+        if(khc_is_machine_handle(conf))
+            hkm = khcint_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;
+}
+
+/* obtaincs cs_conf_handle/cs_conf_global */
+KHMEXP khm_int32 KHMAPI 
+khc_read_binary(khm_handle pconf, const 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;
+
+        const wchar_t * value = NULL;
+        int free_space = 0;
+        khm_handle conf = NULL;
+
+        if((value = wcsrchr(pvalue, L'\\')) != NULL) {
+            if(KHM_FAILED(khc_open_space(
+                pconf, 
+                pvalue, 
+                KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), 
+                &conf)))
+                goto _shadow;
+            free_space = 1;
+
+            if (value) {
+                value++;
+            } else {
+#ifdef DEBUG
+                assert(FALSE);
+#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 = khcint_space_open_key(c, KHM_PERM_READ);
+
+        if(khc_is_machine_handle(conf))
+            hkm = khcint_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;
+}
+
+/* obtains cs_conf_handle/cs_conf_global */
+KHMEXP khm_int32 KHMAPI 
+khc_write_string(khm_handle pconf, 
+                 const wchar_t * pvalue, 
+                 wchar_t * buf) 
+{
+    HKEY pk = NULL;
+    kconf_conf_space * c;
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+    LONG hr;
+    size_t cbsize;
+    const wchar_t * value = NULL;
+    int free_space = 0;
+    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(FAILED(StringCbLength(buf, KCONF_MAXCB_STRING, &cbsize))) {
+        rv = KHM_ERROR_INVALID_PARAM;
+        goto _exit;
+    }
+
+    cbsize += sizeof(wchar_t);
+
+    if (khc_handle_flags(pconf) & KCONF_FLAG_WRITEIFMOD) {
+        wchar_t tmpbuf[512];
+        wchar_t * buffer;
+        size_t tmpsize = cbsize;
+        khm_boolean is_equal = FALSE;
+
+        if (cbsize <= sizeof(tmpbuf)) {
+            buffer = tmpbuf;
+        } else {
+            buffer = PMALLOC(cbsize);
+        }
+
+        if (KHM_SUCCEEDED(khc_read_string(pconf, pvalue, buffer, &tmpsize)) &&
+            tmpsize == cbsize) {
+            if (khc_handle_flags(pconf) & KCONF_FLAG_IFMODCI)
+                is_equal = !_wcsicmp(buffer, buf);
+            else
+                is_equal = !wcscmp(buffer, buf);
+        }
+
+        if (buffer != tmpbuf)
+            PFREE(buffer);
+
+        if (is_equal) {
+            return KHM_ERROR_SUCCESS;
+        }
+    }
+
+    if((value = wcsrchr(pvalue, L'\\')) != NULL) {
+        if(KHM_FAILED(khc_open_space(pconf, pvalue, 
+                                     KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), 
+                                     &conf)))
+            return KHM_ERROR_INVALID_PARAM;
+        free_space = 1;
+
+        if (value) {
+            value ++;
+        } else {
+#ifdef DEBUG
+            assert(FALSE);
+#endif
+        }
+    } else {
+        value = pvalue;
+        conf = pconf;
+        free_space = 0;
+    }
+
+    if(!khc_is_handle(conf) || !buf) {
+        rv = KHM_ERROR_INVALID_PARAM;
+        goto _exit;
+    }
+
+    c = khc_space_from_handle(conf);
+
+    if(khc_is_user_handle(conf)) {
+        pk = khcint_space_open_key(c, KHM_PERM_WRITE | KHM_FLAG_CREATE);
+    } else {
+        pk = khcint_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;
+}
+
+/* obtaincs cs_conf_handle/cs_conf_global */
+KHMEXP khm_int32 KHMAPI 
+khc_write_int32(khm_handle pconf, 
+                const wchar_t * pvalue, 
+                khm_int32 buf) 
+{
+    HKEY pk = NULL;
+    kconf_conf_space * c;
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+    LONG hr;
+    const wchar_t * value = NULL;
+    int free_space = 0;
+    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 (khc_handle_flags(pconf) & KCONF_FLAG_WRITEIFMOD) {
+        khm_int32 tmpvalue;
+
+        if (KHM_SUCCEEDED(khc_read_int32(pconf, pvalue, &tmpvalue)) &&
+            tmpvalue == buf) {
+            return KHM_ERROR_SUCCESS;
+        }
+    }
+
+    if((value = wcsrchr(pvalue, L'\\')) != NULL) {
+        if(KHM_FAILED(khc_open_space(
+            pconf, 
+            pvalue, 
+            KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), 
+            &conf)))
+            return KHM_ERROR_INVALID_PARAM;
+        free_space = 1;
+
+        if (value) {
+            value ++;
+        } else {
+#ifdef DEBUG
+            assert(FALSE);
+#endif
+        }
+    } else {
+        value = pvalue;
+        conf = pconf;
+        free_space = 0;
+    }
+
+    if(!khc_is_handle(conf))
+        return KHM_ERROR_INVALID_PARAM;
+
+    c = khc_space_from_handle( conf);
+
+    if(khc_is_user_handle(conf)) {
+        pk = khcint_space_open_key(c, KHM_PERM_WRITE | KHM_FLAG_CREATE);
+    } else {
+        pk = khcint_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;
+}
+
+/* obtains cs_conf_handle/cs_conf_global */
+KHMEXP khm_int32 KHMAPI 
+khc_write_int64(khm_handle pconf, const wchar_t * pvalue, khm_int64 buf) {
+    HKEY pk = NULL;
+    kconf_conf_space * c;
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+    LONG hr;
+    const wchar_t * value = NULL;
+    int free_space = 0;
+    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 (khc_handle_flags(pconf) & KCONF_FLAG_WRITEIFMOD) {
+        khm_int64 tmpvalue;
+
+        if (KHM_SUCCEEDED(khc_read_int64(pconf, pvalue, &tmpvalue)) &&
+            tmpvalue == buf) {
+            return KHM_ERROR_SUCCESS;
+        }
+    }
+
+    if((value = wcsrchr(pvalue, L'\\')) != NULL) {
+        if(KHM_FAILED(khc_open_space(
+            pconf, 
+            pvalue, 
+            KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), 
+            &conf)))
+            return KHM_ERROR_INVALID_PARAM;
+        free_space = 1;
+
+        if (value) {
+            value ++;
+        } else {
+#ifdef DEBUG
+            assert(FALSE);
+#endif
+        }
+    } else {
+        value = pvalue;
+        conf = pconf;
+        free_space = 0;
+    }
+
+    if(!khc_is_handle(conf))
+        return KHM_ERROR_INVALID_PARAM;
+
+    c = khc_space_from_handle( conf);
+
+    if(khc_is_user_handle(conf)) {
+        pk = khcint_space_open_key(c, KHM_PERM_WRITE | KHM_FLAG_CREATE);
+    } else {
+        pk = khcint_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;
+}
+
+/* obtains cs_conf_handle/cs_conf_global */
+KHMEXP khm_int32 KHMAPI 
+khc_write_binary(khm_handle pconf, 
+                 const wchar_t * pvalue, 
+                 void * buf, khm_size bufsize) {
+    HKEY pk = NULL;
+    kconf_conf_space * c;
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+    LONG hr;
+    const wchar_t * value = NULL;
+    int free_space = 0;
+    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((value = wcsrchr(pvalue, L'\\')) != NULL) {
+        if(KHM_FAILED(khc_open_space(
+            pconf, 
+            pvalue, 
+            KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), 
+            &conf)))
+            return KHM_ERROR_INVALID_PARAM;
+        free_space = 1;
+
+        if (value) {
+            value ++;
+        } else {
+#ifdef DEBUG
+            assert(FALSE);
+#endif
+        }
+    } else {
+        value = pvalue;
+        conf = pconf;
+        free_space = 0;
+    }
+
+    if(!khc_is_handle(conf))
+        return KHM_ERROR_INVALID_PARAM;
+
+    c = khc_space_from_handle(conf);
+
+    if(khc_is_user_handle(conf)) {
+        pk = khcint_space_open_key(c, KHM_PERM_WRITE | KHM_FLAG_CREATE);
+    } else {
+        pk = khcint_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;
+}
+
+/* no locks */
+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_PARAM;
+
+    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;
+}
+
+/* obtains cs_conf_handle/cs_conf_global */
+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_PARAM;
+
+    c = khc_space_from_handle(conf);
+
+    if(c == conf_root || c->parent == conf_root)
+        *parent = NULL;
+    else
+        *parent = khcint_handle_from_space(c->parent, khc_handle_flags(conf));
+
+    return KHM_ERROR_SUCCESS;
+}
+
+/* obtains cs_conf_global */
+KHMEXP khm_int32 KHMAPI 
+khc_get_type(khm_handle conf, const wchar_t * value) {
+    HKEY hkm = NULL;
+    HKEY hku = NULL;
+    kconf_conf_space * c;
+    khm_int32 rv;
+    LONG hr = ERROR_SUCCESS;
+    DWORD type = 0;
+
+    if(!khc_is_config_running())
+        return KC_NONE;
+
+    if(!khc_is_handle(conf))
+        return KC_NONE;
+
+    c = khc_space_from_handle(conf);
+
+    if(!khc_is_machine_handle(conf))
+        hku = khcint_space_open_key(c, KHM_PERM_READ);
+    hkm = khcint_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;
+}
+
+/* obtains cs_conf_global */
+KHMEXP khm_int32 KHMAPI 
+khc_value_exists(khm_handle conf, const 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_PARAM;
+
+    do {
+        c = khc_space_from_handle(conf);
+
+        if (khc_is_user_handle(conf))
+            hku = khcint_space_open_key(c, KHM_PERM_READ);
+        if (khc_is_machine_handle(conf))
+            hkm = khcint_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 && khc_is_schema_handle(conf)) {
+            for(i=0; i<c->nSchema; i++) {
+                if(!wcscmp(c->schema[i].name, value)) {
+                    rv |= KCONF_FLAG_SCHEMA;
+                    break;
+                }
+            }
+        }
+
+        /* if the value is not found at this level and the handle is
+           shadowed, try the next level down. */
+        if (rv == 0 && khc_is_shadowed(conf))
+            conf = khc_shadow(conf);
+        else
+            break;
+    } while (conf);
+
+    return rv;
+}
+
+/* obtains cs_conf_global */
+KHMEXP khm_int32 KHMAPI
+khc_remove_value(khm_handle conf, const wchar_t * value, khm_int32 flags) {
+    HKEY hku = NULL;
+    HKEY hkm = NULL;
+    kconf_conf_space * c;
+    khm_int32 rv = KHM_ERROR_NOT_FOUND;
+    DWORD t;
+    LONG l;
+
+    if(!khc_is_config_running())
+        return KHM_ERROR_NOT_READY;
+
+    if(!khc_is_handle(conf))
+        return KHM_ERROR_INVALID_PARAM;
+
+    c = khc_space_from_handle(conf);
+
+    if(!khc_is_machine_handle(conf))
+        hku = khcint_space_open_key(c, KHM_PERM_READ);
+    hkm = khcint_space_open_key(c, KHM_PERM_READ | KCONF_FLAG_MACHINE);
+
+    if((flags == 0 ||
+        (flags & KCONF_FLAG_USER)) &&
+       hku && (RegQueryValueEx(hku, value, NULL, 
+                               &t, NULL, NULL) == ERROR_SUCCESS)) {
+        l = RegDeleteValue(hku, value);
+        if (l == ERROR_SUCCESS)
+            rv = KHM_ERROR_SUCCESS;
+        else
+            rv = KHM_ERROR_UNKNOWN;
+    }
+    if((flags == 0 ||
+        (flags & KCONF_FLAG_MACHINE)) &&
+       hkm && (RegQueryValueEx(hkm, value, NULL, 
+                               &t, NULL, NULL) == ERROR_SUCCESS)) {
+        l = RegDeleteValue(hkm, value);
+        if (l == ERROR_SUCCESS)
+            rv = (rv == KHM_ERROR_UNKNOWN)?KHM_ERROR_PARTIAL: 
+                KHM_ERROR_SUCCESS;
+        else
+            rv = (rv == KHM_ERROR_SUCCESS)?KHM_ERROR_PARTIAL:
+                KHM_ERROR_UNKNOWN;
+    }
+
+    return rv;
+}
+
+/* called with cs_conf_global held */
+khm_int32
+khcint_remove_space(kconf_conf_space * c, khm_int32 flags) {
+    kconf_conf_space * cc;
+    kconf_conf_space * cn;
+    kconf_conf_space * p;
+
+    /* TODO: if this is the last child space and the parent is marked
+       for deletion, delete the parent as well. */
+
+    p = TPARENT(c);
+
+    /* We don't allow deleting top level keys.  They are
+       predefined. */
+#ifdef DEBUG
+    assert(p);
+#endif
+    if (!p)
+        return KHM_ERROR_INVALID_OPERATION;
+
+    cc = TFIRSTCHILD(c);
+    while (cc) {
+        cn = LNEXT(cc);
+
+        khcint_remove_space(cc, flags);
+
+        cc = cn;
+    }
+
+    cc = TFIRSTCHILD(c);
+    if (!cc && c->refcount == 0) {
+        TDELCHILD(p, c);
+        khcint_free_space(c);
+    } else {
+        c->flags |= (flags &
+                     (KCONF_SPACE_FLAG_DELETE_M |
+                      KCONF_SPACE_FLAG_DELETE_U));
+
+        /* if all the registry spaces have been marked as deleted and
+           there is no schema, we should mark the space as deleted as
+           well.  Note that ideally we only need to check for stores
+           which have data corresponding to this configuration space,
+           but this is a bit problematic since we don't monitor the
+           registry for changes. */
+        if ((c->flags &
+             (KCONF_SPACE_FLAG_DELETE_M |
+              KCONF_SPACE_FLAG_DELETE_U)) ==
+            (KCONF_SPACE_FLAG_DELETE_M |
+             KCONF_SPACE_FLAG_DELETE_U) &&
+            (!c->schema || c->nSchema == 0))
+
+            c->flags |= KCONF_SPACE_FLAG_DELETED;
+    }
+
+    if (c->regpath && p->regpath) {
+        HKEY hk;
+
+        if (flags & KCONF_SPACE_FLAG_DELETE_U) {
+            hk = khcint_space_open_key(p, KCONF_FLAG_USER);
+
+            if (hk)
+                khcint_RegDeleteKey(hk, c->name);
+        }
+        if (flags & KCONF_SPACE_FLAG_DELETE_M) {
+            hk = khcint_space_open_key(p, KCONF_FLAG_MACHINE);
+
+            if (hk)
+                khcint_RegDeleteKey(hk, c->name);
+        }
+    }
+
+    return KHM_ERROR_SUCCESS;
+}
+
+/* obtains cs_conf_global */
+KHMEXP khm_int32 KHMAPI
+khc_remove_space(khm_handle conf) {
+
+    /*
+       - mark this space as well as all child spaces as
+         'delete-on-close' using flags.  Mark should indicate which
+         repository to delete the space from. (user/machine)
+
+       - When each subspace is released, check if it has been marked
+         for deletion.  If so, delete the marked spaces as well as
+         removing the space from kconf space tree.
+
+       - When removing a subspace from a space, check if the parent
+         space has any children left.  If there are none, check if the
+         parent space is also marked for deletion.
+    */
+    kconf_conf_space * c;
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+    khm_int32 flags = 0;
+
+    if(!khc_is_config_running())
+        return KHM_ERROR_NOT_READY;
+
+    if(!khc_is_handle(conf))
+        return KHM_ERROR_INVALID_PARAM;
+
+    c = khc_space_from_handle(conf);
+
+    EnterCriticalSection(&cs_conf_global);
+
+    if (khc_is_machine_handle(conf))
+        flags |= KCONF_SPACE_FLAG_DELETE_M;
+    if (khc_is_user_handle(conf))
+        flags |= KCONF_SPACE_FLAG_DELETE_U;
+
+    rv = khcint_remove_space(c, flags);
+
+    LeaveCriticalSection(&cs_conf_global);
+
+    return rv;
+}
+
+/* no locks */
+khm_boolean 
+khcint_is_valid_name(wchar_t * name)
+{
+    size_t cbsize;
+    if(FAILED(StringCbLength(name, KCONF_MAXCB_NAME, &cbsize)))
+        return FALSE;
+    return TRUE;
+}
+
+/* no locks */
+khm_int32 
+khcint_validate_schema(const 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(!khcint_is_valid_name(schema[i].name) ||
+                    schema[i].type != KC_SPACE)
+                    return KHM_ERROR_INVALID_PARAM;
+                state = 1;
+                break;
+
+            case 1: /* we are inside a config space, in the values area */
+                if(!khcint_is_valid_name(schema[i].name))
+                    return KHM_ERROR_INVALID_PARAM;
+                if(schema[i].type == KC_SPACE) {
+                    if(KHM_FAILED(khcint_validate_schema(schema, i, &i)))
+                        return KHM_ERROR_INVALID_PARAM;
+                    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_PARAM;
+                }
+                break;
+
+            case 2: /* we are inside a config space, in the subspace area */
+                if(schema[i].type == KC_SPACE) {
+                    if(KHM_FAILED(khcint_validate_schema(schema, i, &i)))
+                        return KHM_ERROR_INVALID_PARAM;
+                } else if(schema[i].type == KC_ENDSPACE) {
+                    end_found = 1;
+                    if(end)
+                        *end = i;
+                } else {
+                    return KHM_ERROR_INVALID_PARAM;
+                }
+                break;
+
+            default:
+                /* unreachable */
+                return KHM_ERROR_INVALID_PARAM;
+        }
+        i++;
+    }
+
+    return KHM_ERROR_SUCCESS;
+}
+
+/* obtains cs_conf_handle/cs_conf_global; called with cs_conf_global */
+khm_int32 
+khcint_load_schema_i(khm_handle parent, const kconf_schema * schema, 
+                     int begin, int * end)
+{
+    int i;
+    int state = 0;
+    int end_found = 0;
+    kconf_conf_space * thisconf = NULL;
+    khm_handle h = NULL;
+
+    i=begin;
+    while(!end_found) {
+        switch(state) {
+            case 0: /* initial.  this record should start a config space */
+                LeaveCriticalSection(&cs_conf_global);
+                if(KHM_FAILED(khc_open_space(parent, schema[i].name, 
+                                             KHM_FLAG_CREATE, &h))) {
+                    EnterCriticalSection(&cs_conf_global);
+                    return KHM_ERROR_INVALID_PARAM;
+                }
+                EnterCriticalSection(&cs_conf_global);
+                thisconf = khc_space_from_handle(h);
+                thisconf->schema = schema + (begin + 1);
+                thisconf->nSchema = 0;
+                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(khcint_load_schema_i(h, schema, i, &i)))
+                        return KHM_ERROR_INVALID_PARAM;
+                    state = 2;
+                } else if(schema[i].type == KC_ENDSPACE) {
+                    thisconf->nSchema = i - (begin + 1);
+                    end_found = 1;
+                    if(end)
+                        *end = i;
+                    LeaveCriticalSection(&cs_conf_global);
+                    khc_close_space(h);
+                    EnterCriticalSection(&cs_conf_global);
+                }
+                break;
+
+            case 2: /* we are inside a config space, in the subspace area */
+                if(schema[i].type == KC_SPACE) {
+                    if(KHM_FAILED(khcint_load_schema_i(h, schema, i, &i)))
+                        return KHM_ERROR_INVALID_PARAM;
+                } else if(schema[i].type == KC_ENDSPACE) {
+                    end_found = 1;
+                    if(end)
+                        *end = i;
+                    LeaveCriticalSection(&cs_conf_global);
+                    khc_close_space(h);
+                    EnterCriticalSection(&cs_conf_global);
+                } else {
+                    return KHM_ERROR_INVALID_PARAM;
+                }
+                break;
+
+            default:
+                /* unreachable */
+                return KHM_ERROR_INVALID_PARAM;
+        }
+        i++;
+    }
+
+    return KHM_ERROR_SUCCESS;
+}
+
+/* obtains cs_conf_handle/cs_conf_global */
+KHMEXP khm_int32 KHMAPI 
+khc_load_schema(khm_handle conf, const 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_PARAM;
+
+    if(KHM_FAILED(khcint_validate_schema(schema, 0, NULL)))
+        return KHM_ERROR_INVALID_PARAM;
+
+    EnterCriticalSection(&cs_conf_global);
+    rv = khcint_load_schema_i(conf, schema, 0, NULL);        
+    LeaveCriticalSection(&cs_conf_global);
+
+    return rv;
+}
+
+/* obtains cs_conf_handle/cs_conf_global; called with cs_conf_global */
+khm_int32 
+khcint_unload_schema_i(khm_handle parent, const kconf_schema * schema, 
+                       int begin, int * end)
+{
+    int i;
+    int state = 0;
+    int end_found = 0;
+    kconf_conf_space * thisconf = NULL;
+    khm_handle h = NULL;
+
+    i=begin;
+    while(!end_found) {
+        switch(state) {
+            case 0: /* initial.  this record should start a config space */
+                LeaveCriticalSection(&cs_conf_global);
+                if(KHM_FAILED(khc_open_space(parent, schema[i].name, 0, &h))) {
+                    EnterCriticalSection(&cs_conf_global);
+                    return KHM_ERROR_INVALID_PARAM;
+                }
+                EnterCriticalSection(&cs_conf_global);
+                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(khcint_unload_schema_i(h, schema, i, &i)))
+                        return KHM_ERROR_INVALID_PARAM;
+                    state = 2;
+                } else if(schema[i].type == KC_ENDSPACE) {
+                    end_found = 1;
+                    if(end)
+                        *end = i;
+                    LeaveCriticalSection(&cs_conf_global);
+                    khc_close_space(h);
+                    EnterCriticalSection(&cs_conf_global);
+                }
+                break;
+
+            case 2: /* we are inside a config space, in the subspace area */
+                if(schema[i].type == KC_SPACE) {
+                    if(KHM_FAILED(khcint_unload_schema_i(h, schema, i, &i)))
+                        return KHM_ERROR_INVALID_PARAM;
+                } else if(schema[i].type == KC_ENDSPACE) {
+                    end_found = 1;
+                    if(end)
+                        *end = i;
+                    LeaveCriticalSection(&cs_conf_global);
+                    khc_close_space(h);
+                    EnterCriticalSection(&cs_conf_global);
+                } else {
+                    return KHM_ERROR_INVALID_PARAM;
+                }
+                break;
+
+            default:
+                /* unreachable */
+                return KHM_ERROR_INVALID_PARAM;
+        }
+        i++;
+    }
+
+    return KHM_ERROR_SUCCESS;
+}
+
+/* obtains cs_conf_handle/cs_conf_global */
+KHMEXP khm_int32 KHMAPI 
+khc_unload_schema(khm_handle conf, const 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_PARAM;
+
+    if(KHM_FAILED(khcint_validate_schema(schema, 0, NULL)))
+        return KHM_ERROR_INVALID_PARAM;
+
+    EnterCriticalSection(&cs_conf_global);
+    rv = khcint_unload_schema_i(conf, schema, 0, NULL);
+    LeaveCriticalSection(&cs_conf_global);
+
+    return rv;
+}
+
+/* obtaincs cs_conf_handle/cs_conf_global */
+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_PARAM;
+
+    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 = khcint_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 = khcint_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 = khcint_handle_from_space(c, khc_handle_flags(conf));
+        rv = KHM_ERROR_SUCCESS;
+    } else {
+        *next = NULL;
+        rv = KHM_ERROR_NOT_FOUND;
+    }
+
+    return rv;
+}
+
+/* obtains cs_conf_handle/cs_conf_global */
+KHMEXP khm_int32 KHMAPI 
+khc_write_multi_string(khm_handle conf, const wchar_t * value, wchar_t * buf)
+{
+    size_t cb;
+    wchar_t vbuf[KCONF_MAXCCH_STRING];
+    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_PARAM;
+
+    if(multi_string_to_csv(NULL, &cb, buf) != KHM_ERROR_TOO_LONG)
+        return KHM_ERROR_INVALID_PARAM;
+
+    if (cb < sizeof(vbuf))
+        tb = vbuf;
+    else
+        tb = PMALLOC(cb);
+
+    assert(tb != NULL);
+
+    multi_string_to_csv(tb, &cb, buf);
+    rv = khc_write_string(conf, value, tb);
+
+    if (tb != vbuf)
+        PFREE(tb);
+    return rv;
+}
+
+/* obtains cs_conf_handle/cs_conf_global */
+KHMEXP khm_int32 KHMAPI 
+khc_read_multi_string(khm_handle conf, const wchar_t * value, 
+                      wchar_t * buf, khm_size * bufsize)
+{
+    wchar_t vbuf[KCONF_MAXCCH_STRING];
+    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_PARAM;
+
+    rv = khc_read_string(conf, value, NULL, &cbbuf);
+    if(rv != KHM_ERROR_TOO_LONG)
+        return rv;
+
+    if (cbbuf < sizeof(vbuf))
+        tb = vbuf;
+    else
+        tb = PMALLOC(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:
+    if (tb != vbuf)
+        PFREE(tb);
+
+    return rv;
+}
index b550d135846549905783e4263a67e49568325651..691fe7115c5c4cec86f861a057ad2a3f7b23b3fd 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KCONFIG_H\r
-#define __KHIMAIRA_KCONFIG_H\r
-\r
-#include<khdefs.h>\r
-#include<mstring.h>\r
-\r
-/*! \defgroup kconf NetIDMgr Configuration Provider */\r
-/*@{*/\r
-\r
-/*! \brief Configuration schema descriptor record \r
-\r
-    The schema descriptor is a convenient way to provide a default set\r
-    of configuration options for a part of an application.  It\r
-    describes the configuration spaces and the values and subspaces\r
-    contained in each space.\r
-\r
-    \see kconf_load_schema()\r
-*/\r
-typedef struct tag_kconf_schema {\r
-    wchar_t *   name;       /*!< name of the object being described.\r
-                                Optional for KC_ENDSPACE type object,\r
-                                but required for everything else.\r
-                                Names can be upto KCONF_MAXCCH_NAME\r
-                                characters in length. */\r
-    khm_int32   type;       /*!< type of the object.  Can be one of\r
-                                KC_SPACE, KC_ENDSPACE, KC_INT32,\r
-                                KC_INT64, KC_STRING or KC_BINARY */\r
-    khm_ui_8    value;      /*!< the value of the object.  It is not\r
-                                used for KC_SPACE and KC_ENDSPACE\r
-                                typed objects.  For a KC_STRING, this\r
-                                contains a pointer to the string\r
-                                value.  The string should not be\r
-                                longer than KCONF_MAXCCH_STRING\r
-                                characters. KC_INT32 and KC_INT64\r
-                                objects store the value directly in\r
-                                this field, while KC_BINARY objects do\r
-                                not support defining a default value\r
-                                here. */\r
-    wchar_t *   description;/*!< a friendly description of the value\r
-                                or configuration space. */\r
-} kconf_schema;\r
-\r
-/*! \name Configuration data types\r
-  @{*/\r
-/*! \brief Not a known type */\r
-#define KC_NONE         0\r
-\r
-/*! \brief When used as ::kconf_schema \a type, defines the start of a configuration space.\r
-\r
-    There should be a subsequent KC_ENDSPACE record in the schema\r
-    which defines the end of this configuration space.\r
-\r
-    \a name specifies the name of the configuration space.  Optionally\r
-    use \a description to provide a description.*/\r
-#define KC_SPACE        1\r
-\r
-/*! \brief Ends a configuration space started with KC_SPACE */\r
-#define KC_ENDSPACE     2\r
-\r
-/*! \brief A 32 bit integer\r
-\r
-    Specifies a configuration parameter named \a name which is of this\r
-    type.  Use \a description to provide an optional description of\r
-    the value.\r
-\r
-    \a value specifies a default value for this parameter in the lower\r
-    32 bits.\r
-*/\r
-#define KC_INT32        3\r
-\r
-/*! \brief A 64 bit integer \r
-\r
-    Specifies a configuration parameter named \a name which is of this\r
-    type.  Use \a description to provide an optional description of\r
-    the value.\r
-\r
-    \a value specifies a default value for this parameter.\r
-*/\r
-#define KC_INT64        4\r
-\r
-/*! \brief A unicode string \r
-\r
-    Specifies a configuration parameter named \a name which is of this\r
-    type.  Use \a description to provide an optional description of\r
-    the value.\r
-\r
-    \a value specifies a default value for this parameter which should\r
-    be a pointer to a NULL terminated unicode string of no more than\r
-    ::KCONF_MAXCCH_STRING characters.\r
-*/\r
-#define KC_STRING       5\r
-\r
-/*! \brief An unparsed binary stream \r
-\r
-    Specifies a configuration parameter named \a name which is of this\r
-    type.  Use \a description to provide an optional description of\r
-    the value.\r
-\r
-    Default values are not supported for binary streams.  \a value is\r
-    ignored.\r
-*/\r
-#define KC_BINARY       6\r
-/*@}*/\r
-\r
-/*! \brief This is the root configuration space */\r
-#define KCONF_FLAG_ROOT          0x00000001\r
-\r
-/*! \brief Indicates the configuration store which stores user-specific information */\r
-#define KCONF_FLAG_USER          0x00000002\r
-\r
-/*! \brief Indicates the configuration store which stores machine-specific information */\r
-#define KCONF_FLAG_MACHINE       0x00000004\r
-\r
-/*! \brief Indicates the configuration store which stores the schema */\r
-#define KCONF_FLAG_SCHEMA        0x00000008\r
-\r
-/*! \brief Indicates that the last component of the given configuration path is to be considered to be a configuration value */\r
-#define KCONF_FLAG_TRAILINGVALUE 0x00000020\r
-\r
-/*! \brief Only write values back there is a change\r
-\r
-    Any write operations using the handle with check if the value\r
-    being written is different from the value being read from the\r
-    handle.  It will only be written if the value is different.\r
-\r
-    \note Note that the value being read from a handle takes schema and\r
-    shadowed configuration handles into consideration while the value\r
-    being written is only written to the topmost layer of\r
-    configuration that can be written to.\r
-\r
-    \note Note also that this flag does not affect binary values.\r
- */\r
-#define KCONF_FLAG_WRITEIFMOD    0x00000040\r
-\r
-/*! \brief Use case-insensitive comparison for KCONF_FLAG_WRITEIFMOD\r
-\r
-    When used in combination with \a KCONF_FLAG_WRITEIFMOD , the\r
-    string comparison used when determining whether the string read\r
-    from the configuration handle is the same as the string being\r
-    written will be case insensitive.  If this flag is not set, the\r
-    comparison will be case sensitive.\r
- */\r
-#define KCONF_FLAG_IFMODCI     0x00000080\r
-\r
-/*! \brief Do not parse the configuration space name\r
-\r
-    If set, disables the parsing of the configuration space for\r
-    subspaces.  The space name is taken verbatim to be a configuration\r
-    space name.  This can be used when there can be forward slashes or\r
-    backslahes in the name which are not escaped.\r
-\r
-    By default, the configuration space name,\r
-\r
-    \code\r
-    L"foo\\bar"\r
-    \endcode\r
-\r
-    is taken to mean the configuration space \a bar which is a\r
-    subspace of \a foo.  If ::KCONF_FLAG_NOPARSENAME is set, then this\r
-    is taken to mean configuration space \a foo\\bar.\r
- */\r
-#define KCONF_FLAG_NOPARSENAME   0x00000040\r
-\r
-/*! \brief Maximum number of allowed characters (including terminating NULL) in a name \r
-\r
-    \note This is a hard limit in Windows, since we are mapping\r
-        configuration spaces to registry keys.\r
-*/\r
-#define KCONF_MAXCCH_NAME 256\r
-\r
-/*! \brief Maximum number of allowed bytes (including terminating NULL) in a name */\r
-#define KCONF_MAXCB_NAME (KCONF_MAXCCH_NAME * sizeof(wchar_t))\r
-\r
-/*! \brief Maximum level of nesting for configuration spaces\r
- */\r
-#define KCONF_MAX_DEPTH 16\r
-\r
-/*! \brief Maximum number of allowed characters (including terminating NULL) in a configuration path */\r
-#define KCONF_MAXCCH_PATH (KCONF_MAXCCH_NAME * KCONF_MAX_DEPTH)\r
-\r
-/*! \brief Maximum number of allowed bytes (including terminating NULL) in a configuration path */\r
-#define KCONF_MAXCB_PATH (KCONF_MAXCCH_PATH * sizeof(wchar_t))\r
-\r
-/*! \brief Maximum number of allowed characters (including terminating NULL) in a string */\r
-#define KCONF_MAXCCH_STRING KHM_MAXCCH_STRING\r
-\r
-/*! \brief Maximum number of allowed bytes (including terminating NULL) in a string */\r
-#define KCONF_MAXCB_STRING (KCONF_MAXCCH_STRING * sizeof(wchar_t))\r
-\r
-/*! \brief Open a configuration space\r
-\r
-    Opens the configuration space specified by \a cspace.  By default,\r
-    the opened space includes user,machine and schema configuration\r
-    stores.  However, you can specify a subset of these.\r
-\r
-    If the configuration space does not exist and the \a flags specify\r
-    KHM_FLAG_CREATE, then the configuration space is created.  The\r
-    stores that are affected by the create operation depend on \a\r
-    flags.  If the \a flags only specifies ::KCONF_FLAG_MACHINE, then\r
-    the configuration space is created in the machine store.  If \a\r
-    flags specifies any combination of stores including \a\r
-    ::KCONF_FLAG_USER, then the configuration space is created in the\r
-    user store.  Note that ::KCONF_FLAG_SCHEMA is readonly.\r
-\r
-    Once opened, use khc_close_space() to close the configuration\r
-    space.\r
-\r
-    \param[in] parent The parent configuration space.  The path\r
-        specified in \a cspace is relative to the parent.  Set this to\r
-        NULL to indicate the root configuration space.  \r
-\r
-    \param[in] cspace The confiuration path.  This can be up to\r
-        ::KCONF_MAXCCH_PATH characters in length.  Use either\r
-        backslashes or forward slashes to specify hiearchy.  Set this\r
-        to NULL to reopen the parent configuration space.\r
-\r
-    \param[in] flags Flags.  This can be a combination of KCONF_FLAG_*\r
-        constants and KHM_FLAG_CREATE.  If none of ::KCONF_FLAG_USER,\r
-        ::KCONF_FLAG_MACHINE or ::KCONF_FLAG_SCHEMA is specified, then\r
-        it defaults to all three.\r
-\r
-    \param[out] result Pointer to a handle which receives the handle\r
-        to the opened configuration space if the call succeeds.\r
-\r
-    \note You can re-open a configuration space with different flags\r
-        such as ::KCONF_FLAG_MACHINE by specifying NULL for \a cspace\r
-        and settings \a flags to the required flags.\r
-\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-khc_open_space(khm_handle parent, const wchar_t * cspace, khm_int32 flags, \r
-               khm_handle * result);\r
-\r
-/*! \brief Set the shadow space for a configuration handle\r
-\r
-    The handle specified by \a lower becomes a shadow for the handle\r
-    specified by \a upper.  Any configuration value that is queried in\r
-    \a upper that does not exist in \a upper will be queried in \a\r
-    lower.\r
-\r
-    If \a upper already had a shadow handle, that handle will be\r
-    replaced by \a lower.  The handle \a lower still needs to be\r
-    closed by a call to khc_close_space().  However, closing \a lower\r
-    will not affect \a upper which will still treat the configuration\r
-    space pointed to by \a lower to be it's shadow.\r
-\r
-    Shadows are specific to handles and not configuration spaces.\r
-    Shadowing a configuration space using one handle does not affect\r
-    any other handles which may be obtained for the same configuration\r
-    space.\r
-\r
-    Specify NULL for \a lower to remove any prior shadow.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_shadow_space(khm_handle upper, khm_handle lower);\r
-\r
-/*! \brief Close a handle opened with khc_open_space()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-khc_close_space(khm_handle conf);\r
-\r
-/*! \brief Read a string value from a configuration space\r
-\r
-    The \a value_name parameter specifies the value to read from the\r
-    configuration space.  This can be either a value name or a value\r
-    path consisting of a series nested configuration space names\r
-    followed by the value name all separated by backslashes or forward\r
-    slashes.\r
-\r
-    For example: If \a conf is a handle to the configuration space \c\r
-    'A/B/C', then the value name \c 'D/E/v' refers to the value named\r
-    \c 'v' in the configuration space \c 'A/B/C/D/E'.\r
-\r
-    The specific configuration store that is used to access the value\r
-    depends on the flags that were specified in the call to\r
-    khc_open_space().  The precedence of configuration stores are as\r
-    follows:\r
-\r
-    - If KCONF_FLAG_USER was specified, then the user configuration\r
-      space.\r
-\r
-    - Otherwise, if KCONF_FLAG_MACHINE was specified, then the machine\r
-      configuration space.\r
-\r
-    - Otherwise, if KCONF_FLAG_SCHEMA was specified, the the schema\r
-      store.\r
-\r
-    Note that not specifying any of the configuration store specifiers\r
-    in the call to khc_open_space() is equivalent to specifying all\r
-    three.\r
-\r
-    If the value is not found in the configuration space and any\r
-    shadowed configuration spaces, the function returns \a\r
-    KHM_ERROR_NOT_FOUND.  In this case, the buffer is left unmodified.\r
-\r
-    \param[in] buf Buffer to copy the string to.  Specify NULL to just\r
-        retrieve the number of required bytes.\r
-    \r
-    \param[in,out] bufsize On entry, specifies the number of bytes of\r
-        space available at the location specified by \a buf.  On exit\r
-        specifies the number of bytes actually copied or the size of\r
-        the required buffer if \a buf is NULL or insufficient.\r
-\r
-    \retval KHM_ERROR_NOT_READY The configuration provider has not started\r
-    \retval KHM_ERROR_INVALID_PARAM One or more of the supplied parameters are not valid\r
-    \retval KHM_ERROR_TYPE_MISMATCH The specified value is not a string\r
-    \retval KHM_ERROR_TOO_LONG \a buf was NULL or the size of the buffer was insufficient.  The required size is in bufsize.\r
-    \retval KHM_ERROR_SUCCESS Success.  The number of bytes copied is in bufsize.\r
-    \retval KHM_ERROR_NOT_FOUND The value was not found.\r
-\r
-    \see khc_open_space()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-khc_read_string(khm_handle conf, \r
-                const wchar_t * value_name, \r
-                wchar_t * buf, \r
-                khm_size * bufsize);\r
-\r
-/*! \brief Read a multi-string value from a configuration space\r
-\r
-    The \a value_name parameter specifies the value to read from the\r
-    configuration space.  This can be either a value name or a value\r
-    path consisting of a series nested configuration space names\r
-    followed by the value name all separated by backslashes or forward\r
-    slashes.\r
-\r
-    For example: If \a conf is a handle to the configuration space \c\r
-    'A/B/C', then the value name \c 'D/E/v' refers to the value named\r
-    \c 'v' in the configuration space \c 'A/B/C/D/E'.\r
-\r
-    The specific configuration store that is used to access the value\r
-    depends on the flags that were specified in the call to\r
-    khc_open_space().  The precedence of configuration stores are as\r
-    follows:\r
-\r
-    - If KCONF_FLAG_USER was specified, then the user configuration\r
-      space.\r
-\r
-    - Otherwise, if KCONF_FLAG_MACHINE was specified, then the machine\r
-      configuration space.\r
-\r
-    - Otherwise, if KCONF_FLAG_SCHEMA was specified, the the schema\r
-      store.\r
-\r
-    A multi-string is a pseudo data type.  The value in the\r
-    configuration store should contain a CSV string.  Each comma\r
-    separated value in the CSV string is considered to be a separate\r
-    value.  Empty values are not allowed. The buffer pointed to by \a\r
-    buf will receive these values in the form of a series of NULL\r
-    terminated strings terminated by an empty string (or equivalently,\r
-    the last string will be terminated by a double NULL).\r
-\r
-    Note that not specifying any of the configuration store specifiers\r
-    in the call to khc_open_space() is equivalent to specifying all\r
-    three.\r
-\r
-    If the value is not found in the configuration space and any\r
-    shadowed configuration spaces, the function returns \a\r
-    KHM_ERROR_NOT_FOUND.  In this case, the buffer is left unmodified.\r
-\r
-    \param[in] buf Buffer to copy the multi-string to.  Specify NULL\r
-        to just retrieve the number of required bytes.\r
-    \r
-    \param[in,out] bufsize On entry, specifies the number of bytes of\r
-        space available at the location specified by \a buf.  On exit\r
-        specifies the number of bytes actually copied or the size of\r
-        the required buffer if \a buf is NULL or insufficient.\r
-\r
-    \retval KHM_ERROR_NOT_READY The configuration provider has not started\r
-    \retval KHM_ERROR_INVALID_PARAM One or more of the supplied parameters are not valid\r
-    \retval KHM_ERROR_TYPE_MISMATCH The specified value is not a string\r
-    \retval KHM_ERROR_TOO_LONG \a buf was NULL or the size of the buffer was insufficient.  The required size is in bufsize.\r
-    \retval KHM_ERROR_SUCCESS Success.  The number of bytes copied is in bufsize.\r
-    \retval KHM_ERROR_NOT_FOUND The value was not found.\r
-\r
-    \see khc_open_space()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-khc_read_multi_string(khm_handle conf, \r
-                      const wchar_t * value_name, \r
-                      wchar_t * buf, \r
-                      khm_size * bufsize);\r
-\r
-/*! \brief Read a 32 bit integer value from a configuration space\r
-\r
-    The \a value_name parameter specifies the value to read from the\r
-    configuration space.  This can be either a value name or a value\r
-    path consisting of a series nested configuration space names\r
-    followed by the value name all separated by backslashes or forward\r
-    slashes.\r
-\r
-    For example: If \a conf is a handle to the configuration space \c\r
-    'A/B/C', then the value name \c 'D/E/v' refers to the value named\r
-    \c 'v' in the configuration space \c 'A/B/C/D/E'.\r
-\r
-    The specific configuration store that is used to access the value\r
-    depends on the flags that were specified in the call to\r
-    khc_open_space().  The precedence of configuration stores are as\r
-    follows:\r
-\r
-    - If KCONF_FLAG_USER was specified, then the user configuration\r
-      space.\r
-\r
-    - Otherwise, if KCONF_FLAG_MACHINE was specified, then the machine\r
-      configuration space.\r
-\r
-    - Otherwise, if KCONF_FLAG_SCHEMA was specified, the the schema\r
-      store.\r
-\r
-    Note that not specifying any of the configuration store specifiers\r
-    in the call to khc_open_space() is equivalent to specifying all\r
-    three.\r
-\r
-    If the value is not found in the configuration space and any\r
-    shadowed configuration spaces, the function returns \a\r
-    KHM_ERROR_NOT_FOUND.  In this case, the buffer is left unmodified.\r
-\r
-    \param[in] conf Handle to a configuration space\r
-    \param[in] value The value to query\r
-    \param[out] buf The buffer to receive the value\r
-\r
-    \retval KHM_ERROR_NOT_READY The configuration provider has not started.\r
-    \retval KHM_ERROR_SUCCESS Success.  The value that was read was placed in \a buf\r
-    \retval KHM_ERROR_NOT_FOUND The specified value was not found\r
-    \retval KHM_ERROR_INVALID_PARAM One or more parameters were invalid\r
-    \retval KHM_ERROR_TYPE_MISMATCH The specified value was found but was not of the correct type.\r
-    \see khc_open_space()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-khc_read_int32(khm_handle conf, \r
-               const wchar_t * value_name, \r
-               khm_int32 * buf);\r
-\r
-/*! \brief Read a 64 bit integer value from a configuration space\r
-\r
-    The \a value_name parameter specifies the value to read from the\r
-    configuration space.  This can be either a value name or a value\r
-    path consisting of a series nested configuration space names\r
-    followed by the value name all separated by backslashes or forward\r
-    slashes.\r
-\r
-    For example: If \a conf is a handle to the configuration space \c\r
-    'A/B/C', then the value name \c 'D/E/v' refers to the value named\r
-    \c 'v' in the configuration space \c 'A/B/C/D/E'.\r
-\r
-    The specific configuration store that is used to access the value\r
-    depends on the flags that were specified in the call to\r
-    khc_open_space().  The precedence of configuration stores are as\r
-    follows:\r
-\r
-    - If KCONF_FLAG_USER was specified, then the user configuration\r
-      space.\r
-\r
-    - Otherwise, if KCONF_FLAG_MACHINE was specified, then the machine\r
-      configuration space.\r
-\r
-    - Otherwise, if KCONF_FLAG_SCHEMA was specified, the the schema\r
-      store.\r
-\r
-    Note that not specifying any of the configuration store specifiers\r
-    in the call to khc_open_space() is equivalent to specifying all\r
-    three.\r
-\r
-    If the value is not found in the configuration space and any\r
-    shadowed configuration spaces, the function returns \a\r
-    KHM_ERROR_NOT_FOUND.  In this case, the buffer is left unmodified.\r
-\r
-    \param[in] conf Handle to a configuration space\r
-    \param[in] value_name The value to query\r
-    \param[out] buf The buffer to receive the value\r
-\r
-    \retval KHM_ERROR_NOT_READY The configuration provider has not started\r
-    \retval KHM_ERROR_SUCCESS Success.  The value that was read was placed in \a buf\r
-    \retval KHM_ERROR_NOT_FOUND The specified value was not found\r
-    \retval KHM_ERROR_INVALID_PARAM One or more parameters were invalid\r
-    \retval KHM_ERROR_TYPE_MISMATCH The specified value was found but was not the correct data type.\r
-\r
-    \see khc_open_space()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-khc_read_int64(khm_handle conf, \r
-               const wchar_t * value_name, \r
-               khm_int64 * buf);\r
-\r
-/*! \brief Read a binary value from a configuration space\r
-\r
-    The \a value_name parameter specifies the value to read from the\r
-    configuration space.  This can be either a value name or a value\r
-    path consisting of a series nested configuration space names\r
-    followed by the value name all separated by backslashes or forward\r
-    slashes.\r
-\r
-    For example: If \a conf is a handle to the configuration space \c\r
-    'A/B/C', then the value name \c 'D/E/v' refers to the value named\r
-    \c 'v' in the configuration space \c 'A/B/C/D/E'.\r
-\r
-    The specific configuration store that is used to access the value\r
-    depends on the flags that were specified in the call to\r
-    khc_open_space().  The precedence of configuration stores are as\r
-    follows:\r
-\r
-    - If KCONF_FLAG_USER was specified, then the user configuration\r
-      space.\r
-\r
-    - Otherwise, if KCONF_FLAG_MACHINE was specified, then the machine\r
-      configuration space.\r
-\r
-    Note that not specifying any of the configuration store specifiers\r
-    in the call to khc_open_space() is equivalent to specifying all\r
-    three. Also note that the schema store (KCONF_FLAG_SCHEMA) does\r
-    not support binary values.\r
-\r
-    If the value is not found in the configuration space and any\r
-    shadowed configuration spaces, the function returns \a\r
-    KHM_ERROR_NOT_FOUND.  In this case, the buffer is left unmodified.\r
-\r
-    \param[in] buf Buffer to copy the string to.  Specify NULL to just\r
-        retrieve the number of required bytes.\r
-    \r
-    \param[in,out] bufsize On entry, specifies the number of bytes of\r
-        space available at the location specified by \a buf.  On exit\r
-        specifies the number of bytes actually copied or the size of\r
-        the required buffer if \a buf is NULL or insufficient.\r
-\r
-    \retval KHM_ERROR_SUCCESS Success. The data was copied to \a buf.  The number of bytes copied is stored in \a bufsize\r
-    \retval KHM_ERROR_NOT_FOUND The specified value was not found\r
-    \retval KHM_ERROR_INVALID_PARAM One or more parameters were invalid.\r
-\r
-    \see khc_open_space()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-khc_read_binary(khm_handle conf, \r
-                const wchar_t * value_name, \r
-                void * buf, \r
-                khm_size * bufsize);\r
-\r
-/*! \brief Write a string value to a configuration space\r
-\r
-    The \a value_name parameter specifies the value to write to the\r
-    configuration space.  This can be either a value name or a value\r
-    path consisting of a series nested configuration space names\r
-    followed by the value name all separated by backslashes or forward\r
-    slashes.\r
-\r
-    For example: If \a conf is a handle to the configuration space \c\r
-    'A/B/C', then the value name \c 'D/E/v' refers to the value named\r
-    \c 'v' in the configuration space \c 'A/B/C/D/E'.\r
-\r
-    The specific configuration store that is used to write the value\r
-    depends on the flags that were specified in the call to\r
-    khc_open_space().  The precedence of configuration stores are as\r
-    follows:\r
-\r
-    - If \a KCONF_FLAG_USER was specified, then the user configuration\r
-      space.\r
-\r
-    - Otherwise, if \a KCONF_FLAG_MACHINE was specified, then the\r
-      machine configuration space.\r
-\r
-    Note that not specifying any of the configuration store specifiers\r
-    in the call to khc_open_space() is equivalent to specifying all\r
-    three.  Also note that the schema store (KCONF_FLAG_SCHEMA) is\r
-    readonly.\r
-\r
-    If the \a KCONF_FLAG_WRITEIFMOD flag is specified in the call to\r
-    khc_open_space() for obtaining the configuration handle, the\r
-    specified string will only be written if it is different from the\r
-    value being read from the handle.\r
-\r
-    If the \a KCONF_FLAG_IFMODCI flag is specified along with the \a\r
-    KCONF_FLAG_WRITEIFMOD flag, then the string comparison used will\r
-    be case insensitive.\r
-\r
-    \param[in] conf Handle to a configuration space\r
-    \param[in] value_name Name of value to write\r
-    \param[in] buf A NULL terminated unicode string not exceeding KCONF_MAXCCH_STRING in characters including terminating NULL\r
-\r
-    \see khc_open_space()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-khc_write_string(khm_handle conf, \r
-                 const wchar_t * value_name, \r
-                 wchar_t * buf);\r
-\r
-/*! \brief Write a multi-string value to a configuration space\r
-\r
-    The \a value_name parameter specifies the value to write to the\r
-    configuration space.  This can be either a value name or a value\r
-    path consisting of a series nested configuration space names\r
-    followed by the value name all separated by backslashes or forward\r
-    slashes.\r
-\r
-    For example: If \a conf is a handle to the configuration space \c\r
-    'A/B/C', then the value name \c 'D/E/v' refers to the value named\r
-    \c 'v' in the configuration space \c 'A/B/C/D/E'.\r
-\r
-    The specific configuration store that is used to write the value\r
-    depends on the flags that were specified in the call to\r
-    khc_open_space().  The precedence of configuration stores are as\r
-    follows:\r
-\r
-    A multi-string is a pseudo data type.  The buffer pointed to by \a\r
-    buf should contain a sequence of NULL terminated strings\r
-    terminated by an empty string (or equivalently, the last string\r
-    should terminate with a double NULL).  This will be stored in the\r
-    value as a CSV string.\r
-\r
-    - If KCONF_FLAG_USER was specified, then the user configuration\r
-      space.\r
-\r
-    - Otherwise, if KCONF_FLAG_MACHINE was specified, then the machine\r
-      configuration space.\r
-\r
-    Note that not specifying any of the configuration store specifiers\r
-    in the call to khc_open_space() is equivalent to specifying all\r
-    three.  Also note that the schema store (KCONF_FLAG_SCHEMA) is\r
-    readonly.\r
-\r
-    If the \a KCONF_FLAG_WRITEIFMOD flag is specified in the call to\r
-    khc_open_space() for obtaining the configuration handle, the\r
-    specified string will only be written if it is different from the\r
-    value being read from the handle.\r
-\r
-    If the \a KCONF_FLAG_IFMODCI flag is specified along with the \a\r
-    KCONF_FLAG_WRITEIFMOD flag, then the string comparison used will\r
-    be case insensitive.\r
-\r
-    \see khc_open_space()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-khc_write_multi_string(khm_handle conf, \r
-                       const wchar_t * value_name, \r
-                       wchar_t * buf);\r
-\r
-/*! \brief Write a 32 bit integer value to a configuration space\r
-\r
-    The \a value_name parameter specifies the value to write to the\r
-    configuration space.  This can be either a value name or a value\r
-    path consisting of a series nested configuration space names\r
-    followed by the value name all separated by backslashes or forward\r
-    slashes.\r
-\r
-    For example: If \a conf is a handle to the configuration space \c\r
-    'A/B/C', then the value name \c 'D/E/v' refers to the value named\r
-    \c 'v' in the configuration space \c 'A/B/C/D/E'.\r
-\r
-    The specific configuration store that is used to write the value\r
-    depends on the flags that were specified in the call to\r
-    khc_open_space().  The precedence of configuration stores are as\r
-    follows:\r
-\r
-    - If KCONF_FLAG_USER was specified, then the user configuration\r
-      space.\r
-\r
-    - Otherwise, if KCONF_FLAG_MACHINE was specified, then the machine\r
-      configuration space.\r
-\r
-    Note that not specifying any of the configuration store specifiers\r
-    in the call to khc_open_space() is equivalent to specifying all\r
-    three.  Also note that the schema store (KCONF_FLAG_SCHEMA) is\r
-    readonly.\r
-\r
-    If the \a KCONF_FLAG_WRITEIFMOD flag is specified in the call to\r
-    khc_open_space() for obtaining the configuration handle, the\r
-    specified string will only be written if it is different from the\r
-    value being read from the handle.\r
-\r
-    \see khc_open_space()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-khc_write_int32(khm_handle conf, \r
-                const wchar_t * value_name, \r
-                khm_int32 buf);\r
-\r
-/*! \brief Write a 64 bit integer value to a configuration space\r
-\r
-    The \a value_name parameter specifies the value to write to the\r
-    configuration space.  This can be either a value name or a value\r
-    path consisting of a series nested configuration space names\r
-    followed by the value name all separated by backslashes or forward\r
-    slashes.\r
-\r
-    For example: If \a conf is a handle to the configuration space \c\r
-    'A/B/C', then the value name \c 'D/E/v' refers to the value named\r
-    \c 'v' in the configuration space \c 'A/B/C/D/E'.\r
-\r
-    The specific configuration store that is used to write the value\r
-    depends on the flags that were specified in the call to\r
-    khc_open_space().  The precedence of configuration stores are as\r
-    follows:\r
-\r
-    - If KCONF_FLAG_USER was specified, then the user configuration\r
-      space.\r
-\r
-    - Otherwise, if KCONF_FLAG_MACHINE was specified, then the machine\r
-      configuration space.\r
-\r
-    Note that not specifying any of the configuration store specifiers\r
-    in the call to khc_open_space() is equivalent to specifying all\r
-    three.  Also note that the schema store (KCONF_FLAG_SCHEMA) is\r
-    readonly.\r
-\r
-    If the \a KCONF_FLAG_WRITEIFMOD flag is specified in the call to\r
-    khc_open_space() for obtaining the configuration handle, the\r
-    specified string will only be written if it is different from the\r
-    value being read from the handle.\r
-\r
-    \see khc_open_space()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-khc_write_int64(khm_handle conf, \r
-                const wchar_t * value_name, \r
-                khm_int64 buf);\r
-\r
-/*! \brief Write a binary value to a configuration space\r
-\r
-    The \a value_name parameter specifies the value to write to the\r
-    configuration space.  This can be either a value name or a value\r
-    path consisting of a series nested configuration space names\r
-    followed by the value name all separated by backslashes or forward\r
-    slashes.\r
-\r
-    For example: If \a conf is a handle to the configuration space \c\r
-    'A/B/C', then the value name \c 'D/E/v' refers to the value named\r
-    \c 'v' in the configuration space \c 'A/B/C/D/E'.\r
-\r
-    The specific configuration store that is used to write the value\r
-    depends on the flags that were specified in the call to\r
-    khc_open_space().  The precedence of configuration stores are as\r
-    follows:\r
-\r
-    - If KCONF_FLAG_USER was specified, then the user configuration\r
-      space.\r
-\r
-    - Otherwise, if KCONF_FLAG_MACHINE was specified, then the machine\r
-      configuration space.\r
-\r
-    Note that not specifying any of the configuration store specifiers\r
-    in the call to khc_open_space() is equivalent to specifying all\r
-    three.  Also note that the schema store (KCONF_FLAG_SCHEMA) is\r
-    readonly.\r
-\r
-    \see khc_open_space()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-khc_write_binary(khm_handle conf, \r
-                 const wchar_t * value_name, \r
-                 void * buf, \r
-                 khm_size bufsize);\r
-\r
-/*! \brief Get the type of a value in a configuration space\r
-\r
-    \return The return value is the type of the specified value, or\r
-        KC_NONE if the value does not exist.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_get_type(khm_handle conf, const wchar_t * value_name);\r
-\r
-/*! \brief Check which configuration stores contain a specific value.\r
-\r
-    Each value in a configuration space can be contained in zero or\r
-    more configuration stores.  Use this function to determine which\r
-    configuration stores contain the specific value.\r
-\r
-    The returned bitmask always indicates a subset of the\r
-    configuration stores that were specified when opening the\r
-    configuration space corresponding to \a conf.\r
-\r
-    If the specified handle is shadowed (see khc_shadow_space()) and\r
-    the value is not found in any of the visible stores for the\r
-    topmost handle, each of the shadowed handles will be tried in turn\r
-    until the value is found.  The return value will correspond to the\r
-    handle where the value is first found.\r
-\r
-    \return A combination of ::KCONF_FLAG_MACHINE, ::KCONF_FLAG_USER\r
-        and ::KCONF_FLAG_SCHEMA indicating which stores contain the\r
-        value.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_value_exists(khm_handle conf, const wchar_t * value);\r
-\r
-/*! \brief Remove a value from a configuration space\r
-\r
-    Removes a value from one or more configuration stores.\r
-\r
-    A value can exist in multiple configuration stores.  Only the\r
-    values that are stored in writable stores can be removed.  When\r
-    the function searches for values to remove, it will only look in\r
-    configuration stores that are specified in the handle.  In\r
-    addition, the configuration stores affected can be further\r
-    narrowed by specifying them in the \a flags parameter.  If \a\r
-    flags is zero, then all the stores visible to the handle are\r
-    searched.  If \a flags specifies ::KCONF_FLAG_USER or\r
-    ::KCONF_FLAG_MACHINE or both, then only the specified stores are\r
-    searched, provided that the stores are visible to the handle.\r
-\r
-    This function only operates on the topmost configuration space\r
-    visible to the handle.  If the configuration handle is shadowed,\r
-    the shadowed configuration spaces are unaffected by the removal.\r
-\r
-    \param[in] conf Handle to configuration space to remove value from\r
-\r
-    \param[in] value_name Value to remove\r
-\r
-    \param[in] flags Specifies which configuration stores will be\r
-        affected by the removal.  See above.\r
-\r
-    \retval KHM_ERROR_SUCCESS The value was removed from all the\r
-        specified configuration stores.\r
-\r
-    \retval KHM_ERROR_NOT_FOUND The value was not found.\r
-\r
-    \retval KHM_ERROR_UNKNOWN An unknown error occurred while trying\r
-        to remove the value.\r
-\r
-    \retval KHM_ERROR_PARTIAL The value was successfully removed from\r
-        one or more stores, but the operation failed on one or more\r
-        other stores.\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khc_remove_value(khm_handle conf, const wchar_t * value_name, khm_int32 flags);\r
-\r
-/*! \brief Get the name of a configuration space\r
-\r
-    \param[in] conf Handle to a configuration space\r
-\r
-    \param[out] buf The buffer to receive the name.  Set to NULL if\r
-        only the size of the buffer is required.\r
-\r
-    \param[in,out] bufsize On entry, holds the size of the buffer\r
-        pointed to by \a buf.  On exit, holds the number of bytes\r
-        copied into the buffer including the NULL terminator.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_get_config_space_name(khm_handle conf, \r
-                          wchar_t * buf, \r
-                          khm_size * bufsize);\r
-\r
-/*! \brief Get a handle to the parent space\r
-\r
-    \param[in] conf Handle to a configuration space\r
-\r
-    \param[out] parent Handle to the parent configuration space if the\r
-        call succeeds.  Receives NULL otherwise.  The returned handle\r
-        must be closed using khc_close_space()\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_get_config_space_parent(khm_handle conf, \r
-                            khm_handle * parent);\r
-\r
-/*! \brief Load a configuration schema into the specified configuration space\r
-\r
-    \param[in] conf Handle to a configuration space or NULL to use the\r
-        root configuration space.\r
-\r
-    \param[in] schema The schema to load.  The schema is assumed to be\r
-        well formed.\r
-\r
-    \see khc_unload_schema()\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_load_schema(khm_handle conf, \r
-                const kconf_schema * schema);\r
-\r
-/*! \brief Unload a schema from a configuration space\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_unload_schema(khm_handle conf, \r
-                  const kconf_schema * schema);\r
-\r
-/*! \brief Enumerate the subspaces of a configuration space\r
-\r
-    Prepares a configuration space for enumeration and returns the\r
-    child spaces in no particular order.\r
-\r
-    \param[in] conf The configuration space to enumerate child spaces\r
-\r
-    \param[in] prev The previous configuration space returned by\r
-        khc_enum_subspaces() or NULL if this is the first call.  If\r
-        this is not NULL, then the handle passed in \a prev will be\r
-        freed.\r
-\r
-    \param[out] next If \a prev was NULL, receives the first sub space\r
-        found in \a conf.  You must \b either call\r
-        khc_enum_subspaces() again with the returned handle or call\r
-        khc_close_space() to free the returned handle if no more\r
-        subspaces are required.  \a next can point to the same handle\r
-        specified in \a prev.\r
-\r
-    \retval KHM_ERROR_SUCCESS The call succeeded.  There is a valid\r
-        handle to a configuration space in \a first_subspace.\r
-\r
-    \retval KHM_ERROR_INVALID_PARAM Either \a conf or \a prev was not a\r
-        valid configuration space handle or \a first_subspace is NULL.\r
-        Note that \a prev can be NULL.\r
-\r
-    \retval KHM_ERROR_NOT_FOUND There were no subspaces in the\r
-        configuration space pointed to by \a conf.\r
-\r
-    \note The configuration spaces that are enumerated directly belong\r
-        to the configuration space given by \a conf.  This function\r
-        does not enumerate subspaces of shadowed configuration spaces\r
-        (see khc_shadow_space()).  Even if \a conf was obtained on a\r
-        restricted domain (i.e. you specified one or more\r
-        configuration stores when you openend the handle and didn't\r
-        include all the configuration stores. See khc_open_space()),\r
-        the subspaces that are returned are the union of all\r
-        configuration spaces in all the configuration stores.  This is\r
-        not a bug.  This is a feature.  In NetIDMgr, a configuartion\r
-        space exists if some configuration store defines it (or it was\r
-        created with a call to khc_open_space() even if no\r
-        configuration store defines it yet).  This is the tradeoff you\r
-        make when using a layered configuration system.\r
-\r
-       However, the returned handle has the same domain restrictions\r
-       as \a conf.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khc_enum_subspaces(khm_handle conf,\r
-                   khm_handle prev,\r
-                   khm_handle * next);\r
-\r
-/*! \brief Remove a configuration space\r
-\r
-    The configuration space will be marked for removal.  Once all the\r
-    handles for the space have been released, it will be deleted.  The\r
-    configuration stores that will be affected are the write enabled\r
-    configuration stores for the handle.\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khc_remove_space(khm_handle conf);\r
-/*@}*/\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 tag_kconf_schema {
+    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 Only write values back there is a change
+
+    Any write operations using the handle with check if the value
+    being written is different from the value being read from the
+    handle.  It will only be written if the value is different.
+
+    \note Note that the value being read from a handle takes schema and
+    shadowed configuration handles into consideration while the value
+    being written is only written to the topmost layer of
+    configuration that can be written to.
+
+    \note Note also that this flag does not affect binary values.
+ */
+#define KCONF_FLAG_WRITEIFMOD    0x00000040
+
+/*! \brief Use case-insensitive comparison for KCONF_FLAG_WRITEIFMOD
+
+    When used in combination with \a KCONF_FLAG_WRITEIFMOD , the
+    string comparison used when determining whether the string read
+    from the configuration handle is the same as the string being
+    written will be case insensitive.  If this flag is not set, the
+    comparison will be case sensitive.
+ */
+#define KCONF_FLAG_IFMODCI     0x00000080
+
+/*! \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, const 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_name 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.
+
+    If the value is not found in the configuration space and any
+    shadowed configuration spaces, the function returns \a
+    KHM_ERROR_NOT_FOUND.  In this case, the buffer is left unmodified.
+
+    \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_PARAM 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.
+    \retval KHM_ERROR_NOT_FOUND The value was not found.
+
+    \see khc_open_space()
+*/
+KHMEXP khm_int32 KHMAPI 
+khc_read_string(khm_handle conf, 
+                const wchar_t * value_name, 
+                wchar_t * buf, 
+                khm_size * bufsize);
+
+/*! \brief Read a multi-string value from a configuration space
+
+    The \a value_name 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.
+
+    If the value is not found in the configuration space and any
+    shadowed configuration spaces, the function returns \a
+    KHM_ERROR_NOT_FOUND.  In this case, the buffer is left unmodified.
+
+    \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_PARAM 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.
+    \retval KHM_ERROR_NOT_FOUND The value was not found.
+
+    \see khc_open_space()
+*/
+KHMEXP khm_int32 KHMAPI 
+khc_read_multi_string(khm_handle conf, 
+                      const wchar_t * value_name, 
+                      wchar_t * buf, 
+                      khm_size * bufsize);
+
+/*! \brief Read a 32 bit integer value from a configuration space
+
+    The \a value_name 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.
+
+    If the value is not found in the configuration space and any
+    shadowed configuration spaces, the function returns \a
+    KHM_ERROR_NOT_FOUND.  In this case, the buffer is left unmodified.
+
+    \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_PARAM 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, 
+               const wchar_t * value_name, 
+               khm_int32 * buf);
+
+/*! \brief Read a 64 bit integer value from a configuration space
+
+    The \a value_name 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.
+
+    If the value is not found in the configuration space and any
+    shadowed configuration spaces, the function returns \a
+    KHM_ERROR_NOT_FOUND.  In this case, the buffer is left unmodified.
+
+    \param[in] conf Handle to a configuration space
+    \param[in] value_name 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_PARAM 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, 
+               const wchar_t * value_name, 
+               khm_int64 * buf);
+
+/*! \brief Read a binary value from a configuration space
+
+    The \a value_name 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.
+
+    If the value is not found in the configuration space and any
+    shadowed configuration spaces, the function returns \a
+    KHM_ERROR_NOT_FOUND.  In this case, the buffer is left unmodified.
+
+    \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_PARAM One or more parameters were invalid.
+
+    \see khc_open_space()
+*/
+KHMEXP khm_int32 KHMAPI 
+khc_read_binary(khm_handle conf, 
+                const wchar_t * value_name, 
+                void * buf, 
+                khm_size * bufsize);
+
+/*! \brief Write a string value to a configuration space
+
+    The \a value_name 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 \a KCONF_FLAG_USER was specified, then the user configuration
+      space.
+
+    - Otherwise, if \a 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.
+
+    If the \a KCONF_FLAG_WRITEIFMOD flag is specified in the call to
+    khc_open_space() for obtaining the configuration handle, the
+    specified string will only be written if it is different from the
+    value being read from the handle.
+
+    If the \a KCONF_FLAG_IFMODCI flag is specified along with the \a
+    KCONF_FLAG_WRITEIFMOD flag, then the string comparison used will
+    be case insensitive.
+
+    \param[in] conf Handle to a configuration space
+    \param[in] value_name 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, 
+                 const wchar_t * value_name, 
+                 wchar_t * buf);
+
+/*! \brief Write a multi-string value to a configuration space
+
+    The \a value_name 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.
+
+    If the \a KCONF_FLAG_WRITEIFMOD flag is specified in the call to
+    khc_open_space() for obtaining the configuration handle, the
+    specified string will only be written if it is different from the
+    value being read from the handle.
+
+    If the \a KCONF_FLAG_IFMODCI flag is specified along with the \a
+    KCONF_FLAG_WRITEIFMOD flag, then the string comparison used will
+    be case insensitive.
+
+    \see khc_open_space()
+*/
+KHMEXP khm_int32 KHMAPI 
+khc_write_multi_string(khm_handle conf, 
+                       const wchar_t * value_name, 
+                       wchar_t * buf);
+
+/*! \brief Write a 32 bit integer value to a configuration space
+
+    The \a value_name 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.
+
+    If the \a KCONF_FLAG_WRITEIFMOD flag is specified in the call to
+    khc_open_space() for obtaining the configuration handle, the
+    specified string will only be written if it is different from the
+    value being read from the handle.
+
+    \see khc_open_space()
+*/
+KHMEXP khm_int32 KHMAPI 
+khc_write_int32(khm_handle conf, 
+                const wchar_t * value_name, 
+                khm_int32 buf);
+
+/*! \brief Write a 64 bit integer value to a configuration space
+
+    The \a value_name 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.
+
+    If the \a KCONF_FLAG_WRITEIFMOD flag is specified in the call to
+    khc_open_space() for obtaining the configuration handle, the
+    specified string will only be written if it is different from the
+    value being read from the handle.
+
+    \see khc_open_space()
+*/
+KHMEXP khm_int32 KHMAPI 
+khc_write_int64(khm_handle conf, 
+                const wchar_t * value_name, 
+                khm_int64 buf);
+
+/*! \brief Write a binary value to a configuration space
+
+    The \a value_name 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, 
+                 const wchar_t * value_name, 
+                 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, const wchar_t * value_name);
+
+/*! \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.
+
+    If the specified handle is shadowed (see khc_shadow_space()) and
+    the value is not found in any of the visible stores for the
+    topmost handle, each of the shadowed handles will be tried in turn
+    until the value is found.  The return value will correspond to the
+    handle where the value is first found.
+
+    \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, const wchar_t * value);
+
+/*! \brief Remove a value from a configuration space
+
+    Removes a value from one or more configuration stores.
+
+    A value can exist in multiple configuration stores.  Only the
+    values that are stored in writable stores can be removed.  When
+    the function searches for values to remove, it will only look in
+    configuration stores that are specified in the handle.  In
+    addition, the configuration stores affected can be further
+    narrowed by specifying them in the \a flags parameter.  If \a
+    flags is zero, then all the stores visible to the handle are
+    searched.  If \a flags specifies ::KCONF_FLAG_USER or
+    ::KCONF_FLAG_MACHINE or both, then only the specified stores are
+    searched, provided that the stores are visible to the handle.
+
+    This function only operates on the topmost configuration space
+    visible to the handle.  If the configuration handle is shadowed,
+    the shadowed configuration spaces are unaffected by the removal.
+
+    \param[in] conf Handle to configuration space to remove value from
+
+    \param[in] value_name Value to remove
+
+    \param[in] flags Specifies which configuration stores will be
+        affected by the removal.  See above.
+
+    \retval KHM_ERROR_SUCCESS The value was removed from all the
+        specified configuration stores.
+
+    \retval KHM_ERROR_NOT_FOUND The value was not found.
+
+    \retval KHM_ERROR_UNKNOWN An unknown error occurred while trying
+        to remove the value.
+
+    \retval KHM_ERROR_PARTIAL The value was successfully removed from
+        one or more stores, but the operation failed on one or more
+        other stores.
+ */
+KHMEXP khm_int32 KHMAPI
+khc_remove_value(khm_handle conf, const wchar_t * value_name, khm_int32 flags);
+
+/*! \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, 
+                const kconf_schema * schema);
+
+/*! \brief Unload a schema from a configuration space
+ */
+KHMEXP khm_int32 KHMAPI 
+khc_unload_schema(khm_handle conf, 
+                  const 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_PARAM 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);
+
+/*! \brief Remove a configuration space
+
+    The configuration space will be marked for removal.  Once all the
+    handles for the space have been released, it will be deleted.  The
+    configuration stores that will be affected are the write enabled
+    configuration stores for the handle.
+ */
+KHMEXP khm_int32 KHMAPI
+khc_remove_space(khm_handle conf);
+/*@}*/
+
+#endif
index b4c39ee2d540b9145a258d8b5b4718636a26fa8f..dc1bfa36354c88399c05c5f5c0e37978ee59589c 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KCONFIGINTERNAL_H\r
-#define __KHIMAIRA_KCONFIGINTERNAL_H\r
-\r
-#include<windows.h>\r
-#include<kconfig.h>\r
-#include<khlist.h>\r
-#include<kherror.h>\r
-#include<utils.h>\r
-#include<strsafe.h>\r
-\r
-/* TODO: Implement configuration provider interfaces\r
-\r
-typedef struct kconf_provider_t {\r
-\r
-} kconf_provider;\r
-*/\r
-\r
-typedef struct kconf_conf_space_t {\r
-    wchar_t * name;\r
-\r
-    /* kconf_provider * provider; */\r
-\r
-    /* the regpath is the cumulative path starting from a hive root */\r
-    wchar_t *   regpath;\r
-    HKEY        regkey_user;\r
-    khm_int32   regkey_user_flags;\r
-    HKEY        regkey_machine;\r
-    khm_int32   regkey_machine_flags;\r
-\r
-    khm_int32   refcount;\r
-    khm_int32   flags;\r
-\r
-    const kconf_schema * schema;\r
-    khm_int32   nSchema;\r
-\r
-    TDCL(struct kconf_conf_space_t);\r
-} kconf_conf_space;\r
-\r
-#define KCONF_SPACE_FLAG_DELETE_U 0x00000040\r
-#define KCONF_SPACE_FLAG_DELETE_M 0x00000080\r
-#define KCONF_SPACE_FLAG_DELETED  0x00000100\r
-\r
-typedef struct kconf_conf_handle_t {\r
-    khm_int32   magic;\r
-    khm_int32   flags;\r
-    kconf_conf_space * space;\r
-\r
-    struct kconf_conf_handle_t * lower;\r
-\r
-    LDCL(struct kconf_conf_handle_t);\r
-} kconf_handle;\r
-\r
-#define KCONF_HANDLE_MAGIC 0x38eb49d2\r
-#define khc_is_handle(h) ((h) && ((kconf_handle *)h)->magic == KCONF_HANDLE_MAGIC)\r
-#define khc_shadow(h) (((kconf_handle *)h)->lower)\r
-#define khc_is_shadowed(h) (khc_is_handle(h) && khc_shadow(h) != NULL)\r
-\r
-extern kconf_conf_space * conf_root;\r
-extern kconf_handle * conf_handles;\r
-extern kconf_handle * conf_free_handles;\r
-extern CRITICAL_SECTION cs_conf_global;\r
-extern LONG conf_init;\r
-extern LONG conf_status;\r
-\r
-#define khc_is_config_running() (conf_init && conf_status)\r
-\r
-#define CONFIG_REGPATHW L"Software\\MIT\\NetIDMgr"\r
-\r
-void init_kconf(void);\r
-void exit_kconf(void);\r
-\r
-/* handle operations */\r
-#define khc_space_from_handle(h)    (((kconf_handle *) h)->space)\r
-#define khc_is_schema_handle(h)     (((kconf_handle *) h)->flags & KCONF_FLAG_SCHEMA)\r
-#define khc_is_user_handle(h)       (((kconf_handle *) h)->flags & KCONF_FLAG_USER)\r
-#define khc_is_machine_handle(h)    (((kconf_handle *) h)->flags & KCONF_FLAG_MACHINE)\r
-#define khc_handle_flags(h)         (((kconf_handle *) h)->flags)\r
-\r
-kconf_handle *\r
-khcint_handle_from_space(kconf_conf_space * s, khm_int32 flags);\r
-\r
-void\r
-khcint_handle_free(kconf_handle * h);\r
-\r
-kconf_conf_space *\r
-khcint_create_empty_space(void);\r
-\r
-void\r
-khcint_free_space(kconf_conf_space * r);\r
-\r
-void\r
-khcint_space_hold(kconf_conf_space * s);\r
-\r
-void\r
-khcint_space_release(kconf_conf_space * s);\r
-\r
-HKEY\r
-khcint_space_open_key(kconf_conf_space * s, khm_int32 flags);\r
-\r
-khm_int32\r
-khcint_remove_space(kconf_conf_space * c, khm_int32 flags);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<kconfig.h>
+#include<khlist.h>
+#include<kherror.h>
+#include<utils.h>
+#include<strsafe.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;
+
+    const kconf_schema * schema;
+    khm_int32   nSchema;
+
+    TDCL(struct kconf_conf_space_t);
+} kconf_conf_space;
+
+#define KCONF_SPACE_FLAG_DELETE_U 0x00000040
+#define KCONF_SPACE_FLAG_DELETE_M 0x00000080
+#define KCONF_SPACE_FLAG_DELETED  0x00000100
+
+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 *
+khcint_handle_from_space(kconf_conf_space * s, khm_int32 flags);
+
+void
+khcint_handle_free(kconf_handle * h);
+
+kconf_conf_space *
+khcint_create_empty_space(void);
+
+void
+khcint_free_space(kconf_conf_space * r);
+
+void
+khcint_space_hold(kconf_conf_space * s);
+
+void
+khcint_space_release(kconf_conf_space * s);
+
+HKEY
+khcint_space_open_key(kconf_conf_space * s, khm_int32 flags);
+
+khm_int32
+khcint_remove_space(kconf_conf_space * c, khm_int32 flags);
+
+#endif
index c49364ab28f6dff0edf3d5c8a2ededae38f45656..bcf14625efdb2063739747bad1ce456e7b32a510 100644 (file)
@@ -1,37 +1,37 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kconfiginternal.h>\r
-\r
-void\r
-kconfig_process_attach(void) {\r
-    init_kconf();\r
-}\r
-\r
-void\r
-kconfig_process_detach(void) {\r
-    exit_kconf();\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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();
+}
index 03b49b2c032b1b09b314f3cce6ccea9794e15943..d1e4009f2accd76803d99f007970c53a553a5a95 100644 (file)
@@ -1,28 +1,28 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kconfiginternal.h>\r
-\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+
index 0652c63b983ebaf93af3f92f356f9980e87fe48e..f999dfa7109d8733ddbdc4dad51503e4619c41e2 100644 (file)
-#include<stdio.h>\r
-#include<kconfig.h>\r
-#include<strsafe.h>\r
-\r
-struct string_pair {\r
-  wchar_t * ms;\r
-  wchar_t * csv;\r
-};\r
-\r
-struct string_pair strings[] = {\r
-  {L"foo\0bar\0baz,quux\0ab\"cd\0", L"foo,bar,\"baz,quux\",\"ab\"\"cd\""},\r
-  {L"a\0b\0c\0d\0e\0", L"a,b,c,d,e"},\r
-  {L"1\0", L"1"},\r
-  {L"\0", L""},\r
-  {L"b\0a\0", L"b,a"},\r
-  {L"c\0a\0b\0", L"c,a,b"},\r
-  {L"c\0a\0B\0", L"c,a,B"},\r
-  {L"sdf\0Bar\0Foo\0BBB\0", L"sdf,Bar,Foo,BBB"}\r
-};\r
-\r
-int n_strings = ARRAYLENGTH(strings);\r
-\r
-void print_ms(wchar_t * ms) {\r
-  wchar_t * s;\r
-  size_t cch;\r
-\r
-  s = ms;\r
-  while(*s) {\r
-    printf("%S\\0", s);\r
-    StringCchLength(s, 512, &cch);\r
-    s += cch + 1;\r
-  }\r
-}\r
-\r
-int ms_to_csv_test(void) {\r
-  wchar_t wbuf[512];\r
-  int i;\r
-  khm_int32 code = 0;\r
-  size_t cbbuf;\r
-  size_t cbr;\r
-  size_t cbnull;\r
-\r
-  printf("khc_multi_string_to_csv() test:\n");\r
-\r
-  for(i=0; i<n_strings; i++) {\r
-    cbbuf = sizeof(wbuf);\r
-    printf("Multi string:[");\r
-    print_ms(strings[i].ms);\r
-    printf("]->");\r
-    code = khc_multi_string_to_csv(NULL, &cbnull, strings[i].ms);\r
-    code = khc_multi_string_to_csv(wbuf, &cbbuf, strings[i].ms);\r
-    if(code) {\r
-      printf(" returned %d\n", code);\r
-      return code;\r
-    }\r
-    printf("CSV[%S]", wbuf);\r
-    if(wcscmp(wbuf, strings[i].csv)) {\r
-      printf(" MISMATCH!");\r
-      return 1;\r
-    }\r
-\r
-    StringCbLength(wbuf, sizeof(wbuf), &cbr);\r
-    cbr+= sizeof(wchar_t);\r
-\r
-    if(cbr != cbbuf) {\r
-      printf(" Length mismatch");\r
-      return 1;\r
-    }\r
-\r
-    if(cbnull != cbr) {\r
-      printf(" NULL length mismatch");\r
-      return 1;\r
-    }\r
-\r
-    printf("\n");\r
-  }\r
-\r
-  return code;\r
-}\r
-\r
-int csv_to_ms_test(void) {\r
-  wchar_t wbuf[512];\r
-  int i;\r
-  khm_int32 code = 0;\r
-  size_t cbbuf;\r
-  size_t cbr;\r
-  size_t cbnull;\r
-\r
-  printf("khc_csv_to_multi_string() test:\n");\r
-\r
-  for(i=0; i<n_strings; i++) {\r
-    cbbuf = sizeof(wbuf);\r
-    printf("CSV:[%S]->", strings[i].csv);\r
-    code = khc_csv_to_multi_string(NULL, &cbnull, strings[i].csv);\r
-    code = khc_csv_to_multi_string(wbuf, &cbbuf, strings[i].csv);\r
-    if(code) {\r
-      printf(" returned %d\n", code);\r
-      return code;\r
-    }\r
-    printf("MS[");\r
-    print_ms(wbuf);\r
-    printf("]");\r
-\r
-    if(cbnull != cbbuf) {\r
-      printf(" NULL length mismatch");\r
-      return 1;\r
-    }\r
-\r
-    printf("\n");\r
-\r
-    printf("  Byte length:%d\n", cbbuf);\r
-  }\r
-\r
-  return code;\r
-}\r
-\r
-int ms_append_test(void)\r
-{\r
-  wchar_t wbuf[512];\r
-  size_t cbbuf;\r
-  khm_int32 code;\r
-  int i;\r
-\r
-  printf("khc_multi_string_append() test:\n");\r
-\r
-  for(i=0; i<n_strings; i++) {\r
-    cbbuf = sizeof(wbuf);\r
-    khc_csv_to_multi_string(wbuf, &cbbuf, strings[i].csv);\r
-\r
-    printf("MS[");\r
-    print_ms(wbuf);\r
-    printf("] + [foo]=[");\r
-  \r
-    cbbuf = sizeof(wbuf);\r
-    code = khc_multi_string_append(wbuf, &cbbuf, L"foo");\r
-\r
-    if(code) {\r
-      printf(" returned %d\n", code);\r
-      return code;\r
-    }\r
-\r
-    print_ms(wbuf);\r
-    printf("]\n");\r
-\r
-    printf("  byte length: %d\n", cbbuf);\r
-  }\r
-  return code;\r
-}\r
-\r
-int ms_delete_test(void)\r
-{\r
-  int code = 0;\r
-  wchar_t wbuf[512];\r
-  int i;\r
-  size_t cbs;\r
-\r
-  printf("khc_multi_string_delete() test:\n");\r
-  for(i=0; i<n_strings; i++) {\r
-    cbs = sizeof(wbuf);\r
-    khc_csv_to_multi_string(wbuf, &cbs, strings[i].csv);\r
-\r
-    printf("MS[");\r
-    print_ms(wbuf);\r
-    printf("] - [b]=[");\r
-\r
-    printf("cs:");\r
-    code = khc_multi_string_delete(wbuf, L"b", KHC_CASE_SENSITIVE);\r
-    if(code) {\r
-      printf("ci:");\r
-      code = khc_multi_string_delete(wbuf, L"b", 0);\r
-    }\r
-    if(code) {\r
-      printf("pcs:");\r
-      code = khc_multi_string_delete(wbuf, L"b", KHC_CASE_SENSITIVE | KHC_PREFIX);\r
-    }\r
-    if(code) {\r
-      printf("pci:");\r
-      code = khc_multi_string_delete(wbuf, L"b", KHC_PREFIX);\r
-    }\r
-\r
-    if(!code)\r
-      print_ms(wbuf);\r
-    else\r
-      printf(" returned %d\n", code);\r
-\r
-    printf("]\n");\r
-  }\r
-\r
-  return code;\r
-}\r
-\r
-int main(int argc, char ** argv) {\r
-\r
-  if(ms_to_csv_test())\r
-    return 1;\r
-\r
-  if(csv_to_ms_test())\r
-    return 1;\r
-\r
-  if(ms_append_test())\r
-    return 1;\r
-\r
-  if(ms_delete_test())\r
-    return 1;\r
-\r
-  return 0;\r
-}\r
+#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;
+}
index 9c892dafcb06a3ad2be0777dabc1ab63f57d335c..983e3cc98ebaacc3e46726125b409a26b0277967 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kcreddbinternal.h>\r
-#include<assert.h>\r
-\r
-CRITICAL_SECTION cs_attrib;\r
-hashtable * kcdb_attrib_namemap = NULL;\r
-kcdb_attrib_i ** kcdb_attrib_tbl = NULL;\r
-kcdb_attrib_i ** kcdb_property_tbl = NULL;\r
-kcdb_attrib_i * kcdb_attribs = NULL;\r
-\r
-void \r
-kcdb_attrib_add_ref_func(const void * key, void * va)\r
-{\r
-    kcdb_attrib_hold((kcdb_attrib_i *) va);\r
-}\r
-\r
-void \r
-kcdb_attrib_del_ref_func(const void * key, void * va)\r
-{\r
-    kcdb_attrib_release((kcdb_attrib_i *) va);\r
-}\r
-\r
-void \r
-kcdb_attrib_msg_completion(kmq_message * m) \r
-{\r
-    if(m && m->vparam) {\r
-        kcdb_attrib_release((kcdb_attrib_i *) m->vparam);\r
-    }\r
-}\r
-\r
-khm_int32 \r
-kcdb_attrib_hold(kcdb_attrib_i * ai)\r
-{\r
-    if(!ai)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_attrib);\r
-    ai->refcount++;\r
-    LeaveCriticalSection(&cs_attrib);\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-khm_int32 \r
-kcdb_attrib_release(kcdb_attrib_i * ai)\r
-{\r
-    if(!ai)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_attrib);\r
-    ai->refcount--;\r
-    LeaveCriticalSection(&cs_attrib);\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-void \r
-kcdb_attrib_post_message(khm_int32 op, kcdb_attrib_i * ai)\r
-{\r
-    kcdb_attrib_hold(ai);\r
-    kmq_post_message(KMSG_KCDB, KMSG_KCDB_ATTRIB, op, (void *) ai);\r
-}\r
-\r
-khm_int32 KHMAPI \r
-kcdb_attr_sys_cb(khm_handle vcred, \r
-                 khm_int32 attr, \r
-                 void * buf, \r
-                 khm_size * pcb_buf)\r
-{\r
-    kcdb_cred * c;\r
-\r
-    c = (kcdb_cred *) vcred;\r
-\r
-    switch(attr) {\r
-    case KCDB_ATTR_NAME:\r
-        return kcdb_cred_get_name(vcred, buf, pcb_buf);\r
-\r
-    case KCDB_ATTR_ID:\r
-        if(buf && *pcb_buf >= sizeof(khm_ui_8)) {\r
-            *pcb_buf = sizeof(khm_int64);\r
-            *((khm_ui_8 *) buf) = (khm_ui_8) c->identity;\r
-            return KHM_ERROR_SUCCESS;\r
-        } else {\r
-            *pcb_buf = sizeof(khm_ui_8);\r
-            return KHM_ERROR_TOO_LONG;\r
-        }\r
-\r
-    case KCDB_ATTR_ID_NAME:\r
-        return kcdb_identity_get_name((khm_handle) c->identity, \r
-                                      (wchar_t *) buf, pcb_buf);\r
-\r
-    case KCDB_ATTR_TYPE:\r
-        if(buf && *pcb_buf >= sizeof(khm_int32)) {\r
-            *pcb_buf = sizeof(khm_int32);\r
-            *((khm_int32 *) buf) = c->type;\r
-            return KHM_ERROR_SUCCESS;\r
-        } else {\r
-            *pcb_buf = sizeof(khm_int32);\r
-            return KHM_ERROR_TOO_LONG;\r
-        }\r
-\r
-    case KCDB_ATTR_TYPE_NAME:\r
-        return kcdb_credtype_describe(c->type, buf, \r
-                                      pcb_buf, KCDB_TS_SHORT);\r
-\r
-    case KCDB_ATTR_TIMELEFT:\r
-        {\r
-            khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-            if(!buf || *pcb_buf < sizeof(FILETIME)) {\r
-                *pcb_buf = sizeof(FILETIME);\r
-                rv = KHM_ERROR_TOO_LONG;\r
-            } else if(!kcdb_cred_buf_exist(c,KCDB_ATTR_EXPIRE)) {\r
-                *pcb_buf = sizeof(FILETIME);\r
-                /* setting the timeleft to _I64_MAX has the\r
-                   interpretation that this credential does not\r
-                   expire, which is the default behavior if the\r
-                   expiration time is not known */\r
-                *((FILETIME *) buf) = IntToFt(_I64_MAX);\r
-            } else {\r
-                FILETIME ftc;\r
-                khm_int64 iftc;\r
-\r
-                GetSystemTimeAsFileTime(&ftc);\r
-                iftc = FtToInt(&ftc);\r
-\r
-                *((FILETIME *) buf) =\r
-                    IntToFt(FtToInt((FILETIME *) \r
-                                    kcdb_cred_buf_get(c,KCDB_ATTR_EXPIRE))\r
-                            - iftc);\r
-                *pcb_buf = sizeof(FILETIME);\r
-            }\r
-\r
-            return rv;\r
-        }\r
-\r
-    case KCDB_ATTR_RENEW_TIMELEFT:\r
-        {\r
-            khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-            if(!buf || *pcb_buf < sizeof(FILETIME)) {\r
-                *pcb_buf = sizeof(FILETIME);\r
-                rv = KHM_ERROR_TOO_LONG;\r
-            } else if(!kcdb_cred_buf_exist(c,KCDB_ATTR_RENEW_EXPIRE)) {\r
-                *pcb_buf = sizeof(FILETIME);\r
-                /* setting the timeleft to _I64_MAX has the\r
-                   interpretation that this credential does not\r
-                   expire, which is the default behavior if the\r
-                   expiration time is not known */\r
-                *((FILETIME *) buf) = IntToFt(_I64_MAX);\r
-            } else {\r
-                FILETIME ftc;\r
-                khm_int64 i_re;\r
-                khm_int64 i_ct;\r
-\r
-                GetSystemTimeAsFileTime(&ftc);\r
-\r
-                i_re = FtToInt(((FILETIME *)\r
-                                kcdb_cred_buf_get(c, KCDB_ATTR_RENEW_EXPIRE)));\r
-                i_ct = FtToInt(&ftc);\r
-\r
-                if (i_re > i_ct)\r
-                    *((FILETIME *) buf) =\r
-                        IntToFt(i_re - i_ct);\r
-                else\r
-                    *((FILETIME *) buf) =\r
-                        IntToFt(0);\r
-\r
-                *pcb_buf = sizeof(FILETIME);\r
-            }\r
-\r
-            return rv;\r
-        }\r
-\r
-    case KCDB_ATTR_FLAGS:\r
-        if(buf && *pcb_buf >= sizeof(khm_int32)) {\r
-            *pcb_buf = sizeof(khm_int32);\r
-            *((khm_int32 *) buf) = c->flags;\r
-            return KHM_ERROR_SUCCESS;\r
-        } else {\r
-            *pcb_buf = sizeof(khm_int32);\r
-            return KHM_ERROR_TOO_LONG;\r
-        }\r
-\r
-    default:\r
-        return KHM_ERROR_NOT_FOUND;\r
-    }\r
-}\r
-\r
-void \r
-kcdb_attrib_init(void)\r
-{\r
-    kcdb_attrib attrib;\r
-    wchar_t sbuf[256];\r
-\r
-    InitializeCriticalSection(&cs_attrib);\r
-    kcdb_attrib_namemap = \r
-        hash_new_hashtable(KCDB_ATTRIB_HASH_SIZE,\r
-                           hash_string,\r
-                           hash_string_comp,\r
-                           kcdb_attrib_add_ref_func,\r
-                           kcdb_attrib_del_ref_func);\r
-\r
-    kcdb_attrib_tbl = \r
-        PMALLOC(sizeof(kcdb_attrib_i *) * (KCDB_ATTR_MAX_ID + 1));\r
-    assert(kcdb_attrib_tbl != NULL);\r
-    ZeroMemory(kcdb_attrib_tbl, \r
-               sizeof(kcdb_attrib_i *) * (KCDB_ATTR_MAX_ID + 1));\r
-\r
-    kcdb_property_tbl = \r
-        PMALLOC(sizeof(kcdb_attrib_i *) * KCDB_ATTR_MAX_PROPS);\r
-    assert(kcdb_property_tbl != NULL);\r
-    ZeroMemory(kcdb_property_tbl, \r
-               sizeof(kcdb_attrib_i *) * KCDB_ATTR_MAX_PROPS);\r
-\r
-    kcdb_attribs = NULL;\r
-\r
-    /* register standard attributes */\r
-    \r
-    /* Name */\r
-    attrib.id = KCDB_ATTR_NAME;\r
-    attrib.name = KCDB_ATTRNAME_NAME;\r
-    attrib.type = KCDB_TYPE_STRING;\r
-    LoadString(hinst_kcreddb, IDS_NAME, sbuf, ARRAYLENGTH(sbuf));\r
-    attrib.short_desc = sbuf;\r
-    attrib.long_desc = NULL;\r
-    attrib.flags = \r
-        KCDB_ATTR_FLAG_REQUIRED | \r
-        KCDB_ATTR_FLAG_COMPUTED | \r
-        KCDB_ATTR_FLAG_SYSTEM;\r
-    attrib.compute_cb = kcdb_attr_sys_cb;\r
-    attrib.compute_min_cbsize = sizeof(wchar_t);\r
-    attrib.compute_max_cbsize = KCDB_MAXCB_NAME;\r
-\r
-    kcdb_attrib_register(&attrib, NULL);\r
-\r
-    /* ID */\r
-    attrib.id = KCDB_ATTR_ID;\r
-    attrib.name = KCDB_ATTRNAME_ID;\r
-    attrib.type = KCDB_TYPE_INT64;\r
-    LoadString(hinst_kcreddb, IDS_IDENTITY, sbuf, ARRAYLENGTH(sbuf));\r
-    attrib.short_desc = sbuf;\r
-    attrib.long_desc = NULL;\r
-    attrib.flags = \r
-        KCDB_ATTR_FLAG_REQUIRED | \r
-        KCDB_ATTR_FLAG_COMPUTED | \r
-        KCDB_ATTR_FLAG_SYSTEM |\r
-        KCDB_ATTR_FLAG_HIDDEN;\r
-    attrib.compute_cb = kcdb_attr_sys_cb;\r
-    attrib.compute_min_cbsize = sizeof(khm_int32);\r
-    attrib.compute_max_cbsize = sizeof(khm_int32);\r
-\r
-    kcdb_attrib_register(&attrib, NULL);\r
-\r
-    /* ID Name */\r
-    attrib.id = KCDB_ATTR_ID_NAME;\r
-    attrib.alt_id = KCDB_ATTR_ID;\r
-    attrib.name = KCDB_ATTRNAME_ID_NAME;\r
-    attrib.type = KCDB_TYPE_STRING;\r
-    LoadString(hinst_kcreddb, IDS_IDENTITY, sbuf, ARRAYLENGTH(sbuf));\r
-    attrib.short_desc = sbuf;\r
-    attrib.long_desc = NULL;\r
-    attrib.flags = \r
-        KCDB_ATTR_FLAG_REQUIRED | \r
-        KCDB_ATTR_FLAG_COMPUTED | \r
-        KCDB_ATTR_FLAG_ALTVIEW |\r
-        KCDB_ATTR_FLAG_SYSTEM;\r
-    attrib.compute_cb = kcdb_attr_sys_cb;\r
-    attrib.compute_min_cbsize = sizeof(wchar_t);\r
-    attrib.compute_max_cbsize = KCDB_IDENT_MAXCB_NAME;\r
-\r
-    kcdb_attrib_register(&attrib, NULL);\r
-\r
-    /* Type */\r
-    attrib.id = KCDB_ATTR_TYPE;\r
-    attrib.name = KCDB_ATTRNAME_TYPE;\r
-    attrib.type = KCDB_TYPE_INT32;\r
-    LoadString(hinst_kcreddb, IDS_TYPE, sbuf, ARRAYLENGTH(sbuf));\r
-    attrib.short_desc = sbuf;\r
-    attrib.long_desc = NULL;\r
-    attrib.flags = \r
-        KCDB_ATTR_FLAG_REQUIRED | \r
-        KCDB_ATTR_FLAG_COMPUTED | \r
-        KCDB_ATTR_FLAG_SYSTEM |\r
-        KCDB_ATTR_FLAG_HIDDEN;\r
-    attrib.compute_cb = kcdb_attr_sys_cb;\r
-    attrib.compute_min_cbsize = sizeof(khm_int32);\r
-    attrib.compute_max_cbsize = sizeof(khm_int32);\r
-\r
-    kcdb_attrib_register(&attrib, NULL);\r
-\r
-    /* Type Name */\r
-    attrib.id = KCDB_ATTR_TYPE_NAME;\r
-    attrib.alt_id = KCDB_ATTR_TYPE;\r
-    attrib.name = KCDB_ATTRNAME_TYPE_NAME;\r
-    attrib.type = KCDB_TYPE_STRING;\r
-    LoadString(hinst_kcreddb, IDS_TYPE, sbuf, ARRAYLENGTH(sbuf));\r
-    attrib.short_desc = sbuf;\r
-    attrib.long_desc = NULL;\r
-    attrib.flags = \r
-        KCDB_ATTR_FLAG_REQUIRED | \r
-        KCDB_ATTR_FLAG_COMPUTED |\r
-        KCDB_ATTR_FLAG_ALTVIEW |\r
-        KCDB_ATTR_FLAG_SYSTEM;\r
-    attrib.compute_cb = kcdb_attr_sys_cb;\r
-    attrib.compute_min_cbsize = sizeof(wchar_t);\r
-    attrib.compute_max_cbsize = KCDB_MAXCB_NAME;\r
-\r
-    kcdb_attrib_register(&attrib, NULL);\r
-\r
-    /* Parent Name */\r
-    attrib.id = KCDB_ATTR_PARENT_NAME;\r
-    attrib.name = KCDB_ATTRNAME_PARENT_NAME;\r
-    attrib.type = KCDB_TYPE_STRING;\r
-    LoadString(hinst_kcreddb, IDS_PARENT, sbuf, ARRAYLENGTH(sbuf));\r
-    attrib.short_desc = sbuf;\r
-    attrib.long_desc = NULL;\r
-    attrib.flags = KCDB_ATTR_FLAG_SYSTEM | KCDB_ATTR_FLAG_HIDDEN;\r
-    attrib.compute_cb = NULL;\r
-    attrib.compute_min_cbsize = 0;\r
-    attrib.compute_max_cbsize = 0;\r
-\r
-    kcdb_attrib_register(&attrib, NULL);\r
-\r
-    /* Issed On */\r
-    attrib.id = KCDB_ATTR_ISSUE;\r
-    attrib.name = KCDB_ATTRNAME_ISSUE;\r
-    attrib.type = KCDB_TYPE_DATE;\r
-    LoadString(hinst_kcreddb, IDS_ISSUED, sbuf, ARRAYLENGTH(sbuf));\r
-    attrib.short_desc = sbuf;\r
-    attrib.long_desc = NULL;\r
-    attrib.flags = KCDB_ATTR_FLAG_SYSTEM;\r
-    attrib.compute_cb = NULL;\r
-    attrib.compute_min_cbsize = 0;\r
-    attrib.compute_max_cbsize = 0;\r
-\r
-    kcdb_attrib_register(&attrib, NULL);\r
-\r
-    /* Expires On */\r
-    attrib.id = KCDB_ATTR_EXPIRE;\r
-    attrib.name = KCDB_ATTRNAME_EXPIRE;\r
-    attrib.type = KCDB_TYPE_DATE;\r
-    LoadString(hinst_kcreddb, IDS_EXPIRES, sbuf, ARRAYLENGTH(sbuf));\r
-    attrib.short_desc = sbuf;\r
-    attrib.long_desc = NULL;\r
-    attrib.flags = KCDB_ATTR_FLAG_SYSTEM;\r
-    attrib.compute_cb = NULL;\r
-    attrib.compute_min_cbsize = 0;\r
-    attrib.compute_max_cbsize = 0;\r
-\r
-    kcdb_attrib_register(&attrib, NULL);\r
-\r
-    /* Renewable Time Expires On */\r
-    attrib.id = KCDB_ATTR_RENEW_EXPIRE;\r
-    attrib.name = KCDB_ATTRNAME_RENEW_EXPIRE;\r
-    attrib.type = KCDB_TYPE_DATE;\r
-    LoadString(hinst_kcreddb, IDS_RENEW_EXPIRES, \r
-               sbuf, ARRAYLENGTH(sbuf));\r
-    attrib.short_desc = sbuf;\r
-    attrib.long_desc = NULL;\r
-    attrib.flags = KCDB_ATTR_FLAG_SYSTEM;\r
-    attrib.compute_cb = NULL;\r
-    attrib.compute_min_cbsize = 0;\r
-    attrib.compute_max_cbsize = 0;\r
-\r
-    kcdb_attrib_register(&attrib, NULL);\r
-\r
-    /* Time Left */\r
-    attrib.id = KCDB_ATTR_TIMELEFT;\r
-    attrib.alt_id = KCDB_ATTR_EXPIRE;\r
-    attrib.name = KCDB_ATTRNAME_TIMELEFT;\r
-    attrib.type = KCDB_TYPE_INTERVAL;\r
-    LoadString(hinst_kcreddb, IDS_TIMELEFT, sbuf, ARRAYLENGTH(sbuf));\r
-    attrib.short_desc = sbuf;\r
-    attrib.long_desc = NULL;\r
-    attrib.flags = KCDB_ATTR_FLAG_SYSTEM |\r
-        KCDB_ATTR_FLAG_COMPUTED |\r
-        KCDB_ATTR_FLAG_ALTVIEW |\r
-        KCDB_ATTR_FLAG_VOLATILE;\r
-    attrib.compute_cb = kcdb_attr_sys_cb;\r
-    attrib.compute_min_cbsize = sizeof(FILETIME);\r
-    attrib.compute_max_cbsize = sizeof(FILETIME);\r
-\r
-    kcdb_attrib_register(&attrib, NULL);\r
-\r
-    /* Renewable Time Left */\r
-    attrib.id = KCDB_ATTR_RENEW_TIMELEFT;\r
-    attrib.alt_id = KCDB_ATTR_RENEW_EXPIRE;\r
-    attrib.name = KCDB_ATTRNAME_RENEW_TIMELEFT;\r
-    attrib.type = KCDB_TYPE_INTERVAL;\r
-    LoadString(hinst_kcreddb, \r
-               IDS_RENEW_TIMELEFT, sbuf, ARRAYLENGTH(sbuf));\r
-    attrib.short_desc = sbuf;\r
-    attrib.long_desc = NULL;\r
-    attrib.flags = KCDB_ATTR_FLAG_SYSTEM |\r
-        KCDB_ATTR_FLAG_COMPUTED |\r
-        KCDB_ATTR_FLAG_ALTVIEW |\r
-        KCDB_ATTR_FLAG_VOLATILE;\r
-    attrib.compute_cb = kcdb_attr_sys_cb;\r
-    attrib.compute_min_cbsize = sizeof(FILETIME);\r
-    attrib.compute_max_cbsize = sizeof(FILETIME);\r
-\r
-    kcdb_attrib_register(&attrib, NULL);\r
-\r
-    /* Location of Credential */\r
-    attrib.id = KCDB_ATTR_LOCATION;\r
-    attrib.name = KCDB_ATTRNAME_LOCATION;\r
-    attrib.type = KCDB_TYPE_STRING;\r
-    LoadString(hinst_kcreddb, IDS_LOCATION, sbuf, ARRAYLENGTH(sbuf));\r
-    attrib.short_desc = sbuf;\r
-    attrib.long_desc = NULL;\r
-    attrib.flags = KCDB_ATTR_FLAG_SYSTEM;\r
-    attrib.compute_cb = NULL;\r
-    attrib.compute_min_cbsize = 0;\r
-    attrib.compute_max_cbsize = 0;\r
-\r
-    kcdb_attrib_register(&attrib, NULL);\r
-\r
-    /* Lifetime */\r
-    attrib.id = KCDB_ATTR_LIFETIME;\r
-    attrib.name = KCDB_ATTRNAME_LIFETIME;\r
-    attrib.type = KCDB_TYPE_INTERVAL;\r
-    LoadString(hinst_kcreddb, IDS_LIFETIME, sbuf, ARRAYLENGTH(sbuf));\r
-    attrib.short_desc = sbuf;\r
-    attrib.long_desc = NULL;\r
-    attrib.flags = KCDB_ATTR_FLAG_SYSTEM;\r
-    attrib.compute_cb = NULL;\r
-    attrib.compute_min_cbsize = 0;\r
-    attrib.compute_max_cbsize = 0;\r
-\r
-    kcdb_attrib_register(&attrib, NULL);\r
-\r
-    /* Renewable Lifetime */\r
-    attrib.id = KCDB_ATTR_RENEW_LIFETIME;\r
-    attrib.name = KCDB_ATTRNAME_RENEW_LIFETIME;\r
-    attrib.type = KCDB_TYPE_INTERVAL;\r
-    LoadString(hinst_kcreddb, \r
-               IDS_RENEW_LIFETIME, sbuf, ARRAYLENGTH(sbuf));\r
-    attrib.short_desc = sbuf;\r
-    attrib.long_desc = NULL;\r
-    attrib.flags = KCDB_ATTR_FLAG_SYSTEM;\r
-    attrib.compute_cb = NULL;\r
-    attrib.compute_min_cbsize = 0;\r
-    attrib.compute_max_cbsize = 0;\r
-\r
-    kcdb_attrib_register(&attrib, NULL);\r
-\r
-    /* Flags */\r
-    attrib.id = KCDB_ATTR_FLAGS;\r
-    attrib.name = KCDB_ATTRNAME_FLAGS;\r
-    attrib.type = KCDB_TYPE_INT32;\r
-    LoadString(hinst_kcreddb, IDS_FLAGS, sbuf, ARRAYLENGTH(sbuf));\r
-    attrib.short_desc = sbuf;\r
-    attrib.long_desc = NULL;\r
-    attrib.flags = \r
-        KCDB_ATTR_FLAG_REQUIRED | \r
-        KCDB_ATTR_FLAG_COMPUTED | \r
-        KCDB_ATTR_FLAG_SYSTEM |\r
-        KCDB_ATTR_FLAG_HIDDEN;\r
-    attrib.compute_cb = kcdb_attr_sys_cb;\r
-    attrib.compute_min_cbsize = sizeof(khm_int32);\r
-    attrib.compute_max_cbsize = sizeof(khm_int32);\r
-\r
-    kcdb_attrib_register(&attrib, NULL);\r
-}\r
-\r
-void \r
-kcdb_attrib_exit(void)\r
-{\r
-    DeleteCriticalSection(&cs_attrib);\r
-    \r
-    if(kcdb_attrib_tbl)\r
-        PFREE(kcdb_attrib_tbl);\r
-\r
-    if(kcdb_property_tbl)\r
-        PFREE(kcdb_property_tbl);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_attrib_get_id(const wchar_t *name, khm_int32 * id)\r
-{\r
-    kcdb_attrib_i * ai;\r
-\r
-    if(!name)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_attrib);\r
-    ai = hash_lookup(kcdb_attrib_namemap, (void *) name);\r
-    LeaveCriticalSection(&cs_attrib);\r
-\r
-    if(ai) {\r
-        *id = ai->attr.id;\r
-        return KHM_ERROR_SUCCESS;\r
-    } else {\r
-        *id = KCDB_ATTR_INVALID;\r
-        return KHM_ERROR_NOT_FOUND;\r
-    }\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_attrib_register(const kcdb_attrib * attrib, khm_int32 * new_id)\r
-{\r
-    kcdb_attrib_i * ai;\r
-    size_t cb_name;\r
-    size_t cb_short_desc;\r
-    size_t cb_long_desc;\r
-    khm_int32 attr_id;\r
-    khm_boolean prop = FALSE;\r
-\r
-    if(!attrib ||\r
-        KHM_FAILED(kcdb_type_get_info(attrib->type, NULL)) ||\r
-        !attrib->name)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(FAILED(StringCbLength(attrib->name, KCDB_MAXCB_NAME, &cb_name)))\r
-        return KHM_ERROR_TOO_LONG;\r
-    cb_name += sizeof(wchar_t);\r
-\r
-    if(attrib->short_desc) {\r
-        if(FAILED(StringCbLength(attrib->short_desc, KCDB_MAXCB_SHORT_DESC, &cb_short_desc)))\r
-            return KHM_ERROR_TOO_LONG;\r
-        cb_short_desc += sizeof(wchar_t);\r
-    } else\r
-        cb_short_desc = 0;\r
-\r
-    if(attrib->long_desc) {\r
-        if(FAILED(StringCbLength(attrib->long_desc, KCDB_MAXCB_LONG_DESC, &cb_long_desc)))\r
-            return KHM_ERROR_TOO_LONG;\r
-        cb_long_desc += sizeof(wchar_t);\r
-    } else\r
-        cb_long_desc = 0;\r
-\r
-    if((attrib->flags & KCDB_ATTR_FLAG_COMPUTED) && \r
-        (!attrib->compute_cb ||\r
-        attrib->compute_min_cbsize <= 0 ||\r
-        attrib->compute_max_cbsize < attrib->compute_min_cbsize))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if ((attrib->flags & KCDB_ATTR_FLAG_ALTVIEW) &&\r
-        KHM_FAILED(kcdb_attrib_get_info(attrib->alt_id,\r
-                                        NULL)))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    prop = !!(attrib->flags & KCDB_ATTR_FLAG_PROPERTY);\r
-\r
-    EnterCriticalSection(&cs_attrib);\r
-\r
-    if(!prop && \r
-       (attrib->id < 0 || attrib->id > KCDB_ATTR_MAX_ID)) \r
-    {\r
-        if(KHM_FAILED(kcdb_attrib_next_free_id(&attr_id))) {\r
-            LeaveCriticalSection(&cs_attrib);\r
-            return KHM_ERROR_NO_RESOURCES;\r
-        }\r
-    } else if (prop &&\r
-               (attrib->id < KCDB_ATTR_MIN_PROP_ID || \r
-                attrib->id > KCDB_ATTR_MAX_PROP_ID)) {\r
-\r
-        if(KHM_FAILED(kcdb_attrib_next_free_prop_id(&attr_id))) {\r
-            LeaveCriticalSection(&cs_attrib);\r
-            return KHM_ERROR_NO_RESOURCES;\r
-        }\r
-\r
-    } else {\r
-        attr_id = attrib->id;\r
-    }\r
-\r
-#ifdef DEBUG\r
-    assert(!prop || (attr_id >= KCDB_ATTR_MIN_PROP_ID && attr_id <= KCDB_ATTR_MAX_PROP_ID));\r
-    assert(prop  || (attr_id >= 0 && attr_id <= KCDB_ATTR_MAX_ID));\r
-#endif\r
-\r
-    if((!prop && kcdb_attrib_tbl[attr_id]) ||\r
-       (prop && kcdb_property_tbl[attr_id - KCDB_ATTR_MIN_PROP_ID])) {\r
-\r
-        LeaveCriticalSection(&cs_attrib);\r
-        return KHM_ERROR_DUPLICATE;\r
-\r
-    }\r
-\r
-    ai = PMALLOC(sizeof(kcdb_attrib_i));\r
-    ZeroMemory(ai, sizeof(kcdb_attrib_i));\r
-\r
-    ai->attr.type = attrib->type;\r
-    ai->attr.id = attr_id;\r
-    ai->attr.alt_id = attrib->alt_id;\r
-    ai->attr.flags = attrib->flags;\r
-    ai->attr.compute_cb = attrib->compute_cb;\r
-    ai->attr.compute_max_cbsize = attrib->compute_max_cbsize;\r
-    ai->attr.compute_min_cbsize = attrib->compute_min_cbsize;\r
-    ai->attr.name = PMALLOC(cb_name);\r
-    StringCbCopy(ai->attr.name, cb_name, attrib->name);\r
-    if(cb_short_desc) {\r
-        ai->attr.short_desc = PMALLOC(cb_short_desc);\r
-        StringCbCopy(ai->attr.short_desc, cb_short_desc, attrib->short_desc);\r
-    }\r
-    if(cb_long_desc) {\r
-        ai->attr.long_desc = PMALLOC(cb_long_desc);\r
-        StringCbCopy(ai->attr.long_desc, cb_long_desc, attrib->long_desc);\r
-    }\r
-\r
-    LINIT(ai);\r
-\r
-    if(!prop)\r
-        kcdb_attrib_tbl[attr_id] = ai;\r
-    else\r
-        kcdb_property_tbl[attr_id - KCDB_ATTR_MIN_PROP_ID] = ai;\r
-\r
-    LPUSH(&kcdb_attribs, ai);\r
-\r
-    hash_add(kcdb_attrib_namemap, (void *) ai->attr.name, ai);\r
-\r
-    LeaveCriticalSection(&cs_attrib);\r
-\r
-    kcdb_attrib_post_message(KCDB_OP_INSERT, ai);\r
-\r
-    if(new_id)\r
-        *new_id = attr_id;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_attrib_get_info(\r
-    khm_int32 id, \r
-    kcdb_attrib ** attrib)\r
-{\r
-    kcdb_attrib_i * ai;\r
-    khm_boolean prop;\r
-\r
-    if(id >= 0 && id <= KCDB_ATTR_MAX_ID)\r
-        prop = FALSE;\r
-    else if(id >= KCDB_ATTR_MIN_PROP_ID && id <= KCDB_ATTR_MAX_PROP_ID)\r
-        prop = TRUE;\r
-    else\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_attrib);\r
-    if(prop)\r
-        ai = kcdb_property_tbl[id - KCDB_ATTR_MIN_PROP_ID];\r
-    else\r
-        ai = kcdb_attrib_tbl[id];\r
-    LeaveCriticalSection(&cs_attrib);\r
-\r
-    if(ai) {\r
-        if(attrib) {\r
-            *attrib = &(ai->attr);\r
-            kcdb_attrib_hold(ai);\r
-        }\r
-        return KHM_ERROR_SUCCESS;\r
-    } else {\r
-        if(attrib)\r
-            *attrib = NULL;\r
-        return KHM_ERROR_NOT_FOUND;\r
-    }\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_attrib_release_info(kcdb_attrib * attrib)\r
-{\r
-    if(attrib)\r
-        kcdb_attrib_release((kcdb_attrib_i *) attrib);\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_attrib_unregister(khm_int32 id)\r
-{\r
-    /*TODO: implement this */\r
-    return KHM_ERROR_NOT_IMPLEMENTED;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_attrib_describe(\r
-    khm_int32 id, \r
-    wchar_t * buffer, \r
-    khm_size * cbsize, \r
-    khm_int32 flags)\r
-{\r
-    kcdb_attrib_i * ai;\r
-    size_t cb_size = 0;\r
-    khm_boolean prop = FALSE;\r
-\r
-    if(!cbsize)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(id >= 0 && id <= KCDB_ATTR_MAX_ID)\r
-        prop = FALSE;\r
-    else if(id >= KCDB_ATTR_MIN_PROP_ID && id <= KCDB_ATTR_MAX_PROP_ID)\r
-        prop = TRUE;\r
-    else \r
-       return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(prop)\r
-        ai = kcdb_property_tbl[id - KCDB_ATTR_MIN_PROP_ID];\r
-    else\r
-        ai = kcdb_attrib_tbl[id];\r
-\r
-    if(!ai)\r
-        return KHM_ERROR_NOT_FOUND;\r
-\r
-    if((flags & KCDB_TS_SHORT) &&\r
-        ai->attr.short_desc) \r
-    {\r
-        if(FAILED(StringCbLength(ai->attr.short_desc, KCDB_MAXCB_SHORT_DESC, &cb_size)))\r
-            return KHM_ERROR_UNKNOWN;\r
-        cb_size += sizeof(wchar_t);\r
-\r
-        if(!buffer || *cbsize < cb_size) {\r
-            *cbsize = cb_size;\r
-            return KHM_ERROR_TOO_LONG;\r
-        }\r
-\r
-        StringCbCopy(buffer, *cbsize, ai->attr.short_desc);\r
-\r
-        *cbsize = cb_size;\r
-\r
-        return KHM_ERROR_SUCCESS;\r
-    } else {\r
-        if(FAILED(StringCbLength(ai->attr.long_desc, KCDB_MAXCB_LONG_DESC, &cb_size)))\r
-            return KHM_ERROR_UNKNOWN;\r
-        cb_size += sizeof(wchar_t);\r
-\r
-        if(!buffer || *cbsize < cb_size) {\r
-            *cbsize = cb_size;\r
-            return KHM_ERROR_TOO_LONG;\r
-        }\r
-\r
-        StringCbCopy(buffer, *cbsize, ai->attr.long_desc);\r
-\r
-        *cbsize = cb_size;\r
-\r
-        return KHM_ERROR_SUCCESS;\r
-    }\r
-}\r
-\r
-khm_int32 kcdb_attrib_next_free_prop_id(khm_int32 * id)\r
-{\r
-    int i;\r
-\r
-    if(!id)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_attrib);\r
-    for(i=0;i < KCDB_ATTR_MAX_PROPS; i++) {\r
-        if(!kcdb_property_tbl[i])\r
-            break;\r
-    }\r
-    LeaveCriticalSection(&cs_attrib);\r
-\r
-    if(i < KCDB_ATTR_MAX_PROPS) {\r
-        *id = i + KCDB_ATTR_MIN_PROP_ID;\r
-        return KHM_ERROR_SUCCESS;\r
-    } else {\r
-        *id = KCDB_ATTR_INVALID;\r
-        return KHM_ERROR_NO_RESOURCES;\r
-    }\r
-}\r
-\r
-khm_int32 kcdb_attrib_next_free_id(khm_int32 * id)\r
-{\r
-    int i;\r
-\r
-    if(!id)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_attrib);\r
-    for(i=0;i<= KCDB_ATTR_MAX_ID; i++) {\r
-        if(!kcdb_attrib_tbl[i])\r
-            break;\r
-    }\r
-    LeaveCriticalSection(&cs_attrib);\r
-\r
-    if(i <= KCDB_ATTR_MAX_ID) {\r
-        *id = i;\r
-        return KHM_ERROR_SUCCESS;\r
-    } else {\r
-        *id = KCDB_ATTR_INVALID;\r
-        return KHM_ERROR_NO_RESOURCES;\r
-    }\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_attrib_get_count(\r
-    khm_int32 and_flags,\r
-    khm_int32 eq_flags,\r
-    khm_size * pcount)\r
-{\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    khm_size count = 0;\r
-    int i;\r
-\r
-    if(pcount == NULL)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    eq_flags &= and_flags;\r
-\r
-    EnterCriticalSection(&cs_attrib);\r
-    for(i = 0; i <= KCDB_ATTR_MAX_ID; i++) {\r
-        if(kcdb_attrib_tbl[i] &&\r
-            (kcdb_attrib_tbl[i]->attr.flags & and_flags) == eq_flags)\r
-            count++;\r
-    }\r
-\r
-    for(i = 0; i < KCDB_ATTR_MAX_PROPS; i++) {\r
-        if(kcdb_property_tbl[i] &&\r
-            (kcdb_property_tbl[i]->attr.flags & and_flags) == eq_flags)\r
-            count++;\r
-    }\r
-    LeaveCriticalSection(&cs_attrib);\r
-\r
-    *pcount = count;\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_attrib_get_ids(\r
-    khm_int32 and_flags,\r
-    khm_int32 eq_flags,\r
-    khm_int32 * plist,\r
-    khm_size * pcsize)\r
-{\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    khm_size count = 0;\r
-    int i;\r
-\r
-    if(plist == NULL || pcsize == NULL)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    eq_flags &= and_flags;\r
-\r
-    EnterCriticalSection(&cs_attrib);\r
-    for(i = 0; i <= KCDB_ATTR_MAX_ID; i++) {\r
-        if(kcdb_attrib_tbl[i] &&\r
-            (kcdb_attrib_tbl[i]->attr.flags & and_flags) == eq_flags) {\r
-            if(count >= *pcsize) {\r
-                rv = KHM_ERROR_TOO_LONG;\r
-                count++;\r
-            } else\r
-                plist[count++] = i;\r
-        }\r
-    }\r
-\r
-    for(i = 0; i < KCDB_ATTR_MAX_PROPS; i++) {\r
-        if(kcdb_property_tbl[i] &&\r
-            (kcdb_property_tbl[i]->attr.flags & and_flags) == eq_flags) {\r
-            if(count >= *pcsize) {\r
-                rv = KHM_ERROR_TOO_LONG;\r
-                count++;\r
-            } else\r
-                plist[count++] = i + KCDB_ATTR_MIN_PROP_ID;\r
-        }\r
-    }\r
-    LeaveCriticalSection(&cs_attrib);\r
-\r
-    *pcsize = count;\r
-\r
-    return rv;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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_PARAM;
+
+    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_PARAM;
+
+    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:
+        {
+            khm_int32 rv = KHM_ERROR_SUCCESS;
+
+            if(!buf || *pcb_buf < sizeof(FILETIME)) {
+                *pcb_buf = sizeof(FILETIME);
+                rv = KHM_ERROR_TOO_LONG;
+            } else if(!kcdb_cred_buf_exist(c,KCDB_ATTR_EXPIRE)) {
+                *pcb_buf = sizeof(FILETIME);
+                /* 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 */
+                *((FILETIME *) buf) = IntToFt(_I64_MAX);
+            } else {
+                FILETIME ftc;
+                khm_int64 iftc;
+
+                GetSystemTimeAsFileTime(&ftc);
+                iftc = FtToInt(&ftc);
+
+                *((FILETIME *) buf) =
+                    IntToFt(FtToInt((FILETIME *) 
+                                    kcdb_cred_buf_get(c,KCDB_ATTR_EXPIRE))
+                            - iftc);
+                *pcb_buf = sizeof(FILETIME);
+            }
+
+            return rv;
+        }
+
+    case KCDB_ATTR_RENEW_TIMELEFT:
+        {
+            khm_int32 rv = KHM_ERROR_SUCCESS;
+
+            if(!buf || *pcb_buf < sizeof(FILETIME)) {
+                *pcb_buf = sizeof(FILETIME);
+                rv = KHM_ERROR_TOO_LONG;
+            } else if(!kcdb_cred_buf_exist(c,KCDB_ATTR_RENEW_EXPIRE)) {
+                *pcb_buf = sizeof(FILETIME);
+                /* 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 */
+                *((FILETIME *) buf) = IntToFt(_I64_MAX);
+            } else {
+                FILETIME ftc;
+                khm_int64 i_re;
+                khm_int64 i_ct;
+
+                GetSystemTimeAsFileTime(&ftc);
+
+                i_re = FtToInt(((FILETIME *)
+                                kcdb_cred_buf_get(c, KCDB_ATTR_RENEW_EXPIRE)));
+                i_ct = FtToInt(&ftc);
+
+                if (i_re > i_ct)
+                    *((FILETIME *) buf) =
+                        IntToFt(i_re - i_ct);
+                else
+                    *((FILETIME *) buf) =
+                        IntToFt(0);
+
+                *pcb_buf = sizeof(FILETIME);
+            }
+
+            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 = 
+        PMALLOC(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 = 
+        PMALLOC(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 | KCDB_ATTR_FLAG_HIDDEN;
+    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(FILETIME);
+    attrib.compute_max_cbsize = sizeof(FILETIME);
+
+    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(FILETIME);
+    attrib.compute_max_cbsize = sizeof(FILETIME);
+
+    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)
+        PFREE(kcdb_attrib_tbl);
+
+    if(kcdb_property_tbl)
+        PFREE(kcdb_property_tbl);
+}
+
+KHMEXP khm_int32 KHMAPI 
+kcdb_attrib_get_id(const wchar_t *name, khm_int32 * id)
+{
+    kcdb_attrib_i * ai;
+
+    if(!name)
+        return KHM_ERROR_INVALID_PARAM;
+
+    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(const 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_PARAM;
+
+    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_PARAM;
+
+    if ((attrib->flags & KCDB_ATTR_FLAG_ALTVIEW) &&
+        KHM_FAILED(kcdb_attrib_get_info(attrib->alt_id,
+                                        NULL)))
+        return KHM_ERROR_INVALID_PARAM;
+
+    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 = PMALLOC(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 = PMALLOC(cb_name);
+    StringCbCopy(ai->attr.name, cb_name, attrib->name);
+    if(cb_short_desc) {
+        ai->attr.short_desc = PMALLOC(cb_short_desc);
+        StringCbCopy(ai->attr.short_desc, cb_short_desc, attrib->short_desc);
+    }
+    if(cb_long_desc) {
+        ai->attr.long_desc = PMALLOC(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_PARAM;
+
+    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 = FALSE;
+
+    if(!cbsize)
+        return KHM_ERROR_INVALID_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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;
+}
index f5647d67a670b7077b53d18124d23d9f60a1b0ee..2f0cc0f8e8b9fb43d5a15cb83589f7a247e44912 100644 (file)
@@ -1,55 +1,55 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KCDB_ATTRIB_H\r
-#define __KHIMAIRA_KCDB_ATTRIB_H\r
-\r
-/* Attributes */\r
-\r
-typedef struct kcdb_attrib_i_t {\r
-    kcdb_attrib attr;\r
-\r
-    khm_int32 refcount;\r
-\r
-    struct kcdb_attrib_i_t * next;\r
-    struct kcdb_attrib_i_t * prev;\r
-} kcdb_attrib_i;\r
-\r
-#define KCDB_ATTRIB_HASH_SIZE 31\r
-\r
-void kcdb_attrib_init(void);\r
-void kcdb_attrib_exit(void);\r
-void kcdb_attrib_add_ref_func(const void * key, void * va);\r
-void kcdb_attrib_del_ref_func(const void * key, void * va);\r
-void kcdb_attrib_msg_completion(kmq_message * m);\r
-khm_int32 kcdb_attrib_next_free_prop_id(khm_int32 * id);\r
-khm_int32 kcdb_attrib_next_free_id(khm_int32 * id);\r
-khm_int32 kcdb_attrib_hold(kcdb_attrib_i * ai);\r
-khm_int32 kcdb_attrib_release(kcdb_attrib_i * ai);\r
-void kcdb_attrib_post_message(khm_int32 op, kcdb_attrib_i * ai);\r
-khm_int32 KHMAPI kcdb_attr_sys_cb(khm_handle cred, khm_int32 attr, void * buf, khm_size * pcb_buf);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
index 6272924e5c993798bec18340452e70cfd4109fc7..521baeb976703ef219481db3b0918930e394dbcb 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kcreddbinternal.h>\r
-#include<assert.h>\r
-\r
-void kcdb_buf_new(kcdb_buf * buf, khm_size n_fields)\r
-{\r
-    buf->buffer = PMALLOC(KCDB_BUF_CBBUF_INITIAL);\r
-    buf->cb_buffer = KCDB_BUF_CBBUF_INITIAL;\r
-    buf->cb_used = 0;\r
-\r
-    if(n_fields == KCDB_BUF_DEFAULT)\r
-        n_fields = KCDB_BUF_FIELDS_INITIAL;\r
-\r
-    assert(n_fields < KCDB_BUF_MAX_SLOTS);\r
-\r
-    buf->n_fields = n_fields;\r
-    buf->nc_fields = UBOUNDSS(n_fields, KCDB_BUF_FIELDS_INITIAL, KCDB_BUF_FIELDS_GROWTH);\r
-    buf->fields = PMALLOC(sizeof(buf->fields[0]) * buf->n_fields);\r
-    ZeroMemory(buf->fields, sizeof(buf->fields[0]) * buf->n_fields);\r
-}\r
-\r
-void kcdb_buf_delete(kcdb_buf * buf)\r
-{\r
-    buf->cb_buffer = 0;\r
-    buf->cb_used = 0;\r
-    if(buf->buffer)\r
-        PFREE(buf->buffer);\r
-    buf->buffer = NULL;\r
-\r
-    buf->n_fields = 0;\r
-    buf->nc_fields = 0;\r
-    if(buf->fields)\r
-        PFREE(buf->fields);\r
-    buf->fields = NULL;\r
-}\r
-\r
-static void kcdb_buf_assert_size(kcdb_buf * buf, khm_size cbsize)\r
-{\r
-    khm_size new_size;\r
-    void * new_buf;\r
-\r
-    /* should be less than or equal to the max signed 32 bit int */\r
-    assert(cbsize <= KHM_INT32_MAX);\r
-    if(cbsize <= buf->cb_buffer)\r
-        return;\r
-\r
-    new_size = UBOUNDSS(cbsize, KCDB_BUF_CBBUF_INITIAL, KCDB_BUF_CBBUF_GROWTH);\r
-\r
-    assert(new_size > buf->cb_buffer && new_size > 0);\r
-\r
-    new_buf = PMALLOC(new_size);\r
-    assert(new_buf != NULL);\r
-\r
-    memcpy(new_buf, buf->buffer, buf->cb_used);\r
-    PFREE(buf->buffer);\r
-    buf->buffer = new_buf;\r
-}\r
-\r
-void kcdb_buf_alloc(kcdb_buf * buf, khm_size slot, khm_ui_2 id, khm_size cbsize)\r
-{\r
-    khm_size cbnew;\r
-    khm_ssize cbdelta;\r
-    khm_size cbold;\r
-    kcdb_buf_field * f;\r
-\r
-    cbnew = UBOUND32(cbsize);\r
-\r
-    assert(slot <= KCDB_BUF_APPEND);\r
-\r
-    if(slot == KCDB_BUF_APPEND) {\r
-        slot = kcdb_buf_slot_by_id(buf, id);\r
-        if(slot == KCDB_BUF_INVALID_SLOT)\r
-            slot = buf->n_fields;\r
-    }\r
-\r
-    assert(slot < KCDB_BUF_MAX_SLOTS);\r
-\r
-    if((slot + 1) > buf->nc_fields) {\r
-        kcdb_buf_field * nf;\r
-        khm_size ns;\r
-\r
-        ns = UBOUNDSS((slot + 1), KCDB_BUF_FIELDS_INITIAL, KCDB_BUF_FIELDS_GROWTH);\r
-\r
-        nf = PMALLOC(sizeof(buf->fields[0]) * ns);\r
-        memcpy(nf, buf->fields, sizeof(buf->fields[0]) * buf->n_fields);\r
-\r
-        if(ns > buf->n_fields)\r
-            memset(&(nf[buf->n_fields]), 0, sizeof(buf->fields[0]) * (ns - buf->n_fields));\r
-\r
-        PFREE(buf->fields);\r
-        buf->fields = nf;\r
-        buf->nc_fields = ns;\r
-    }\r
-\r
-    if((slot + 1) > buf->n_fields)\r
-        buf->n_fields = slot + 1;\r
-\r
-    f = &(buf->fields[slot]);\r
-\r
-    if(f->flags & KCDB_CREDF_FLAG_ALLOCD) {\r
-        /* there's already an allocation.  we have to resize it to\r
-           accomodate the new size */\r
-        cbold = UBOUND32(f->cbsize);\r
-        /* demote before substraction */\r
-        cbdelta = ((khm_ssize) cbnew) - (khm_ssize) cbold;\r
-\r
-        if(cbnew > cbold) {\r
-            kcdb_buf_assert_size(buf, buf->cb_used + cbdelta);\r
-        }\r
-\r
-        if(buf->cb_used > f->offset + cbold) {\r
-            khm_size i;\r
-\r
-            memmove(\r
-                ((BYTE *) buf->buffer) + (f->offset + cbnew),\r
-                ((BYTE *) buf->buffer) + (f->offset + cbold),\r
-                buf->cb_used - (f->offset + cbold));\r
-\r
-            for(i=0; i < (int) buf->n_fields; i++) {\r
-                if(i != slot && \r
-                    (buf->fields[i].flags & KCDB_CREDF_FLAG_ALLOCD) &&\r
-                    buf->fields[i].offset > f->offset) \r
-                {\r
-                    buf->fields[i].offset = \r
-                        (khm_ui_4)(((khm_ssize) buf->fields[i].offset) + cbdelta);\r
-                }\r
-            }\r
-        }\r
-\r
-        /* demote integer before adding signed quantity */\r
-        buf->cb_used = (khm_size)(((khm_ssize) buf->cb_used) + cbdelta);\r
-\r
-        f->cbsize = (khm_ui_4) cbsize;\r
-\r
-    } else {\r
-        kcdb_buf_assert_size(buf, buf->cb_used + cbnew);\r
-        f->offset = (khm_ui_4) buf->cb_used;\r
-        f->cbsize = (khm_ui_4) cbsize;\r
-        buf->cb_used += cbnew;\r
-    }\r
-\r
-    if(cbsize == 0) {\r
-        f->flags &= ~KCDB_CREDF_FLAG_ALLOCD;\r
-        f->flags &= ~KCDB_CREDF_FLAG_DATA;\r
-        f->id = KCDB_BUFF_ID_INVALID;\r
-    } else {\r
-        f->flags |= KCDB_CREDF_FLAG_ALLOCD;\r
-        f->id = id;\r
-    }\r
-}\r
-\r
-void kcdb_buf_dup(kcdb_buf * dest, const kcdb_buf * src)\r
-{\r
-    khm_size cb_buf;\r
-    khm_size nc_fields;\r
-\r
-    cb_buf = UBOUNDSS(src->cb_used, KCDB_BUF_CBBUF_INITIAL, KCDB_BUF_CBBUF_GROWTH);\r
-#if 0\r
-        /* replaced by UBOUNDSS() above */\r
-        (src->cb_used <= kcdb_cred_initial_size)? kcdb_cred_initial_size:\r
-        kcdb_cred_initial_size + \r
-            (((src->cb_used - (kcdb_cred_initial_size + 1)) / kcdb_cred_growth_factor + 1) * kcdb_cred_growth_factor);\r
-#endif\r
-\r
-    kcdb_buf_delete(dest);\r
-\r
-    dest->cb_buffer = cb_buf;\r
-    dest->cb_used = src->cb_used;\r
-    dest->buffer = PMALLOC(cb_buf);\r
-    memcpy(dest->buffer, src->buffer, src->cb_used);\r
-\r
-    nc_fields = UBOUNDSS(src->n_fields, KCDB_BUF_FIELDS_INITIAL, KCDB_BUF_FIELDS_GROWTH);\r
-    dest->nc_fields = nc_fields;\r
-    dest->n_fields = src->n_fields;\r
-    dest->fields = PMALLOC(nc_fields * sizeof(dest->fields[0]));\r
-    memcpy(dest->fields, src->fields, src->n_fields * sizeof(dest->fields[0]));\r
-    if(dest->n_fields < dest->nc_fields)\r
-        memset(&(dest->fields[dest->n_fields]), 0, (src->nc_fields - src->n_fields) * sizeof(dest->fields[0]));\r
-}\r
-\r
-void kcdb_buf_set_value(kcdb_buf * buf, khm_size slot, khm_ui_2 id, void * src, khm_size cb_src)\r
-{\r
-    void * dest;\r
-    kcdb_buf_alloc(buf, slot, id, cb_src);\r
-    if(slot == KCDB_BUF_APPEND) {\r
-        slot = kcdb_buf_slot_by_id(buf, id);\r
-        if(slot == KCDB_BUF_INVALID_SLOT) {\r
-#ifdef DEBUG\r
-            assert(FALSE);\r
-#else\r
-            return;\r
-#endif\r
-        }\r
-    }\r
-    if(kcdb_buf_exist(buf, slot)) {\r
-        dest = kcdb_buf_get(buf, slot);\r
-        memcpy(dest, src, cb_src);\r
-\r
-        buf->fields[slot].flags |= KCDB_CREDF_FLAG_DATA;\r
-    }\r
-}\r
-\r
-int kcdb_buf_exist(kcdb_buf * buf, khm_size slot)\r
-{\r
-    if(slot >= buf->n_fields)\r
-        return 0;\r
-    return (buf->fields[slot].flags & KCDB_CREDF_FLAG_ALLOCD);\r
-}\r
-\r
-int kcdb_buf_val_exist(kcdb_buf * buf, khm_size slot)\r
-{\r
-    if(slot >= buf->n_fields)\r
-        return 0;\r
-    return (buf->fields[slot].flags & KCDB_CREDF_FLAG_DATA);\r
-}\r
-\r
-void * kcdb_buf_get(kcdb_buf * buf, khm_size slot)\r
-{\r
-    if(slot >= buf->n_fields || \r
-        !(buf->fields[slot].flags & KCDB_CREDF_FLAG_ALLOCD))\r
-        return NULL;\r
-    return (((BYTE *) buf->buffer) + buf->fields[slot].offset);\r
-}\r
-\r
-khm_size kcdb_buf_size(kcdb_buf * buf, khm_size slot)\r
-{\r
-    if(slot >= buf->n_fields || \r
-        !(buf->fields[slot].flags & KCDB_CREDF_FLAG_ALLOCD))\r
-        return 0;\r
-    return (buf->fields[slot].cbsize);\r
-}\r
-\r
-void kcdb_buf_set_value_flag(kcdb_buf * buf, khm_size slot)\r
-{\r
-    if(slot >= buf->n_fields || \r
-        !(buf->fields[slot].flags & KCDB_CREDF_FLAG_ALLOCD))\r
-        return;\r
-\r
-    (buf->fields[slot].flags |= KCDB_CREDF_FLAG_DATA);\r
-}\r
-\r
-khm_size kcdb_buf_slot_by_id(kcdb_buf * buf, khm_ui_2 id)\r
-{\r
-    int i;\r
-\r
-    for(i=0; i < (int) buf->n_fields; i++) {\r
-        if(buf->fields[i].id == id)\r
-            break;\r
-    }\r
-\r
-    if(i < (int) buf->n_fields)\r
-        return i;\r
-    else\r
-        return KCDB_BUF_INVALID_SLOT;\r
-}\r
-\r
-/* API for accessing generic buffers */\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_buf_get_attr(\r
-    khm_handle  record, \r
-    khm_int32   attr_id, \r
-    khm_int32 * attr_type, \r
-    void *      buffer, \r
-    khm_size *  pcb_buf)\r
-{\r
-    if(kcdb_cred_is_active_cred(record))\r
-        return kcdb_cred_get_attr(record, attr_id, attr_type, buffer, pcb_buf);\r
-    else if(kcdb_is_active_identity(record))\r
-        return kcdb_identity_get_attr(record, attr_id, attr_type, buffer, pcb_buf);\r
-    else\r
-        return KHM_ERROR_INVALID_PARAM;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_buf_get_attrib(\r
-    khm_handle  record,\r
-    const wchar_t *   attr_name,\r
-    khm_int32 * attr_type,\r
-    void *      buffer,\r
-    khm_size *  pcb_buf)\r
-{\r
-    if(kcdb_cred_is_active_cred(record))\r
-        return kcdb_cred_get_attrib(record, attr_name, attr_type, buffer, pcb_buf);\r
-    else if(kcdb_is_active_identity(record))\r
-        return kcdb_identity_get_attrib(record, attr_name, attr_type, buffer, pcb_buf);\r
-    else\r
-        return KHM_ERROR_INVALID_PARAM;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_buf_get_attr_string(\r
-    khm_handle  record,\r
-    khm_int32   attr_id,\r
-    wchar_t *   buffer,\r
-    khm_size *  pcbbuf,\r
-    khm_int32  flags)\r
-{\r
-    if(kcdb_cred_is_active_cred(record))\r
-        return kcdb_cred_get_attr_string(record, attr_id, buffer, pcbbuf, flags);\r
-    else if(kcdb_is_active_identity(record))\r
-        return kcdb_identity_get_attr_string(record, attr_id, buffer, pcbbuf, flags);\r
-    else\r
-        return KHM_ERROR_INVALID_PARAM;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_buf_get_attrib_string(\r
-    khm_handle  record,\r
-    const wchar_t *   attr_name,\r
-    wchar_t *   buffer,\r
-    khm_size *  pcbbuf,\r
-    khm_int32   flags)\r
-{\r
-    if(kcdb_cred_is_active_cred(record))\r
-        return kcdb_cred_get_attrib_string(record, attr_name, buffer, pcbbuf, flags);\r
-    else if(kcdb_is_active_identity(record))\r
-        return kcdb_identity_get_attrib_string(record, attr_name, buffer, pcbbuf, flags);\r
-    else\r
-        return KHM_ERROR_INVALID_PARAM;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_buf_set_attr(\r
-    khm_handle  record,\r
-    khm_int32   attr_id,\r
-    void *      buffer,\r
-    khm_size    cbbuf)\r
-{\r
-    if(kcdb_cred_is_active_cred(record))\r
-        return kcdb_cred_set_attr(record, attr_id, buffer, cbbuf);\r
-    else if(kcdb_is_active_identity(record))\r
-        return kcdb_identity_set_attr(record, attr_id, buffer, cbbuf);\r
-    else\r
-        return KHM_ERROR_INVALID_PARAM;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_buf_set_attrib(\r
-    khm_handle  record,\r
-    const wchar_t *   attr_name,\r
-    void *      buffer,\r
-    khm_size    cbbuf)\r
-{\r
-    if(kcdb_cred_is_active_cred(record))\r
-        return kcdb_cred_set_attrib(record, attr_name, buffer, cbbuf);\r
-    else if(kcdb_is_active_identity(record))\r
-        return kcdb_identity_set_attrib(record, attr_name, buffer, cbbuf);\r
-    else\r
-        return KHM_ERROR_INVALID_PARAM;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_buf_hold(khm_handle  record)\r
-{\r
-    if(kcdb_cred_is_active_cred(record))\r
-        return kcdb_cred_hold(record);\r
-    else if(kcdb_is_active_identity(record))\r
-        return kcdb_identity_hold(record);\r
-    else\r
-        return KHM_ERROR_INVALID_PARAM;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_buf_release(khm_handle record)\r
-{\r
-    if(kcdb_cred_is_active_cred(record))\r
-        return kcdb_cred_release(record);\r
-    else if(kcdb_is_active_identity(record))\r
-        return kcdb_identity_release(record);\r
-    else\r
-        return KHM_ERROR_INVALID_PARAM;\r
-}\r
-\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 = PMALLOC(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 = PMALLOC(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)
+        PFREE(buf->buffer);
+    buf->buffer = NULL;
+
+    buf->n_fields = 0;
+    buf->nc_fields = 0;
+    if(buf->fields)
+        PFREE(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 = PMALLOC(new_size);
+    assert(new_buf != NULL);
+
+    memcpy(new_buf, buf->buffer, buf->cb_used);
+    PFREE(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 = PMALLOC(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));
+
+        PFREE(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) {
+            khm_size 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 = PMALLOC(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 = PMALLOC(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_PARAM;
+}
+
+KHMEXP khm_int32 KHMAPI kcdb_buf_get_attrib(
+    khm_handle  record,
+    const 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_PARAM;
+}
+
+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_PARAM;
+}
+
+KHMEXP khm_int32 KHMAPI kcdb_buf_get_attrib_string(
+    khm_handle  record,
+    const 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_PARAM;
+}
+
+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_PARAM;
+}
+
+KHMEXP khm_int32 KHMAPI kcdb_buf_set_attrib(
+    khm_handle  record,
+    const 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_PARAM;
+}
+
+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_PARAM;
+}
+
+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_PARAM;
+}
+
index f47bd0a2a0ebe9464f2cedeea193fc91ac949fa6..723253036c0ab8677d25b1f091c2f2e5cdff491c 100644 (file)
@@ -1,78 +1,78 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KCDB_BUF_H\r
-#define __KHIMAIRA_KCDB_BUF_H\r
-\r
-typedef struct tag_kcdb_buf_field {\r
-    khm_ui_2    id;\r
-    khm_ui_2    flags;\r
-    khm_ui_4    offset;\r
-    khm_ui_4    cbsize;\r
-} kcdb_buf_field;\r
-\r
-#define KCDB_CREDF_FLAG_EMPTY   0\r
-#define KCDB_CREDF_FLAG_DATA    1\r
-#define KCDB_CREDF_FLAG_INLINE  2\r
-#define KCDB_CREDF_FLAG_ALLOCD  4\r
-\r
-#define KCDB_BUFF_ID_INVALID    0xffff\r
-\r
-typedef struct tag_kcdb_buf {\r
-    void *      buffer;\r
-    khm_size    cb_buffer;\r
-    khm_size    cb_used;\r
-\r
-    kcdb_buf_field * fields;\r
-    khm_size    n_fields;\r
-    khm_size    nc_fields;\r
-} kcdb_buf;\r
-\r
-#define KCDB_BUF_CBBUF_INITIAL  4096\r
-#define KCDB_BUF_CBBUF_GROWTH   4096\r
-#define KCDB_BUF_FIELDS_INITIAL 16\r
-#define KCDB_BUF_FIELDS_GROWTH  16\r
-\r
-#define KCDB_BUF_APPEND 0x8000\r
-\r
-#define KCDB_BUF_INVALID_SLOT   0xf0000000\r
-#define KCDB_BUF_DEFAULT        0xe0000000\r
-\r
-#define KCDB_BUF_MAX_SLOTS      0x00004000\r
-\r
-void    kcdb_buf_new(kcdb_buf * buf, khm_size n_slots);\r
-void    kcdb_buf_delete(kcdb_buf * buf);\r
-void    kcdb_buf_alloc(kcdb_buf * buf, khm_size slot, khm_ui_2 id, khm_size cbsize);\r
-void    kcdb_buf_dup(kcdb_buf * dest, const kcdb_buf * src);\r
-void    kcdb_buf_set_value(kcdb_buf * buf, khm_size slot, khm_ui_2 id, void * src, khm_size cb_src);\r
-int     kcdb_buf_exist(kcdb_buf * buf, khm_size slot);\r
-int     kcdb_buf_val_exist(kcdb_buf * buf, khm_size slot);\r
-void *  kcdb_buf_get(kcdb_buf * buf, khm_size slot);\r
-khm_size kcdb_buf_size(kcdb_buf * buf, khm_size slot);\r
-void    kcdb_buf_set_value_flag(kcdb_buf * buf, khm_size slot);\r
-khm_size kcdb_buf_slot_by_id(kcdb_buf * buf, khm_ui_2 id);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
index 12b8c5fc5378944da6c2b60fd01c9a1e0f007e1c..c8a423a2a8a9c77db1d126061081148e8f8344ce 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kcreddbinternal.h>\r
-#include<assert.h>\r
-\r
-/* cs_creds protects the *collection* of credentials, while l_creds\r
-   protects the *contents* of individual credentials. */\r
-CRITICAL_SECTION cs_creds;\r
-kcdb_cred * kcdb_creds = NULL;\r
-\r
-/* a read lock must be obtained when querying any existing credential.\r
-   a write lock must be obtained when modifying any existing credential.\r
-   */\r
-RWLOCK l_creds;\r
-\r
-/* serial number */\r
-khm_ui_8 kcdb_cred_id = 0;\r
-\r
-void kcdb_cred_init(void)\r
-{\r
-    InitializeCriticalSection(&cs_creds);\r
-    InitializeRwLock(&l_creds);\r
-    kcdb_cred_id = 0;\r
-}\r
-\r
-void kcdb_cred_exit(void)\r
-{\r
-    /*TODO: Free the credentials */\r
-    DeleteCriticalSection(&cs_creds);\r
-    DeleteRwLock(&l_creds);\r
-}\r
-\r
-/*! \internal\r
-\r
-    can be called by kcdb_cred_dup with a write lock on l_creds and in other\r
-    places with a read lock on l_creds.  New credentials must be creatable while\r
-    holding either lock. */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_create(const wchar_t *   name, \r
-                 khm_handle  identity,\r
-                 khm_int32   cred_type,\r
-                 khm_handle * result) \r
-{\r
-    kcdb_cred * cred;\r
-    size_t cb_name;\r
-\r
-    if(!name || !result ||\r
-        FAILED(StringCbLength(name, KCDB_CRED_MAXCB_NAME, &cb_name)) ||\r
-        KHM_FAILED(kcdb_credtype_get_info(cred_type, NULL)) ||\r
-        KHM_FAILED(kcdb_identity_hold(identity))) {\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    cb_name += sizeof(wchar_t);\r
-\r
-    cred = PMALLOC(sizeof(kcdb_cred));\r
-    ZeroMemory(cred, sizeof(kcdb_cred));\r
-\r
-    cred->magic = KCDB_CRED_MAGIC;\r
-    cred->identity = identity;\r
-    cred->name = PMALLOC(cb_name);\r
-    StringCbCopy(cred->name, cb_name, name);\r
-    cred->type = cred_type;\r
-\r
-    cred->refcount = 1; /* initially held */\r
-    \r
-    LINIT(cred);\r
-\r
-    kcdb_buf_new(&cred->buf, KCDB_ATTR_MAX_ID + 1);\r
-\r
-    /* Not obtaining a write lock on l_cred on purpose.\r
-       Well, because no one should be referencing this credential until\r
-       this function returns. */\r
-    EnterCriticalSection(&cs_creds);\r
-    cred->id = kcdb_cred_id++;\r
-    LPUSH(&kcdb_creds, cred);\r
-    LeaveCriticalSection(&cs_creds);\r
-\r
-    *result = cred;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_cred_update(khm_handle vdest,\r
-                                         khm_handle vsrc)\r
-{\r
-    khm_int32 rv = KHM_ERROR_EQUIVALENT;\r
-    kcdb_cred * src;\r
-    kcdb_cred * dest;\r
-    kcdb_type * t;\r
-    kcdb_attrib * a;\r
-    void * srcbuf;\r
-    void * destbuf;\r
-    khm_size cbsrcbuf;\r
-    khm_size cbdestbuf;\r
-\r
-    int i;\r
-\r
-    kcdb_cred_lock_write();\r
-\r
-    if(!kcdb_cred_is_active_cred(vsrc) ||\r
-       !kcdb_cred_is_active_cred(vdest))\r
-        goto _exit;\r
-\r
-    src = (kcdb_cred *) vsrc;\r
-    dest = (kcdb_cred *) vdest;\r
-\r
-    for(i=0;i<KCDB_ATTR_MAX_ID;i++) {\r
-        if(kcdb_cred_val_exist(src, i)) {\r
-            /*NOTE: the logic here has to reflect the logic in\r
-              kcdb_cred_set_attr() */\r
-            if(KHM_FAILED(kcdb_attrib_get_info(i, &a)))\r
-                continue;\r
-\r
-            if((a->flags & KCDB_ATTR_FLAG_COMPUTED) ||\r
-                KHM_FAILED(kcdb_type_get_info(a->type, &t))) {\r
-                kcdb_attrib_release_info(a);\r
-                continue;\r
-            }\r
-\r
-            srcbuf = kcdb_cred_buf_get(src,i);\r
-            cbsrcbuf = kcdb_cred_buf_size(src, i);\r
-\r
-            if(kcdb_cred_val_exist(dest, i)) {\r
-                destbuf = kcdb_cred_buf_get(dest, i);\r
-                cbdestbuf = kcdb_cred_buf_size(dest, i);\r
-\r
-                if(!t->comp(srcbuf, cbsrcbuf, destbuf, cbdestbuf))\r
-                    goto _skip_copy;\r
-            }\r
-\r
-            kcdb_buf_set_value(&dest->buf, i, i, srcbuf, cbsrcbuf);\r
-            rv = KHM_ERROR_SUCCESS;\r
-\r
-       _skip_copy:\r
-            kcdb_attrib_release_info(a);\r
-            kcdb_type_release_info(t);\r
-        } else {\r
-           if (KHM_FAILED(kcdb_attrib_get_info(i, &a)))\r
-               continue;\r
-\r
-           if (!(a->flags & KCDB_ATTR_FLAG_COMPUTED) &&\r
-               (a->flags & KCDB_ATTR_FLAG_TRANSIENT) &&\r
-               kcdb_cred_val_exist(dest, i)) {\r
-               kcdb_buf_set_value(&dest->buf, i, i, NULL, 0);\r
-\r
-               rv = KHM_ERROR_SUCCESS;\r
-           }\r
-\r
-           kcdb_attrib_release_info(a);\r
-       }\r
-    }\r
-\r
-    if (dest->flags != src->flags) {\r
-        khm_int32 old_flags;\r
-\r
-        old_flags = dest->flags;\r
-\r
-        dest->flags = (src->flags & ~KCDB_CRED_FLAGMASK_ADDITIVE) |\r
-            ((src->flags | dest->flags) & KCDB_CRED_FLAGMASK_ADDITIVE);\r
-\r
-        if (dest->flags != old_flags)\r
-            rv = KHM_ERROR_SUCCESS;\r
-    }\r
-\r
- _exit:\r
-    kcdb_cred_unlock_write();\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_cred_dup(\r
-    khm_handle vcred,\r
-    khm_handle * pnewcred)\r
-{\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    kcdb_cred * cred;\r
-    kcdb_cred * newcred;\r
-    khm_handle vnewcred;\r
-\r
-    if(!pnewcred)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    *pnewcred = NULL;\r
-\r
-    kcdb_cred_lock_write();\r
-\r
-    if(!kcdb_cred_is_active_cred(vcred)) {\r
-        code = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit;\r
-    }\r
-\r
-    cred = (kcdb_cred *) vcred;\r
-\r
-    if(KHM_FAILED(kcdb_cred_create(cred->name,\r
-                                   cred->identity,\r
-                                   cred->type,\r
-                                   &vnewcred))) \r
-    {\r
-        code = KHM_ERROR_UNKNOWN;\r
-        goto _exit;\r
-    }\r
-\r
-    newcred = (kcdb_cred *) vnewcred;\r
-\r
-    newcred->flags = cred->flags;\r
-\r
-    kcdb_buf_dup(&newcred->buf, &cred->buf);\r
-\r
-    /* newcred is already held from the call to kcdb_cred_create */\r
-    *pnewcred = (khm_handle) newcred;\r
-\r
-_exit:\r
-    kcdb_cred_unlock_write();\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_cred_get_serial(\r
-    khm_handle vcred,\r
-    khm_ui_8 * pserial)\r
-{\r
-    kcdb_cred * c;\r
-\r
-    if(!pserial)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    kcdb_cred_lock_read();\r
-\r
-    if(!kcdb_cred_is_active_cred(vcred)) {\r
-        kcdb_cred_unlock_read();\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    c = (kcdb_cred *) vcred;\r
-\r
-    *pserial = c->id;\r
-\r
-    kcdb_cred_unlock_read();\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_cred_set_identity(\r
-    khm_handle vcred,\r
-    khm_handle id)\r
-{\r
-    kcdb_cred * c;\r
-\r
-    if(!kcdb_is_identity(id))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    kcdb_cred_lock_write();\r
-    if(!kcdb_cred_is_active_cred(vcred)) {\r
-        kcdb_cred_unlock_write();\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    c = (kcdb_cred *) vcred;\r
-\r
-    if(c->identity) {\r
-        kcdb_identity_release((khm_handle) c->identity);\r
-    }\r
-    kcdb_identity_hold(id);\r
-    c->identity = (kcdb_identity *) id;\r
-\r
-    kcdb_cred_unlock_write();\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_cred_get_type(\r
-    khm_handle vcred,\r
-    khm_int32 * type)\r
-{\r
-    kcdb_cred * c;\r
-\r
-    if(!type)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    kcdb_cred_lock_read();\r
-\r
-    if(!kcdb_cred_is_active_cred(vcred)) {\r
-        kcdb_cred_unlock_read();\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    c = (kcdb_cred *) vcred;\r
-\r
-    *type = c->type;\r
-\r
-    kcdb_cred_unlock_read();\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_cred_set_attrib(\r
-    khm_handle cred, \r
-    const wchar_t * name, \r
-    void * buffer, \r
-    khm_size cbbuf)\r
-{\r
-    khm_int32 attr_id = -1;\r
-\r
-    if(KHM_FAILED(kcdb_attrib_get_id(name, &attr_id)))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    return kcdb_cred_set_attr(\r
-        cred,\r
-        attr_id,\r
-        buffer,\r
-        cbbuf);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_cred_set_attr(\r
-    khm_handle vcred, \r
-    khm_int32 attr_id, \r
-    void * buffer, \r
-    khm_size cbbuf)\r
-{\r
-    kcdb_cred * cred;\r
-    kcdb_type * type = NULL;\r
-    kcdb_attrib * attrib = NULL;\r
-    khm_size cbdest;\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-\r
-    if(attr_id < 0 || attr_id > KCDB_ATTR_MAX_ID)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    kcdb_cred_lock_write();\r
-\r
-    if(!kcdb_cred_is_active_cred(vcred)) {\r
-        kcdb_cred_unlock_write();\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    cred = (kcdb_cred *) vcred;\r
-\r
-    if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib))) {\r
-        kcdb_cred_unlock_write();\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    if(attrib->flags & KCDB_ATTR_FLAG_COMPUTED)\r
-    {\r
-        kcdb_cred_unlock_write();\r
-        kcdb_attrib_release_info(attrib);\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-    }\r
-\r
-    if (buffer == 0) {\r
-        /* we are removing the value */\r
-        kcdb_buf_alloc(&cred->buf, attr_id, attr_id, 0);\r
-        code = KHM_ERROR_SUCCESS;\r
-        goto _exit;\r
-    }\r
-\r
-    if(KHM_FAILED(kcdb_type_get_info(attrib->type, &type))) {\r
-        kcdb_cred_unlock_write();\r
-        kcdb_attrib_release_info(attrib);\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    if(!(type->isValid(buffer,cbbuf))) {\r
-        code = KHM_ERROR_TYPE_MISMATCH;\r
-        goto _exit;\r
-    }\r
-\r
-    if((type->dup(buffer, cbbuf, NULL, &cbdest)) != KHM_ERROR_TOO_LONG) {\r
-        code = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit;\r
-    }\r
-\r
-    kcdb_buf_alloc(&cred->buf, attr_id, attr_id, cbdest);\r
-    if(!kcdb_cred_buf_exist(cred, attr_id)) {\r
-        code = KHM_ERROR_NO_RESOURCES;\r
-        goto _exit;\r
-    }\r
-\r
-    if(KHM_FAILED(code =\r
-        type->dup(buffer, cbbuf, kcdb_cred_buf_get(cred,attr_id), &cbdest))) \r
-    {\r
-        kcdb_buf_alloc(&cred->buf, attr_id, attr_id, 0);\r
-        goto _exit;\r
-    }\r
-\r
-    kcdb_buf_set_value_flag(&cred->buf, attr_id);\r
-\r
-_exit:\r
-    kcdb_cred_unlock_write();\r
-\r
-    if(attrib)\r
-        kcdb_attrib_release_info(attrib);\r
-    if(type)\r
-        kcdb_type_release_info(type);\r
-\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_cred_get_attrib(\r
-    khm_handle cred, \r
-    const wchar_t * name, \r
-    khm_int32 * attr_type,\r
-    void * buffer, \r
-    khm_size * cbbuf) \r
-{\r
-    khm_int32 attr_id = -1;\r
-\r
-    if(KHM_FAILED(kcdb_attrib_get_id(name, &attr_id)))\r
-        return KHM_ERROR_NOT_FOUND;\r
-\r
-    return kcdb_cred_get_attr(\r
-        cred,\r
-        attr_id,\r
-        attr_type,\r
-        buffer,\r
-        cbbuf);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_cred_get_attrib_string(\r
-    khm_handle cred, \r
-    const wchar_t * name, \r
-    wchar_t * buffer, \r
-    khm_size * cbbuf,\r
-    khm_int32 flags) \r
-{\r
-    khm_int32 attr_id = -1;\r
-\r
-    if(KHM_FAILED(kcdb_attrib_get_id(name, &attr_id)))\r
-        return KHM_ERROR_NOT_FOUND;\r
-\r
-    return kcdb_cred_get_attr_string(\r
-        cred,\r
-        attr_id,\r
-        buffer,\r
-        cbbuf,\r
-        flags);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_get_attr(khm_handle vcred, \r
-                   khm_int32 attr_id,\r
-                   khm_int32 * attr_type,\r
-                   void * buffer, \r
-                   khm_size * pcbbuf)\r
-{\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    kcdb_cred * cred = NULL;\r
-    kcdb_attrib * attrib = NULL;\r
-    kcdb_type * type = NULL;\r
-\r
-    if(attr_id < 0 || attr_id > KCDB_ATTR_MAX_ID)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib))) {\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    if(KHM_FAILED(kcdb_type_get_info(attrib->type, &type))) {\r
-        kcdb_attrib_release_info(attrib);\r
-        return KHM_ERROR_UNKNOWN;\r
-    }\r
-\r
-    if(attr_type)\r
-        *attr_type = attrib->type;\r
-\r
-    kcdb_cred_lock_read();\r
-    if(!kcdb_cred_is_active_cred(vcred)) {\r
-        code = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit;\r
-    }\r
-\r
-    cred = (kcdb_cred *) vcred;\r
-\r
-    if(!buffer && !pcbbuf) {\r
-        /* in this case the caller is only trying to determine if the\r
-            field contains data.  We assume that computed fields are\r
-            always non-null. */\r
-        code = (kcdb_cred_val_exist(cred, attr_id) ||\r
-            (attrib->flags & KCDB_ATTR_FLAG_COMPUTED))?KHM_ERROR_SUCCESS:KHM_ERROR_NOT_FOUND;\r
-        goto _exit;\r
-    }\r
-\r
-    if(attrib->flags & KCDB_ATTR_FLAG_COMPUTED) {\r
-        code = attrib->compute_cb(\r
-            vcred,\r
-            attr_id,\r
-            buffer,\r
-            pcbbuf);\r
-    } else if (kcdb_cred_val_exist(cred, attr_id)) {\r
-        code = type->dup(\r
-            kcdb_cred_buf_get(cred, attr_id),\r
-            kcdb_cred_buf_size(cred, attr_id),\r
-            buffer,\r
-            pcbbuf);\r
-    } else {\r
-        code = KHM_ERROR_NOT_FOUND;\r
-    }\r
-\r
-_exit:\r
-    kcdb_cred_unlock_read();\r
-    if(type)\r
-        kcdb_type_release_info(type);\r
-    if(attrib)\r
-        kcdb_attrib_release_info(attrib);\r
-\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_cred_get_attr_string(\r
-    khm_handle vcred, \r
-    khm_int32 attr_id,\r
-    wchar_t * buffer, \r
-    khm_size * pcbbuf,\r
-    khm_int32 flags)\r
-{\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    kcdb_cred * cred = NULL;\r
-    kcdb_attrib * attrib = NULL;\r
-    kcdb_type * type = NULL;\r
-\r
-    if(attr_id < 0 || attr_id > KCDB_ATTR_MAX_ID)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib))) {\r
-        code = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit_nolock;\r
-    }\r
-\r
-    if(KHM_FAILED(kcdb_type_get_info(attrib->type, &type))) {\r
-        code = KHM_ERROR_UNKNOWN;\r
-        goto _exit_nolock;\r
-    }\r
-\r
-    kcdb_cred_lock_read();\r
-    if(!kcdb_cred_is_active_cred(vcred)) {\r
-        code = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit;\r
-    }\r
-\r
-    cred = (kcdb_cred *) vcred;\r
-\r
-    if(!buffer && !pcbbuf) {\r
-        /* in this case the caller is only trying to determine if the\r
-            field contains data.  We assume that computed fields are\r
-            always non-null. */\r
-        code = (kcdb_cred_val_exist(cred, attr_id) ||\r
-            (attrib->flags & KCDB_ATTR_FLAG_COMPUTED))?KHM_ERROR_SUCCESS:KHM_ERROR_NOT_FOUND;\r
-        goto _exit;\r
-    }\r
-\r
-    if(attrib->flags & KCDB_ATTR_FLAG_COMPUTED) {\r
-        void * buf;\r
-        khm_size cbbuf;\r
-\r
-        code = attrib->compute_cb(vcred,\r
-                                  attr_id,\r
-                                  NULL,\r
-                                  &cbbuf);\r
-        if(code == KHM_ERROR_TOO_LONG) {\r
-            wchar_t vbuf[KCDB_MAXCCH_NAME];\r
-\r
-            if (cbbuf < sizeof(vbuf))\r
-                buf = vbuf;\r
-            else\r
-                buf = PMALLOC(cbbuf);\r
-\r
-            code = attrib->compute_cb(vcred,\r
-                                      attr_id,\r
-                                      buf,\r
-                                      &cbbuf);\r
-            if(KHM_SUCCEEDED(code)) {\r
-                code = type->toString(buf,\r
-                                      cbbuf,\r
-                                      buffer,\r
-                                      pcbbuf,\r
-                                      flags);\r
-            }\r
-\r
-            if (buf != vbuf)\r
-                PFREE(buf);\r
-        }\r
-    } else {\r
-        if(kcdb_cred_buf_exist(cred, attr_id)) {\r
-            code = type->toString(\r
-                kcdb_cred_buf_get(cred, attr_id),\r
-                kcdb_cred_buf_size(cred, attr_id),\r
-                buffer,\r
-                pcbbuf,\r
-                flags);\r
-        } else\r
-            code = KHM_ERROR_NOT_FOUND;\r
-    }\r
-\r
- _exit:\r
-    kcdb_cred_unlock_read();\r
- _exit_nolock:\r
-    if(type)\r
-        kcdb_type_release_info(type);\r
-    if(attrib)\r
-        kcdb_attrib_release_info(attrib);\r
-\r
-    return code;\r
-}\r
-\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_cred_get_name(\r
-    khm_handle vcred, \r
-    wchar_t * buffer, \r
-    khm_size * cbbuf)\r
-{\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    kcdb_cred * cred = NULL;\r
-    size_t cbsize;\r
-\r
-    if(!cbbuf)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    kcdb_cred_lock_read();\r
-    \r
-    if(!kcdb_cred_is_active_cred(vcred)) {\r
-        code = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit;\r
-    }\r
-\r
-    cred = (kcdb_cred *) vcred;\r
-\r
-    if(FAILED(StringCbLength(cred->name, KCDB_CRED_MAXCB_NAME, &cbsize))) {\r
-        code = KHM_ERROR_UNKNOWN;\r
-        goto _exit;\r
-    }\r
-\r
-    cbsize += sizeof(wchar_t);\r
-\r
-    if(!buffer || *cbbuf < cbsize) {\r
-        *cbbuf = cbsize;\r
-        code = KHM_ERROR_TOO_LONG;\r
-        goto _exit;\r
-    }\r
-\r
-    StringCbCopy(buffer, *cbbuf, cred->name);\r
-\r
-    *cbbuf = cbsize;\r
-\r
-_exit:\r
-\r
-    kcdb_cred_unlock_read();\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_cred_get_identity(\r
-    khm_handle vcred, \r
-    khm_handle * identity)\r
-{\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    kcdb_cred * cred;\r
-\r
-    if(!identity)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    kcdb_cred_lock_read();\r
-\r
-    if(!kcdb_cred_is_active_cred(vcred)) {\r
-        code = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit;\r
-    }\r
-\r
-    cred = (kcdb_cred *) vcred;\r
-\r
-    kcdb_identity_hold((khm_handle) cred->identity);\r
-\r
-    *identity = cred->identity;\r
-    \r
-_exit:\r
-    kcdb_cred_unlock_read();\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_cred_hold(khm_handle vcred)\r
-{\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    kcdb_cred * cred;\r
-\r
-    kcdb_cred_lock_write();\r
-\r
-    if(!kcdb_cred_is_cred(vcred)) {\r
-        code = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit;\r
-    }\r
-\r
-    cred = (kcdb_cred *) vcred;\r
-\r
-    cred->refcount++;\r
-\r
-_exit:\r
-    kcdb_cred_unlock_write();\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_cred_release(khm_handle vcred)\r
-{\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    kcdb_cred * cred;\r
-\r
-    kcdb_cred_lock_write();\r
-\r
-    if(!kcdb_cred_is_cred(vcred)) {\r
-        code = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit;\r
-    }\r
-\r
-    cred = (kcdb_cred *) vcred;\r
-\r
-    cred->refcount--;\r
-\r
-_exit:\r
-    kcdb_cred_unlock_write();\r
-\r
-    kcdb_cred_check_and_delete(vcred);\r
-    \r
-    return code;\r
-}\r
-\r
-void kcdb_cred_check_and_delete(khm_handle vcred)\r
-{\r
-    kcdb_cred * cred;\r
-\r
-    kcdb_cred_lock_read();\r
-    if(!kcdb_cred_is_cred(vcred)) {\r
-        goto _exit;\r
-    }\r
-\r
-    cred = (kcdb_cred *) vcred;\r
-\r
-    if(cred->refcount)\r
-        goto _exit;\r
-\r
-    kcdb_cred_unlock_read();\r
-    kcdb_cred_lock_write();\r
-    if(!kcdb_cred_is_cred(vcred)) {\r
-        /* did we lose the race? */\r
-        goto _exit2;\r
-    }\r
-\r
-    cred->magic = 0; /* no longer a cred */\r
-    kcdb_identity_release(cred->identity);\r
-\r
-    EnterCriticalSection(&cs_creds);\r
-    LDELETE(&kcdb_creds, cred);\r
-    LeaveCriticalSection(&cs_creds);\r
-\r
-    kcdb_buf_delete(&cred->buf);\r
-    PFREE(cred->name);\r
-    PFREE(cred);\r
-\r
-    /*TODO: notifications */\r
-\r
-_exit2:\r
-    kcdb_cred_unlock_write();\r
-    return;\r
-\r
-_exit:\r
-    kcdb_cred_unlock_read();\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_cred_delete(khm_handle vcred)\r
-{\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    kcdb_cred * cred;\r
-\r
-    kcdb_cred_lock_write();\r
-\r
-    if(!kcdb_cred_is_active_cred(vcred)) {\r
-        code = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit;\r
-    }\r
-\r
-    cred = (kcdb_cred *) vcred;\r
-\r
-    cred->flags |= KCDB_CRED_FLAG_DELETED;\r
-\r
-_exit:\r
-    kcdb_cred_unlock_write();\r
-\r
-    kcdb_cred_check_and_delete(vcred);\r
-\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_creds_comp_attrib(khm_handle cred1, \r
-                       khm_handle cred2, \r
-                       const wchar_t * name)\r
-{\r
-    khm_int32 attr_id;\r
-\r
-    if(KHM_FAILED(kcdb_attrib_get_id(name, &attr_id)))\r
-        return 0;\r
-\r
-    return kcdb_creds_comp_attr(cred1, cred2, attr_id);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_creds_comp_attr(khm_handle vcred1, \r
-                     khm_handle vcred2, \r
-                     khm_int32 attr_id)\r
-{\r
-    khm_int32 code = 0;\r
-    kcdb_cred * cred1;\r
-    kcdb_cred * cred2;\r
-    kcdb_attrib * attrib = NULL;\r
-    kcdb_type * type = NULL;\r
-\r
-    if(attr_id < 0 || attr_id > KCDB_ATTR_MAX_ID)\r
-        return 0;\r
-\r
-    cred1 = (kcdb_cred *) vcred1;\r
-    cred2 = (kcdb_cred *) vcred2;\r
-\r
-    kcdb_cred_lock_read();\r
-    if(\r
-        !kcdb_cred_is_active_cred(vcred1) ||\r
-        !kcdb_cred_is_active_cred(vcred2))\r
-        goto _exit;\r
-\r
-    cred1 = (kcdb_cred *) vcred1;\r
-    cred2 = (kcdb_cred *) vcred2;\r
-\r
-    if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib)))\r
-        goto _exit;\r
-\r
-    if(!(attrib->flags & KCDB_ATTR_FLAG_COMPUTED)) {\r
-        int nc = 0;\r
-\r
-        if(!kcdb_cred_val_exist(cred1, attr_id)) {\r
-            code = -1;\r
-            nc = 1;\r
-        }\r
-        if(!kcdb_cred_val_exist(cred2, attr_id)) {\r
-            code += 1;\r
-            nc = 1;\r
-        }\r
-\r
-        if(nc)\r
-            goto _exit;\r
-    }\r
-\r
-    if(KHM_FAILED(kcdb_type_get_info(attrib->type, &type)))\r
-        goto _exit;\r
-\r
-    if(attrib->flags & KCDB_ATTR_FLAG_COMPUTED) {\r
-        khm_octet  vbuf[KCDB_MAXCB_NAME * 2];\r
-        void * buf1 = NULL;\r
-        void * buf2 = NULL;\r
-        khm_size cb1;\r
-        khm_size cb2;\r
-\r
-        code = 0;\r
-\r
-        if(attrib->compute_cb(vcred1, attr_id, \r
-                              NULL, &cb1) != KHM_ERROR_TOO_LONG)\r
-            goto _exit_1;\r
-\r
-        if(attrib->compute_cb(vcred2, attr_id, \r
-                              NULL, &cb2) != KHM_ERROR_TOO_LONG)\r
-            goto _exit_1;\r
-\r
-        if(cb1) {\r
-            if (cb1 < sizeof(vbuf))\r
-                buf1 = vbuf;\r
-            else\r
-                buf1 = PMALLOC(cb1);\r
-\r
-            if(KHM_FAILED(attrib->compute_cb(vcred1, attr_id, buf1, &cb1)))\r
-                goto _exit_1;\r
-        }\r
-\r
-        if(cb2) {\r
-            if (cb1 + cb2 < sizeof(vbuf))\r
-                buf2 = vbuf + cb1;\r
-            else\r
-                buf2 = PMALLOC(cb2);\r
-\r
-            if(KHM_FAILED(attrib->compute_cb(vcred2, attr_id, buf2, &cb2)))\r
-                goto _exit_1;\r
-        }\r
-\r
-        code = type->comp(buf1, cb1,\r
-                          buf2, cb2);\r
-_exit_1:\r
-        if(buf1 && (buf1 < (void *)vbuf || \r
-                    buf1 >= (void*)(vbuf + sizeof(vbuf))))\r
-            PFREE(buf1);\r
-        if(buf2 && (buf2 < (void *)vbuf ||\r
-                    buf2 >= (void *)(vbuf + sizeof(vbuf))))\r
-            PFREE(buf2);\r
-    } else {\r
-        code = type->comp(\r
-            kcdb_cred_buf_get(cred1, attr_id),\r
-            kcdb_cred_buf_size(cred1, attr_id),\r
-            kcdb_cred_buf_get(cred2, attr_id),\r
-            kcdb_cred_buf_size(cred2, attr_id));\r
-    }\r
-\r
-_exit:\r
-    kcdb_cred_unlock_read();\r
-    if(attrib)\r
-        kcdb_attrib_release_info(attrib);\r
-    if(type)\r
-        kcdb_type_release_info(type);\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_creds_is_equal(khm_handle vcred1,\r
-                    khm_handle vcred2)\r
-{\r
-    khm_int32 code = 0;\r
-    kcdb_cred * cred1;\r
-    kcdb_cred * cred2;\r
-\r
-    kcdb_cred_lock_read();\r
-    if(!kcdb_cred_is_active_cred(vcred1) ||\r
-       !kcdb_cred_is_active_cred(vcred2)) {\r
-\r
-        code = FALSE;\r
-        goto _exit;\r
-\r
-    }\r
-\r
-    if(vcred1 == vcred2) {\r
-\r
-        code = TRUE;\r
-        goto _exit;\r
-\r
-    }\r
-\r
-    cred1 = vcred1;\r
-    cred2 = vcred2;\r
-\r
-    if(cred1->identity == cred2->identity &&\r
-       cred1->type == cred2->type &&\r
-       !wcscmp(cred1->name, cred2->name)) {\r
-\r
-        kcdb_credtype * type;\r
-\r
-        code = TRUE;\r
-\r
-        if (KHM_SUCCEEDED(kcdb_credtype_get_info(cred1->type, &type))) {\r
-            if (type->is_equal &&\r
-                (*type->is_equal)(vcred1, vcred2, NULL))\r
-                code = 0;\r
-\r
-            kcdb_credtype_release_info(type);\r
-        }\r
-    }\r
-\r
-_exit:\r
-    kcdb_cred_unlock_read();\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_get_flags(khm_handle vcred,\r
-                    khm_int32 * pflags)\r
-{\r
-    khm_int32 f;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    kcdb_cred * cred;\r
-    int release_lock = TRUE;\r
-\r
-    if (pflags == NULL)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    kcdb_cred_lock_read();\r
-    if (!kcdb_cred_is_active_cred(vcred)) {\r
-        *pflags = 0;\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit;\r
-    }\r
-\r
-    cred = vcred;\r
-    f = cred->flags;\r
-\r
-    /* Update flags if necessary */\r
-\r
-    if (!(f & KCDB_CRED_FLAG_EXPIRED) && \r
-        kcdb_cred_buf_exist(cred, KCDB_ATTR_EXPIRE)) {\r
-\r
-        FILETIME ftc;\r
-            \r
-        GetSystemTimeAsFileTime(&ftc);\r
-        if (CompareFileTime(&ftc, ((FILETIME *) \r
-                                   kcdb_cred_buf_get(cred, KCDB_ATTR_EXPIRE)))\r
-            >= 0)\r
-            f |= KCDB_CRED_FLAG_EXPIRED;\r
-    }\r
-\r
-#if 0\r
-    /* Commented out: if the credential has expired, then checking the\r
-       renewable time is not useful */\r
-    if (!(f & KCDB_CRED_FLAG_INVALID)) {\r
-        if (f & KCDB_CRED_FLAG_RENEWABLE) {\r
-            if (kcdb_cred_buf_exist(cred, KCDB_ATTR_RENEW_EXPIRE)) {\r
-                FILETIME ftc;\r
-\r
-                GetSystemTimeAsFileTime(&ftc);\r
-                if (CompareFileTime(&ftc, ((FILETIME *)\r
-                                           kcdb_cred_buf_get(cred, KCDB_ATTR_RENEW_EXPIRE))) >= 0)\r
-                    f |= KCDB_CRED_FLAG_INVALID;\r
-            }\r
-        } else {\r
-            if (f & KCDB_CRED_FLAG_EXPIRED)\r
-                f |= KCDB_CRED_FLAG_INVALID;\r
-        }\r
-    }\r
-\r
-    /* Commented out: this is a read operation.  We shouldn't attempt\r
-       to lock for writing */\r
-    if (f != cred->flags) {\r
-        kcdb_cred_unlock_read();\r
-        kcdb_cred_lock_write();\r
-        /* Did we lose a race? */\r
-        if (kcdb_cred_is_active_cred(vcred))\r
-            cred->flags = f;\r
-        else {\r
-            rv = KHM_ERROR_INVALID_PARAM;\r
-            f = 0;\r
-        }\r
-        kcdb_cred_unlock_write();\r
-        release_lock = FALSE;\r
-    }\r
-#endif\r
-\r
-    *pflags = f;\r
-\r
- _exit:\r
-    if (release_lock)\r
-        kcdb_cred_unlock_read();\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_cred_set_flags(\r
-    khm_handle vcred,\r
-    khm_int32 flags,\r
-    khm_int32 mask)\r
-{\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    kcdb_cred * cred;\r
-\r
-    kcdb_cred_lock_write();\r
-    if(!kcdb_cred_is_active_cred(vcred)) {\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit;\r
-    }\r
-\r
-    cred = vcred;\r
-\r
-    flags &= ~(KCDB_CRED_FLAG_DELETED);\r
-    mask &= ~(KCDB_CRED_FLAG_DELETED);\r
-\r
-    cred->flags =\r
-        (cred->flags & (~mask)) |\r
-        (flags & mask);\r
-\r
- _exit:\r
-    kcdb_cred_unlock_write();\r
-    return rv;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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(const 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_PARAM;
+    }
+
+    cb_name += sizeof(wchar_t);
+
+    cred = PMALLOC(sizeof(kcdb_cred));
+    ZeroMemory(cred, sizeof(kcdb_cred));
+
+    cred->magic = KCDB_CRED_MAGIC;
+    cred->identity = identity;
+    cred->name = PMALLOC(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);
+        } else {
+           if (KHM_FAILED(kcdb_attrib_get_info(i, &a)))
+               continue;
+
+           if (!(a->flags & KCDB_ATTR_FLAG_COMPUTED) &&
+               (a->flags & KCDB_ATTR_FLAG_TRANSIENT) &&
+               kcdb_cred_val_exist(dest, i)) {
+               kcdb_buf_set_value(&dest->buf, i, i, NULL, 0);
+
+               rv = KHM_ERROR_SUCCESS;
+           }
+
+           kcdb_attrib_release_info(a);
+       }
+    }
+
+    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_PARAM;
+
+    *pnewcred = NULL;
+
+    kcdb_cred_lock_write();
+
+    if(!kcdb_cred_is_active_cred(vcred)) {
+        code = KHM_ERROR_INVALID_PARAM;
+        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_PARAM;
+
+    kcdb_cred_lock_read();
+
+    if(!kcdb_cred_is_active_cred(vcred)) {
+        kcdb_cred_unlock_read();
+        return KHM_ERROR_INVALID_PARAM;
+    }
+
+    c = (kcdb_cred *) vcred;
+
+    *pserial = c->id;
+
+    kcdb_cred_unlock_read();
+
+    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_PARAM;
+
+    kcdb_cred_lock_write();
+    if(!kcdb_cred_is_active_cred(vcred)) {
+        kcdb_cred_unlock_write();
+        return KHM_ERROR_INVALID_PARAM;
+    }
+
+    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_PARAM;
+
+    kcdb_cred_lock_read();
+
+    if(!kcdb_cred_is_active_cred(vcred)) {
+        kcdb_cred_unlock_read();
+        return KHM_ERROR_INVALID_PARAM;
+    }
+
+    c = (kcdb_cred *) vcred;
+
+    *type = c->type;
+
+    kcdb_cred_unlock_read();
+
+    return KHM_ERROR_SUCCESS;
+}
+
+KHMEXP khm_int32 KHMAPI kcdb_cred_set_attrib(
+    khm_handle cred, 
+    const 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_PARAM;
+
+    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_PARAM;
+
+    kcdb_cred_lock_write();
+
+    if(!kcdb_cred_is_active_cred(vcred)) {
+        kcdb_cred_unlock_write();
+        return KHM_ERROR_INVALID_PARAM;
+    }
+
+    cred = (kcdb_cred *) vcred;
+
+    if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib))) {
+        kcdb_cred_unlock_write();
+        return KHM_ERROR_INVALID_PARAM;
+    }
+
+    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_PARAM;
+    }
+
+    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_PARAM;
+        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, 
+    const 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, 
+    const 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_PARAM;
+
+    if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib))) {
+        return KHM_ERROR_INVALID_PARAM;
+    }
+
+    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;
+
+    kcdb_cred_lock_read();
+    if(!kcdb_cred_is_active_cred(vcred)) {
+        code = KHM_ERROR_INVALID_PARAM;
+        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:
+    kcdb_cred_unlock_read();
+    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_PARAM;
+
+    if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib))) {
+        code = KHM_ERROR_INVALID_PARAM;
+        goto _exit_nolock;
+    }
+
+    if(KHM_FAILED(kcdb_type_get_info(attrib->type, &type))) {
+        code = KHM_ERROR_UNKNOWN;
+        goto _exit_nolock;
+    }
+
+    kcdb_cred_lock_read();
+    if(!kcdb_cred_is_active_cred(vcred)) {
+        code = KHM_ERROR_INVALID_PARAM;
+        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) {
+            wchar_t vbuf[KCDB_MAXCCH_NAME];
+
+            if (cbbuf < sizeof(vbuf))
+                buf = vbuf;
+            else
+                buf = PMALLOC(cbbuf);
+
+            code = attrib->compute_cb(vcred,
+                                      attr_id,
+                                      buf,
+                                      &cbbuf);
+            if(KHM_SUCCEEDED(code)) {
+                code = type->toString(buf,
+                                      cbbuf,
+                                      buffer,
+                                      pcbbuf,
+                                      flags);
+            }
+
+            if (buf != vbuf)
+                PFREE(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:
+    kcdb_cred_unlock_read();
+ _exit_nolock:
+    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_PARAM;
+
+    kcdb_cred_lock_read();
+    
+    if(!kcdb_cred_is_active_cred(vcred)) {
+        code = KHM_ERROR_INVALID_PARAM;
+        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:
+
+    kcdb_cred_unlock_read();
+    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_PARAM;
+
+    kcdb_cred_lock_read();
+
+    if(!kcdb_cred_is_active_cred(vcred)) {
+        code = KHM_ERROR_INVALID_PARAM;
+        goto _exit;
+    }
+
+    cred = (kcdb_cred *) vcred;
+
+    kcdb_identity_hold((khm_handle) cred->identity);
+
+    *identity = cred->identity;
+    
+_exit:
+    kcdb_cred_unlock_read();
+    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_cred(vcred)) {
+        code = KHM_ERROR_INVALID_PARAM;
+        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_cred(vcred)) {
+        code = KHM_ERROR_INVALID_PARAM;
+        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;
+
+    kcdb_cred_lock_read();
+    if(!kcdb_cred_is_cred(vcred)) {
+        goto _exit;
+    }
+
+    cred = (kcdb_cred *) vcred;
+
+    if(cred->refcount)
+        goto _exit;
+
+    kcdb_cred_unlock_read();
+    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);
+    PFREE(cred->name);
+    PFREE(cred);
+
+    /*TODO: notifications */
+
+_exit2:
+    kcdb_cred_unlock_write();
+    return;
+
+_exit:
+    kcdb_cred_unlock_read();
+}
+
+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_PARAM;
+        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, 
+                       const 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;
+
+    kcdb_cred_lock_read();
+    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) {
+        khm_octet  vbuf[KCDB_MAXCB_NAME * 2];
+        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) {
+            if (cb1 < sizeof(vbuf))
+                buf1 = vbuf;
+            else
+                buf1 = PMALLOC(cb1);
+
+            if(KHM_FAILED(attrib->compute_cb(vcred1, attr_id, buf1, &cb1)))
+                goto _exit_1;
+        }
+
+        if(cb2) {
+            if (cb1 + cb2 < sizeof(vbuf))
+                buf2 = vbuf + cb1;
+            else
+                buf2 = PMALLOC(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 && (buf1 < (void *)vbuf || 
+                    buf1 >= (void*)(vbuf + sizeof(vbuf))))
+            PFREE(buf1);
+        if(buf2 && (buf2 < (void *)vbuf ||
+                    buf2 >= (void *)(vbuf + sizeof(vbuf))))
+            PFREE(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:
+    kcdb_cred_unlock_read();
+    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;
+
+    kcdb_cred_lock_read();
+    if(!kcdb_cred_is_active_cred(vcred1) ||
+       !kcdb_cred_is_active_cred(vcred2)) {
+
+        code = FALSE;
+        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)) {
+
+        kcdb_credtype * type;
+
+        code = TRUE;
+
+        if (KHM_SUCCEEDED(kcdb_credtype_get_info(cred1->type, &type))) {
+            if (type->is_equal &&
+                (*type->is_equal)(vcred1, vcred2, NULL))
+                code = 0;
+
+            kcdb_credtype_release_info(type);
+        }
+    }
+
+_exit:
+    kcdb_cred_unlock_read();
+    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_PARAM;
+
+    kcdb_cred_lock_read();
+    if (!kcdb_cred_is_active_cred(vcred)) {
+        *pflags = 0;
+        rv = KHM_ERROR_INVALID_PARAM;
+        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)) {
+
+        FILETIME ftc;
+            
+        GetSystemTimeAsFileTime(&ftc);
+        if (CompareFileTime(&ftc, ((FILETIME *) 
+                                   kcdb_cred_buf_get(cred, KCDB_ATTR_EXPIRE)))
+            >= 0)
+            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)) {
+                FILETIME ftc;
+
+                GetSystemTimeAsFileTime(&ftc);
+                if (CompareFileTime(&ftc, ((FILETIME *)
+                                           kcdb_cred_buf_get(cred, KCDB_ATTR_RENEW_EXPIRE))) >= 0)
+                    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) {
+        kcdb_cred_unlock_read();
+        kcdb_cred_lock_write();
+        /* Did we lose a race? */
+        if (kcdb_cred_is_active_cred(vcred))
+            cred->flags = f;
+        else {
+            rv = KHM_ERROR_INVALID_PARAM;
+            f = 0;
+        }
+        kcdb_cred_unlock_write();
+        release_lock = FALSE;
+    }
+#endif
+
+    *pflags = f;
+
+ _exit:
+    if (release_lock)
+        kcdb_cred_unlock_read();
+
+    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;
+
+    kcdb_cred_lock_write();
+    if(!kcdb_cred_is_active_cred(vcred)) {
+        rv = KHM_ERROR_INVALID_PARAM;
+        goto _exit;
+    }
+
+    cred = vcred;
+
+    flags &= ~(KCDB_CRED_FLAG_DELETED);
+    mask &= ~(KCDB_CRED_FLAG_DELETED);
+
+    cred->flags =
+        (cred->flags & (~mask)) |
+        (flags & mask);
+
+ _exit:
+    kcdb_cred_unlock_write();
+    return rv;
+}
index 9cb70ab4d25f89b6d89e2879e366f1e69921897d..49b3953c9f80bcc338e257ce24c96ebd0a5886d0 100644 (file)
@@ -1,71 +1,71 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KCDB_CREDENTIAL_H\r
-#define __KHIMAIRA_KCDB_CREDENTIAL_H\r
-\r
-/* Credentials */\r
-\r
-typedef struct kcdb_cred_t {\r
-    khm_int32   magic;\r
-    khm_ui_8    id; /* serial number */\r
-    kcdb_identity * identity;\r
-    khm_int32   type;\r
-    wchar_t *   name;\r
-\r
-    khm_int32   flags;\r
-    khm_int32   refcount;\r
-\r
-    kcdb_buf    buf;\r
-\r
-    LDCL(struct kcdb_cred_t);\r
-} kcdb_cred;\r
-\r
-#define KCDB_CRED_MAGIC 0x38fb84a6\r
-\r
-extern CRITICAL_SECTION cs_creds;\r
-extern kcdb_cred * kcdb_creds;\r
-extern RWLOCK l_creds;\r
-extern khm_ui_8 kcdb_cred_id;\r
-\r
-#define kcdb_cred_val_exist(c,a)    kcdb_buf_val_exist(&(c)->buf, a)\r
-#define kcdb_cred_buf_exist(c,a)    kcdb_buf_exist(&(c)->buf, a)\r
-#define kcdb_cred_buf_get(c,a)      kcdb_buf_get(&(c)->buf, a)\r
-#define kcdb_cred_buf_size(c,a)     kcdb_buf_size(&(c)->buf, a)\r
-\r
-#define kcdb_cred_is_cred(c)        ((c) && ((kcdb_cred *) c)->magic == KCDB_CRED_MAGIC)\r
-#define kcdb_cred_is_active_cred(c) (kcdb_cred_is_cred(c) && !(((kcdb_cred *) c)->flags & KCDB_CRED_FLAG_DELETED))\r
-\r
-#define kcdb_cred_lock_read()       (LockObtainRead(&l_creds))\r
-#define kcdb_cred_unlock_read()     (LockReleaseRead(&l_creds))\r
-#define kcdb_cred_lock_write()      (LockObtainWrite(&l_creds))\r
-#define kcdb_cred_unlock_write()    (LockReleaseWrite(&l_creds))\r
-\r
-void kcdb_cred_init(void);\r
-void kcdb_cred_exit(void);\r
-void kcdb_cred_check_and_delete(khm_handle vcred);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
index 2d7eeeb1fc72a436069099013fb2d29228938f6b..41ca19a468cb92483492014192639be10c5d9393 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kcreddbinternal.h>\r
-#include<assert.h>\r
-\r
-CRITICAL_SECTION cs_credset;\r
-kcdb_credset * kcdb_credsets = NULL;\r
-kcdb_credset * kcdb_root_credset = NULL;\r
-\r
-void \r
-kcdb_credset_init(void)\r
-{\r
-    khm_handle rc;\r
-\r
-    InitializeCriticalSection(&cs_credset);\r
-    kcdb_credsets = NULL;\r
-\r
-    kcdb_credset_create(&rc);\r
-    kcdb_root_credset = (kcdb_credset *) rc;\r
-    kcdb_root_credset->flags |= KCDB_CREDSET_FLAG_ROOT;\r
-}\r
-\r
-void \r
-kcdb_credset_exit(void)\r
-{\r
-    /*TODO: free the credsets */\r
-    DeleteCriticalSection(&cs_credset);\r
-}\r
-\r
-/* called on an unreleased credset, or with credset::cs held */\r
-void \r
-kcdb_credset_buf_new(kcdb_credset * cs)\r
-{\r
-    cs->clist = PMALLOC(KCDB_CREDSET_INITIAL_SIZE * \r
-                        sizeof(kcdb_credset_credref));\r
-    ZeroMemory(cs->clist, \r
-               KCDB_CREDSET_INITIAL_SIZE * \r
-               sizeof(kcdb_credset_credref));\r
-    cs->nc_clist = KCDB_CREDSET_INITIAL_SIZE;\r
-    cs->nclist = 0;\r
-}\r
-\r
-/* called on an unreleased credset, or with credset::cs held */\r
-void \r
-kcdb_credset_buf_delete(kcdb_credset * cs)\r
-{\r
-    PFREE(cs->clist);\r
-    cs->nc_clist = 0;\r
-    cs->nclist = 0;\r
-}\r
-\r
-void \r
-kcdb_credset_buf_assert_size(kcdb_credset * cs, khm_int32 nclist)\r
-{\r
-    if(cs->nc_clist < nclist) {\r
-        kcdb_credset_credref * new_clist;\r
-        \r
-        /* nclist had better be greater than KCDB_CREDSET_INITIAL_SIZE */\r
-        nclist = KCDB_CREDSET_INITIAL_SIZE + \r
-            (((nclist - (KCDB_CREDSET_INITIAL_SIZE + 1)) / KCDB_CREDSET_GROWTH_FACTOR) + 1) *\r
-            KCDB_CREDSET_GROWTH_FACTOR;\r
-\r
-        new_clist = PCALLOC(nclist, sizeof(kcdb_credset_credref));\r
-\r
-        memcpy(new_clist, cs->clist, cs->nclist * sizeof(kcdb_credset_credref));\r
-\r
-        PFREE(cs->clist);\r
-\r
-        cs->clist = new_clist;\r
-    }\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_create(khm_handle * result)\r
-{\r
-    kcdb_credset * cs;\r
-\r
-    cs = PMALLOC(sizeof(kcdb_credset));\r
-    ZeroMemory(cs, sizeof(kcdb_credset));\r
-\r
-    cs->magic = KCDB_CREDSET_MAGIC;\r
-    InitializeCriticalSection(&(cs->cs));\r
-    LINIT(cs);\r
-    kcdb_credset_buf_new(cs);\r
-    cs->version = 0;\r
-    cs->seal_count = 0;\r
-\r
-    EnterCriticalSection(&cs_credset);\r
-    LPUSH(&kcdb_credsets, cs);\r
-    LeaveCriticalSection(&cs_credset);\r
-\r
-    *result = (khm_handle) cs;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_delete(khm_handle vcredset)\r
-{\r
-    kcdb_credset * cs;\r
-    int i;\r
-\r
-    if(!kcdb_credset_is_credset(vcredset)) {\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    cs = (kcdb_credset *) vcredset;\r
-\r
-    EnterCriticalSection(&cs_credset);\r
-    LDELETE(&kcdb_credsets, cs);\r
-    LeaveCriticalSection(&cs_credset);\r
-\r
-    EnterCriticalSection(&(cs->cs));\r
-    cs->magic = 0;\r
-\r
-    for(i=0;i<cs->nclist;i++) {\r
-        if(cs->clist[i].cred) {\r
-            kcdb_cred_release((khm_handle) cs->clist[i].cred);\r
-        }\r
-    }\r
-    kcdb_credset_buf_delete(cs);\r
-\r
-    LeaveCriticalSection(&(cs->cs));\r
-    DeleteCriticalSection(&(cs->cs));\r
-\r
-    PFREE(cs);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/*! \internal\r
-\r
-Collect credentials from cs2 to cs1 which have already been selected into\r
-cl1 and cl2.\r
-\r
-- Credentials in cl2 that are not in cl1 will get added to cs1\r
-- Credentials in cl1 that are not in cl2 will get removed from cs1\r
-- Credentials in cl1 and cl2 will be updated in cs1\r
-\r
-cl1 and cl2 will be modified.\r
-*/\r
-khm_int32 \r
-kcdb_credset_collect_core(kcdb_credset * cs1,\r
-                          kcdb_cred ** cl1,\r
-                          khm_int32 ncl1,\r
-                          kcdb_credset * cs2,\r
-                          kcdb_cred ** cl2,\r
-                          khm_int32 ncl2,\r
-                          khm_int32 * delta)\r
-{\r
-    int i, j;\r
-    int ldelta = 0;\r
-    khm_int32 rv;\r
-\r
-    /* find matching creds and update them */\r
-    for(i=0; i<ncl1; i++) \r
-        if(cl1[i]) {\r
-            for(j=0; j<ncl2; j++) \r
-                if(cl2[j] && kcdb_creds_is_equal((khm_handle) cl1[i], (khm_handle) cl2[j])) {\r
-                    /* they are equivalent. make them equal */\r
-\r
-                    /* depending on whether any changes were made,\r
-                        update ldelta with the proper bit flag */\r
-\r
-                    rv = kcdb_cred_update(cl1[i], cl2[j]);\r
-                    if (rv == KHM_ERROR_SUCCESS) {\r
-                        kcdb_credset_update_cred_ref((khm_handle) cs1, (khm_handle) cl1[i]);\r
-                        ldelta |= KCDB_DELTA_MODIFY;\r
-                    }\r
-\r
-                    cl2[j] = NULL;\r
-                    cl1[i] = NULL;\r
-                    break;\r
-                }\r
-        }\r
-\r
-    /* all the creds that are left in cl1 need to be removed */\r
-    for(i=0; i<ncl1; i++)\r
-        if(cl1[i]) {\r
-            kcdb_credset_del_cred_ref((khm_handle) cs1, (khm_handle) cl1[i]);\r
-            cl1[i] = NULL;\r
-            ldelta |= KCDB_DELTA_DEL;\r
-        }\r
-\r
-    /* all the creds in cl2 need to be added to cs1 */\r
-    for(j=0; j<ncl2; j++)\r
-        if(cl2[j]) {\r
-            /* duplicate the credential and add it if we are adding it to the\r
-               root credential store. */\r
-            if(cs1 == kcdb_root_credset) {\r
-                khm_handle h;\r
-\r
-                if(KHM_SUCCEEDED(kcdb_cred_dup((khm_handle) cl2[j], &h))) {\r
-                    kcdb_credset_add_cred((khm_handle) cs1, h, -1);\r
-                    kcdb_cred_release(h);\r
-                }\r
-            } else\r
-                kcdb_credset_add_cred((khm_handle) cs1, cl2[j], -1);\r
-            cl2[j] = NULL;\r
-            ldelta |= KCDB_DELTA_ADD;\r
-        }\r
-\r
-    if(delta)\r
-        *delta = ldelta;\r
-\r
-    if((cs1 == kcdb_root_credset) && ldelta) {\r
-        /* something changed in the root credential set */\r
-        kmq_post_message(KMSG_CRED,KMSG_CRED_ROOTDELTA,ldelta,NULL);\r
-    }\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_collect(khm_handle cs_dest,\r
-                    khm_handle cs_src, \r
-                    khm_handle identity, \r
-                    khm_int32 type,\r
-                    khm_int32 * delta)\r
-{\r
-    kcdb_credset * cs;\r
-    kcdb_credset * rcs;\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    kcdb_cred ** r_sel = NULL;\r
-    kcdb_cred ** c_sel = NULL;\r
-    int nr_sel, nc_sel;\r
-    int i;\r
-\r
-    if((cs_src && !kcdb_credset_is_credset(cs_src)) ||\r
-        (cs_dest && !kcdb_credset_is_credset(cs_dest)) ||\r
-        (cs_src == cs_dest)) /* works because credsets use shared\r
-                                handles */\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(identity && !kcdb_is_active_identity(identity))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(cs_src)\r
-        cs = (kcdb_credset *) cs_src;\r
-    else\r
-        cs = kcdb_root_credset;\r
-\r
-    if(cs_dest)\r
-        rcs = (kcdb_credset *) cs_dest;\r
-    else\r
-        rcs = kcdb_root_credset;\r
-\r
-    if (kcdb_credset_is_sealed(rcs))\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-\r
-    EnterCriticalSection(&(cs->cs));\r
-    EnterCriticalSection(&(rcs->cs));\r
-\r
-    /* enumerate through the root and given credential sets and select\r
-       the ones we want */\r
-\r
-    if(rcs->nclist > 0)\r
-        r_sel = PMALLOC(sizeof(kcdb_cred *) * rcs->nclist);\r
-    if(cs->nclist > 0)\r
-        c_sel = PMALLOC(sizeof(kcdb_cred *) * cs->nclist);\r
-    nr_sel = 0;\r
-    nc_sel = 0;\r
-\r
-    for(i=0; i<rcs->nclist; i++) {\r
-        if(rcs->clist[i].cred &&\r
-            (!identity || rcs->clist[i].cred->identity == identity) &&\r
-            (type==KCDB_CREDTYPE_ALL || rcs->clist[i].cred->type == type))\r
-        {\r
-            r_sel[nr_sel++] = rcs->clist[i].cred;\r
-        }\r
-    }\r
-\r
-    for(i=0; i<cs->nclist; i++) {\r
-        if(cs->clist[i].cred &&\r
-            (!identity || cs->clist[i].cred->identity == identity) &&\r
-            (type==KCDB_CREDTYPE_ALL || cs->clist[i].cred->type == type))\r
-        {\r
-            c_sel[nc_sel++] = cs->clist[i].cred;\r
-        }\r
-    }\r
-\r
-    rcs->version++;\r
-\r
-    code = kcdb_credset_collect_core(\r
-        rcs,\r
-        r_sel,\r
-        nr_sel,\r
-        cs,\r
-        c_sel,\r
-        nc_sel,\r
-        delta);\r
-\r
-    LeaveCriticalSection(&(rcs->cs));\r
-    LeaveCriticalSection(&(cs->cs));\r
-\r
-    if(r_sel)\r
-        PFREE(r_sel);\r
-    if(c_sel)\r
-        PFREE(c_sel);\r
-\r
-    if (cs_dest == NULL) {\r
-        kcdb_identity_refresh_all();\r
-    }\r
-\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_collect_filtered(khm_handle cs_dest,\r
-                             khm_handle cs_src,\r
-                             kcdb_cred_filter_func filter,\r
-                             void * rock,\r
-                             khm_int32 * delta)\r
-{\r
-    kcdb_credset * cs;\r
-    kcdb_credset * rcs;\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    kcdb_cred ** r_sel = NULL;\r
-    kcdb_cred ** c_sel = NULL;\r
-    int nr_sel, nc_sel;\r
-    int i;\r
-    khm_int32 cs_f = 0;\r
-    khm_int32 rcs_f = 0;\r
-\r
-    if((cs_src && !kcdb_credset_is_credset(cs_src)) ||\r
-        (cs_dest && !kcdb_credset_is_credset(cs_dest)) ||\r
-        (cs_src == cs_dest)) /* works because credsets use shared\r
-                                handles */\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(cs_src)\r
-        cs = (kcdb_credset *) cs_src;\r
-    else {\r
-        cs = kcdb_root_credset;\r
-        cs_f = KCDB_CREDCOLL_FILTER_ROOT;\r
-    }\r
-\r
-    if(cs_dest)\r
-        rcs = (kcdb_credset *) cs_dest;\r
-    else {\r
-        rcs = kcdb_root_credset;\r
-        rcs_f = KCDB_CREDCOLL_FILTER_ROOT;\r
-    }\r
-\r
-    if (kcdb_credset_is_sealed(rcs))\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-\r
-    EnterCriticalSection(&(cs->cs));\r
-    EnterCriticalSection(&(rcs->cs));\r
-\r
-#ifdef DEBUG\r
-    assert(!(rcs->flags & KCDB_CREDSET_FLAG_ENUM));\r
-    assert(!(cs->flags & KCDB_CREDSET_FLAG_ENUM));\r
-#endif\r
-\r
-    if(rcs->nclist)\r
-        r_sel = PMALLOC(sizeof(kcdb_cred *) * rcs->nclist);\r
-    if(cs->nclist)\r
-        c_sel = PMALLOC(sizeof(kcdb_cred *) * cs->nclist);\r
-    nr_sel = 0;\r
-    nc_sel = 0;\r
-\r
-    rcs->flags |= KCDB_CREDSET_FLAG_ENUM;\r
-\r
-    for(i=0; i<rcs->nclist; i++) {\r
-        if(rcs->clist[i].cred && \r
-           (*filter)((khm_handle)rcs->clist[i].cred, \r
-                     KCDB_CREDCOLL_FILTER_DEST | rcs_f, \r
-                     rock))\r
-        {\r
-            r_sel[nr_sel++] = rcs->clist[i].cred;\r
-        }\r
-    }\r
-\r
-    rcs->flags &= ~KCDB_CREDSET_FLAG_ENUM;\r
-    cs->flags |= KCDB_CREDSET_FLAG_ENUM;\r
-\r
-    for(i=0; i<cs->nclist; i++) {\r
-        if(cs->clist[i].cred && filter((khm_handle)rcs->clist[i].cred, KCDB_CREDCOLL_FILTER_SRC | cs_f, rock))\r
-        {\r
-            c_sel[nc_sel++] = cs->clist[i].cred;\r
-        }\r
-    }\r
-\r
-    cs->flags &= ~KCDB_CREDSET_FLAG_ENUM;\r
-\r
-    rcs->version++;\r
-\r
-    code = kcdb_credset_collect_core(\r
-        rcs,\r
-        r_sel,\r
-        nr_sel,\r
-        cs,\r
-        c_sel,\r
-        nc_sel,\r
-        delta);\r
-\r
-    LeaveCriticalSection(&(rcs->cs));\r
-    LeaveCriticalSection(&(cs->cs));\r
-\r
-    if(r_sel)\r
-        PFREE(r_sel);\r
-    if(c_sel)\r
-        PFREE(c_sel);\r
-\r
-    if (cs_dest == NULL) {\r
-        kcdb_identity_refresh_all();\r
-    }\r
-\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_flush(khm_handle vcredset)\r
-{\r
-    int i;\r
-    kcdb_credset * cs;\r
-\r
-    if(!kcdb_credset_is_credset(vcredset))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cs = (kcdb_credset *) vcredset;\r
-\r
-    if (kcdb_credset_is_sealed(cs))\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-\r
-    EnterCriticalSection(&(cs->cs));\r
-\r
-#ifdef DEBUG\r
-    assert(!(cs->flags & KCDB_CREDSET_FLAG_ENUM));\r
-#endif\r
-\r
-    for(i=0;i<cs->nclist;i++) {\r
-        if(cs->clist[i].cred) {\r
-            kcdb_cred_release((khm_handle) cs->clist[i].cred);\r
-        }\r
-    }\r
-    cs->nclist = 0;\r
-    LeaveCriticalSection(&(cs->cs));\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_extract(khm_handle destcredset, \r
-                    khm_handle sourcecredset, \r
-                    khm_handle identity, \r
-                    khm_int32 type)\r
-{\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    kcdb_credset * dest;\r
-    kcdb_credset * src;\r
-    int isRoot = 0;\r
-    khm_size srcSize = 0;\r
-    int i;\r
-\r
-    if(!kcdb_credset_is_credset(destcredset))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(sourcecredset) {\r
-        if(!kcdb_credset_is_credset(sourcecredset))\r
-            return KHM_ERROR_INVALID_PARAM;\r
-    } else {\r
-        sourcecredset = kcdb_root_credset;\r
-    }\r
-\r
-    if (sourcecredset == kcdb_root_credset)\r
-        isRoot = 1;\r
-\r
-    src = (kcdb_credset *) sourcecredset;\r
-    dest = (kcdb_credset *) destcredset;\r
-\r
-    if (kcdb_credset_is_sealed(dest))\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-\r
-    EnterCriticalSection(&(src->cs));\r
-    EnterCriticalSection(&(dest->cs));\r
-\r
-#ifdef DEBUG\r
-    assert(!(dest->flags & KCDB_CREDSET_FLAG_ENUM));\r
-#endif\r
-\r
-    if(KHM_FAILED(kcdb_credset_get_size(sourcecredset, &srcSize))) {\r
-        code = KHM_ERROR_UNKNOWN;\r
-        goto _exit;\r
-    }\r
-\r
-    kcdb_cred_lock_read();\r
-\r
-    for(i=0; i < (int) srcSize; i++) {\r
-        kcdb_cred * c;\r
-\r
-        c = src->clist[i].cred;\r
-        if(kcdb_cred_is_active_cred((khm_handle) c) &&\r
-            (!identity || c->identity == identity) &&\r
-            (type < 0 || c->type == type))\r
-        {\r
-            if(isRoot) {\r
-                khm_handle newcred;\r
-\r
-                kcdb_cred_unlock_read();\r
-                kcdb_cred_dup((khm_handle) c, &newcred);\r
-                kcdb_credset_add_cred(destcredset, newcred, -1);\r
-                kcdb_cred_release(newcred);\r
-                kcdb_cred_lock_read();\r
-            } else {\r
-                kcdb_cred_unlock_read();\r
-                kcdb_credset_add_cred(destcredset, (khm_handle) c, -1);\r
-                kcdb_cred_lock_read();\r
-            }\r
-        }\r
-    }\r
-\r
-    kcdb_cred_unlock_read();\r
-\r
-_exit:\r
-    LeaveCriticalSection(&(dest->cs));\r
-    LeaveCriticalSection(&(src->cs));\r
-\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_extract_filtered(khm_handle destcredset,\r
-                             khm_handle sourcecredset,\r
-                             kcdb_cred_filter_func filter,\r
-                             void * rock)\r
-{\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    kcdb_credset * dest;\r
-    kcdb_credset * src;\r
-    int isRoot = 0;\r
-    khm_size srcSize = 0;\r
-    int i;\r
-\r
-    if(!kcdb_credset_is_credset(destcredset))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(sourcecredset) {\r
-        if(!kcdb_credset_is_credset(sourcecredset))\r
-            return KHM_ERROR_INVALID_PARAM;\r
-    } else {\r
-        sourcecredset = kcdb_root_credset;\r
-        isRoot = 1;\r
-    }\r
-\r
-    src = (kcdb_credset *) sourcecredset;\r
-    dest = (kcdb_credset *) destcredset;\r
-\r
-    if (kcdb_credset_is_sealed(dest))\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-\r
-    EnterCriticalSection(&(src->cs));\r
-    EnterCriticalSection(&(dest->cs));\r
-\r
-#ifdef DEBUG\r
-    assert(!(dest->flags & KCDB_CREDSET_FLAG_ENUM));\r
-#endif\r
-\r
-    if(KHM_FAILED(kcdb_credset_get_size(sourcecredset, &srcSize))) {\r
-        code = KHM_ERROR_UNKNOWN;\r
-        goto _exit;\r
-    }\r
-\r
-    kcdb_cred_lock_read();\r
-\r
-    dest->flags |= KCDB_CREDSET_FLAG_ENUM;\r
-\r
-    for(i=0; i < (int) srcSize; i++) {\r
-        kcdb_cred * c;\r
-\r
-        c = src->clist[i].cred;\r
-        if(kcdb_cred_is_active_cred((khm_handle) c) &&\r
-            filter(c, 0, rock))\r
-        {\r
-            if(isRoot) {\r
-                khm_handle newcred;\r
-\r
-                kcdb_cred_unlock_read();\r
-                kcdb_cred_dup((khm_handle) c, &newcred);\r
-                kcdb_credset_add_cred(destcredset, newcred, -1);\r
-                kcdb_cred_release(newcred);\r
-                kcdb_cred_lock_read();\r
-            } else {\r
-                kcdb_cred_unlock_read();\r
-                kcdb_credset_add_cred(destcredset, (khm_handle) c, -1);\r
-                kcdb_cred_lock_read();\r
-            }\r
-        }\r
-    }\r
-\r
-    dest->flags &= ~KCDB_CREDSET_FLAG_ENUM;\r
-\r
-    kcdb_cred_unlock_read();\r
-\r
-_exit:\r
-    LeaveCriticalSection(&(dest->cs));\r
-    LeaveCriticalSection(&(src->cs));\r
-\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_apply(khm_handle vcredset, kcdb_cred_apply_func f,\r
-                  void * rock)\r
-{\r
-    kcdb_credset * cs;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    int i;\r
-\r
-    if(vcredset != NULL && !kcdb_credset_is_credset(vcredset))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(vcredset == NULL) {\r
-        cs = kcdb_root_credset;\r
-    } else {\r
-        cs = (kcdb_credset *) vcredset;\r
-    }\r
-\r
-    EnterCriticalSection(&cs->cs);\r
-\r
-#ifdef DEBUG\r
-    assert(!(cs->flags & KCDB_CREDSET_FLAG_ENUM));\r
-#endif\r
-\r
-    cs->flags |= KCDB_CREDSET_FLAG_ENUM;\r
-\r
-    for(i=0; i<cs->nclist; i++) {\r
-        if(!kcdb_cred_is_active_cred(cs->clist[i].cred))\r
-            continue;\r
-\r
-        if(KHM_FAILED(f((khm_handle) cs->clist[i].cred, rock)))\r
-            break;\r
-    }\r
-\r
-    cs->flags &= ~KCDB_CREDSET_FLAG_ENUM;\r
-\r
-    LeaveCriticalSection(&cs->cs);\r
-\r
-    if(i<cs->nclist)\r
-        rv = KHM_ERROR_EXIT;\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_get_cred(khm_handle vcredset,\r
-                     khm_int32 idx,\r
-                     khm_handle * cred)\r
-{\r
-    kcdb_credset * cs;\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-\r
-    if(!kcdb_credset_is_credset(vcredset))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cs = (kcdb_credset *) vcredset;\r
-\r
-    *cred = NULL;\r
-\r
-    EnterCriticalSection(&(cs->cs));\r
-    if(idx < 0 || idx >= cs->nclist)\r
-        code = KHM_ERROR_OUT_OF_BOUNDS;\r
-    else if(!cs->clist[idx].cred || !kcdb_cred_is_active_cred((khm_handle) cs->clist[idx].cred)) {\r
-        code = KHM_ERROR_DELETED;\r
-        if(cs->clist[idx].cred) {\r
-            kcdb_cred_release((khm_handle) cs->clist[idx].cred);\r
-            cs->clist[idx].cred = NULL;\r
-        }\r
-    }\r
-    else {\r
-        kcdb_cred_hold((khm_handle) cs->clist[idx].cred);\r
-        *cred = cs->clist[idx].cred;\r
-    }\r
-    LeaveCriticalSection(&(cs->cs));\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_find_filtered(khm_handle credset,\r
-                          khm_int32 idx_start,\r
-                          kcdb_cred_filter_func f,\r
-                          void * rock,\r
-                          khm_handle * cred,\r
-                          khm_int32 * idx)\r
-{\r
-    kcdb_credset * cs;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    int i;\r
-\r
-    if((credset && !kcdb_credset_is_credset(credset)) || !f)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(credset)\r
-        cs = (kcdb_credset *) credset;\r
-    else\r
-        cs = kcdb_root_credset;\r
-\r
-    EnterCriticalSection(&cs->cs);\r
-\r
-    if(idx_start < 0)\r
-        i = 0;\r
-    else\r
-        i = idx_start + 1;\r
-\r
-#ifdef DEBUG\r
-    assert(!(cs->flags & KCDB_CREDSET_FLAG_ENUM));\r
-#endif\r
-\r
-    cs->flags |= KCDB_CREDSET_FLAG_ENUM;\r
-\r
-    for(; i < cs->nclist; i++) {\r
-        if(kcdb_cred_is_active_cred(cs->clist[i].cred) &&\r
-            (*f)((khm_handle) cs->clist[i].cred, 0, rock) != 0)\r
-            break;\r
-    }\r
-\r
-    cs->flags &= ~KCDB_CREDSET_FLAG_ENUM;\r
-\r
-    if(i < cs->nclist) {\r
-        if (cred) {\r
-            *cred = (khm_handle) cs->clist[i].cred;\r
-            kcdb_cred_hold(*cred);\r
-        }\r
-\r
-        if(idx) {\r
-            *idx = i;\r
-        }\r
-    } else {\r
-        rv = KHM_ERROR_NOT_FOUND;\r
-    }\r
-\r
-    LeaveCriticalSection(&cs->cs);\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_find_cred(khm_handle vcredset,\r
-                       khm_handle vcred_src,\r
-                       khm_handle *cred_dest) {\r
-    kcdb_credset * cs;\r
-    khm_handle cred = NULL;\r
-    int idx;\r
-\r
-    if (!kcdb_credset_is_credset(vcredset))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if (!kcdb_cred_is_active_cred(vcred_src))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cs = (kcdb_credset *) vcredset;\r
-\r
-    EnterCriticalSection(&cs->cs);\r
-    for (idx = 0; idx < cs->nclist; idx++) {\r
-        if (cs->clist[idx].cred &&\r
-            kcdb_creds_is_equal(vcred_src, cs->clist[idx].cred)) {\r
-            cred = cs->clist[idx].cred;\r
-            break;\r
-        }\r
-    }\r
-\r
-    if (cred)\r
-        kcdb_cred_hold(cred);\r
-\r
-    LeaveCriticalSection(&cs->cs);\r
-\r
-    if (cred) {\r
-        if (cred_dest)\r
-            *cred_dest = cred;\r
-        else\r
-            kcdb_cred_release(cred);\r
-\r
-        return KHM_ERROR_SUCCESS;\r
-    } else {\r
-        return KHM_ERROR_NOT_FOUND;\r
-    }\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_del_cred(khm_handle vcredset,\r
-                     khm_int32 idx)\r
-{\r
-    kcdb_credset * cs;\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-\r
-    if(!kcdb_credset_is_credset(vcredset))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cs = (kcdb_credset *) vcredset;\r
-\r
-    if (kcdb_credset_is_sealed(cs))\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-\r
-    EnterCriticalSection(&(cs->cs));\r
-    if(idx < 0 || idx >= cs->nclist) {\r
-        code = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit;\r
-    }\r
-\r
-    if(cs->clist[idx].cred)\r
-        kcdb_cred_release((khm_handle) cs->clist[idx].cred);\r
-\r
-    if (!(cs->flags & KCDB_CREDSET_FLAG_ENUM)) {\r
-\r
-        if(idx + 1 < cs->nclist)\r
-            memmove(&(cs->clist[idx]), \r
-                    &(cs->clist[idx+1]), \r
-                    sizeof(kcdb_credset_credref) * \r
-                    (cs->nclist - (idx + 1)));\r
-\r
-        cs->nclist--;\r
-    } else {\r
-        cs->clist[idx].cred = NULL;\r
-    }\r
-\r
-_exit:\r
-    LeaveCriticalSection(&(cs->cs));\r
-\r
-    return code;\r
-}\r
-\r
-khm_int32 \r
-kcdb_credset_update_cred_ref(khm_handle credset,\r
-                            khm_handle cred)\r
-{\r
-    kcdb_credset * cs;\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    int i;\r
-\r
-    if(!kcdb_credset_is_credset(credset))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cs = (kcdb_credset *) credset;\r
-\r
-    EnterCriticalSection(&(cs->cs));\r
-\r
-    for(i=0; i<cs->nclist; i++) {\r
-        if(cs->clist[i].cred == cred)\r
-            break;\r
-    }\r
-\r
-    if(i<cs->nclist) {\r
-        cs->clist[i].version = cs->version;\r
-    } else {\r
-        code = KHM_ERROR_NOT_FOUND;\r
-    }\r
-\r
-    LeaveCriticalSection(&(cs->cs));\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_del_cred_ref(khm_handle credset,\r
-                         khm_handle cred)\r
-{\r
-    kcdb_credset * cs;\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    int i;\r
-\r
-    if(!kcdb_credset_is_credset(credset))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cs = (kcdb_credset *) credset;\r
-\r
-    if (kcdb_credset_is_sealed(cs))\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-\r
-    EnterCriticalSection(&(cs->cs));\r
-\r
-    for(i=0; i<cs->nclist; i++) {\r
-        if(cs->clist[i].cred == cred)\r
-            break;\r
-    }\r
-\r
-    if(i<cs->nclist) {\r
-        code = kcdb_credset_del_cred(credset, i);\r
-    } else {\r
-        code = KHM_ERROR_NOT_FOUND;\r
-    }\r
-\r
-    LeaveCriticalSection(&(cs->cs));\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_add_cred(khm_handle credset,\r
-                     khm_handle cred,\r
-                     khm_int32 idx)\r
-{\r
-    int new_idx;\r
-    kcdb_credset * cs;\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-\r
-    if(!kcdb_credset_is_credset(credset))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cs = (kcdb_credset *) credset;\r
-\r
-    if (kcdb_credset_is_sealed(cs))\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-\r
-    EnterCriticalSection(&(cs->cs));\r
-\r
-    kcdb_credset_buf_assert_size(cs, cs->nclist + 1);\r
-\r
-    if(idx < 0 || idx > cs->nclist)\r
-        new_idx = cs->nclist;\r
-    else if(idx < cs->nclist){\r
-#ifdef DEBUG\r
-        assert(!(cs->flags & KCDB_CREDSET_FLAG_ENUM));\r
-#endif\r
-        memmove(&(cs->clist[idx+1]), &(cs->clist[idx]), (cs->nclist - idx)*sizeof(cs->clist[0]));\r
-        new_idx = idx;\r
-    } else\r
-        new_idx = idx;\r
-\r
-    kcdb_cred_hold(cred);\r
-\r
-    cs->clist[new_idx].cred = (kcdb_cred *) cred;\r
-    cs->clist[new_idx].version = cs->version;\r
-    cs->nclist++;\r
-\r
-    LeaveCriticalSection(&(cs->cs));\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_get_size(khm_handle credset,\r
-                     khm_size * size)\r
-{\r
-    kcdb_credset * cs;\r
-\r
-    *size = 0;\r
-\r
-    /* we don't rely on this working, since we can't purge a sealed\r
-       credset, although we can measure its size. */\r
-    kcdb_credset_purge(credset);\r
-\r
-    if (credset == NULL)\r
-        cs = kcdb_root_credset;\r
-    else\r
-        cs = (kcdb_credset *) credset;\r
-\r
-    EnterCriticalSection(&(cs->cs));\r
-    /* while it may seem a bit redundant to get a lock, it ensures that\r
-       that the size that we return is consistent with the current state\r
-       of the credential set */\r
-    *size = cs->nclist;\r
-    LeaveCriticalSection(&(cs->cs));\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-kcdb_credset_purge(khm_handle credset)\r
-{\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    kcdb_credset * cs;\r
-    int i,j;\r
-\r
-    if(!kcdb_credset_is_credset(credset))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cs = (kcdb_credset *) credset;\r
-\r
-    if (kcdb_credset_is_sealed(cs))\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-\r
-    EnterCriticalSection(&(cs->cs));\r
-\r
-    /* we can't purge a credset while an enumeration operation is in\r
-       progress. */\r
-    if (cs->flags & KCDB_CREDSET_FLAG_ENUM) {\r
-        code = KHM_ERROR_INVALID_OPERATION;\r
-        goto _exit;\r
-    }\r
-\r
-    for(i=0,j=0; i < cs->nclist; i++) {\r
-        if(cs->clist[i].cred) {\r
-            if(!kcdb_cred_is_active_cred((khm_handle) cs->clist[i].cred)) {\r
-                kcdb_cred_release((khm_handle) cs->clist[i].cred);\r
-            } else if(i != j) {\r
-                cs->clist[j++] = cs->clist[i];\r
-            } else\r
-                j++;\r
-        }\r
-    }\r
-    cs->nclist = j;\r
-\r
- _exit:\r
-    LeaveCriticalSection(&(cs->cs));\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_seal(khm_handle credset) {\r
-    kcdb_credset * cs;\r
-\r
-    if (!kcdb_credset_is_credset(credset))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cs = (kcdb_credset *) credset;\r
-\r
-    EnterCriticalSection(&cs->cs);\r
-    cs->seal_count++;\r
-    LeaveCriticalSection(&cs->cs);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-kcdb_credset_unseal(khm_handle credset) {\r
-    kcdb_credset * cs;\r
-    khm_int32 rv;\r
-\r
-    if (!kcdb_credset_is_credset(credset))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cs = (kcdb_credset *) credset;\r
-\r
-    EnterCriticalSection(&cs->cs);\r
-    if (cs->seal_count > 0) {\r
-        cs->seal_count--;\r
-        rv = KHM_ERROR_SUCCESS;\r
-    } else {\r
-        rv = KHM_ERROR_INVALID_OPERATION;\r
-    }\r
-    LeaveCriticalSection(&cs->cs);\r
-\r
-    return rv;\r
-}\r
-\r
-\r
-/* wrapper for qsort and also parameter gobbling FSM.  Access to this\r
-   function is serialized via cs_credset. */\r
-int __cdecl \r
-kcdb_creds_comp_wrapper(const void * a, const void * b)\r
-{\r
-    static void * rock = NULL;\r
-    static kcdb_cred_comp_func comp = NULL;\r
-\r
-    if(!b) {\r
-        rock = (void *) a;\r
-        return 0;\r
-    }\r
-\r
-    if(!a) {\r
-        comp = (kcdb_cred_comp_func) b;\r
-        return 0;\r
-    }\r
-\r
-    return comp((khm_handle) ((kcdb_credset_credref *)a)->cred, \r
-                (khm_handle) ((kcdb_credset_credref *)b)->cred, \r
-                rock);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_sort(khm_handle credset,\r
-                 kcdb_cred_comp_func comp,\r
-                 void * rock)\r
-{\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    kcdb_credset * cs;\r
-\r
-    if(!kcdb_credset_is_credset(credset))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cs = (kcdb_credset *) credset;\r
-\r
-    if (kcdb_credset_is_sealed(cs))\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-\r
-    EnterCriticalSection(&(cs->cs));\r
-\r
-#ifdef DEBUG\r
-    assert(!(cs->flags & KCDB_CREDSET_FLAG_ENUM));\r
-#endif\r
-\r
-    EnterCriticalSection(&cs_credset);\r
-\r
-    kcdb_creds_comp_wrapper(rock, NULL);\r
-    kcdb_creds_comp_wrapper(NULL, (void *) comp);\r
-\r
-    qsort(cs->clist, cs->nclist,\r
-         sizeof(kcdb_credset_credref), kcdb_creds_comp_wrapper);\r
-\r
-    LeaveCriticalSection(&cs_credset);\r
-\r
-    LeaveCriticalSection(&(cs->cs));\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_comp_generic(khm_handle cred1, \r
-                      khm_handle cred2, \r
-                      void * rock)\r
-{\r
-    kcdb_cred_comp_order * o = (kcdb_cred_comp_order *) rock;\r
-    int i;\r
-    khm_int32 r = 0;\r
-    khm_int32 f1, f2;\r
-    khm_int32 t1, t2;\r
-    khm_int32 pt;\r
-\r
-    for(i=0; i<o->nFields; i++) {\r
-        if (o->fields[i].order & KCDB_CRED_COMP_INITIAL_FIRST) {\r
-\r
-            if (o->fields[i].attrib == KCDB_ATTR_TYPE_NAME ||\r
-                o->fields[i].attrib == KCDB_ATTR_TYPE) {\r
-\r
-                kcdb_cred_get_type(cred1, &t1);\r
-                kcdb_cred_get_type(cred2, &t2);\r
-                kcdb_identity_get_type(&pt);\r
-\r
-                if (t1 == t2)\r
-                    r = 0;\r
-                else if (t1 == pt)\r
-                    r = -1;\r
-                else if (t2 == pt)\r
-                    r = 1;\r
-                else\r
-                    r = 0;\r
-\r
-            } else {\r
-\r
-                kcdb_cred_get_flags(cred1, &f1);\r
-                kcdb_cred_get_flags(cred2, &f2);\r
-\r
-                if (((f1 ^ f2) & KCDB_CRED_FLAG_INITIAL) == 0)\r
-                    r = 0;\r
-                else if (f1 & KCDB_CRED_FLAG_INITIAL)\r
-                    r = -1;\r
-                else\r
-                    r = 1;\r
-\r
-            }\r
-\r
-        } else {\r
-            r = 0;\r
-        }\r
-\r
-        if (r == 0)\r
-            r = kcdb_creds_comp_attr(cred1,cred2,o->fields[i].attrib);\r
-\r
-        if(r != 0) {\r
-            if(o->fields[i].order & KCDB_CRED_COMP_DECREASING)\r
-                r = -r;\r
-            break;\r
-        }\r
-    }\r
-\r
-    return r;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 = PMALLOC(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)
+{
+    PFREE(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 = PCALLOC(nclist, sizeof(kcdb_credset_credref));
+
+        memcpy(new_clist, cs->clist, cs->nclist * sizeof(kcdb_credset_credref));
+
+        PFREE(cs->clist);
+
+        cs->clist = new_clist;
+    }
+}
+
+KHMEXP khm_int32 KHMAPI 
+kcdb_credset_create(khm_handle * result)
+{
+    kcdb_credset * cs;
+
+    cs = PMALLOC(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_PARAM;
+    }
+
+    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));
+
+    PFREE(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_PARAM;
+
+    if(identity && !kcdb_is_active_identity(identity))
+        return KHM_ERROR_INVALID_PARAM;
+
+    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 = PMALLOC(sizeof(kcdb_cred *) * rcs->nclist);
+    if(cs->nclist > 0)
+        c_sel = PMALLOC(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)
+        PFREE(r_sel);
+    if(c_sel)
+        PFREE(c_sel);
+
+    if (cs_dest == NULL) {
+        kcdb_identity_refresh_all();
+    }
+
+    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_PARAM;
+
+    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 = PMALLOC(sizeof(kcdb_cred *) * rcs->nclist);
+    if(cs->nclist)
+        c_sel = PMALLOC(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)
+        PFREE(r_sel);
+    if(c_sel)
+        PFREE(c_sel);
+
+    if (cs_dest == NULL) {
+        kcdb_identity_refresh_all();
+    }
+
+    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_PARAM;
+
+    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_PARAM;
+
+    if(sourcecredset) {
+        if(!kcdb_credset_is_credset(sourcecredset))
+            return KHM_ERROR_INVALID_PARAM;
+    } 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 < 0 || 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_PARAM;
+
+    if(sourcecredset) {
+        if(!kcdb_credset_is_credset(sourcecredset))
+            return KHM_ERROR_INVALID_PARAM;
+    } 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_PARAM;
+
+    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_PARAM;
+
+    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)
+        return KHM_ERROR_INVALID_PARAM;
+
+    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) {
+        if (cred) {
+            *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_PARAM;
+
+    if (!kcdb_cred_is_active_cred(vcred_src))
+        return KHM_ERROR_INVALID_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+        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_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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.  Access to this
+   function is serialized via cs_credset. */
+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_PARAM;
+
+    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
+
+    EnterCriticalSection(&cs_credset);
+
+    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_credset);
+
+    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 t1, t2;
+    khm_int32 pt;
+
+    for(i=0; i<o->nFields; i++) {
+        if (o->fields[i].order & KCDB_CRED_COMP_INITIAL_FIRST) {
+
+            if (o->fields[i].attrib == KCDB_ATTR_TYPE_NAME ||
+                o->fields[i].attrib == KCDB_ATTR_TYPE) {
+
+                kcdb_cred_get_type(cred1, &t1);
+                kcdb_cred_get_type(cred2, &t2);
+                kcdb_identity_get_type(&pt);
+
+                if (t1 == t2)
+                    r = 0;
+                else if (t1 == pt)
+                    r = -1;
+                else if (t2 == pt)
+                    r = 1;
+                else
+                    r = 0;
+
+            } else {
+
+                kcdb_cred_get_flags(cred1, &f1);
+                kcdb_cred_get_flags(cred2, &f2);
+
+                if (((f1 ^ f2) & KCDB_CRED_FLAG_INITIAL) == 0)
+                    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;
+}
index cd216fdd2588406f12ba31337029c343b5f4dd22..ba8ec419e1b528d6e37c6dbd05e9ff1d8f988c4b 100644 (file)
@@ -1,75 +1,75 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KCDB_CREDSET_H\r
-#define __KHIMAIRA_KCDB_CREDSET_H\r
-\r
-/* credset */\r
-\r
-typedef struct kcdb_credset_credref_t {\r
-    khm_int32 version;\r
-    kcdb_cred * cred;\r
-} kcdb_credset_credref;\r
-\r
-typedef struct kcdb_credset_t {\r
-    khm_int32 magic;\r
-    khm_int32 flags;\r
-    CRITICAL_SECTION cs;\r
-\r
-    kcdb_credset_credref * clist;\r
-    khm_int32 nc_clist; /* total capacity */\r
-    khm_int32 nclist;   /* current load */\r
-\r
-    khm_int32 version;  /* data version */\r
-\r
-    khm_int32 seal_count;       /* number of seals applied to the\r
-                                   credset */\r
-\r
-    struct kcdb_credset_t * next;\r
-    struct kcdb_credset_t * prev;\r
-} kcdb_credset;\r
-\r
-#define KCDB_CREDSET_MAGIC 0x63a84f8b\r
-\r
-#define KCDB_CREDSET_FLAG_ROOT 1\r
-\r
-/* the credset is in the process of being enumerated */\r
-#define KCDB_CREDSET_FLAG_ENUM 2\r
-\r
-#define kcdb_credset_is_credset(c) ((c) && ((kcdb_credset *)c)->magic == KCDB_CREDSET_MAGIC)\r
-\r
-#define kcdb_credset_is_sealed(c) ((c)->seal_count != 0)\r
-\r
-#define KCDB_CREDSET_INITIAL_SIZE 256\r
-#define KCDB_CREDSET_GROWTH_FACTOR 256\r
-\r
-void kcdb_credset_init(void);\r
-void kcdb_credset_exit(void);\r
-khm_int32 kcdb_credset_update_cred_ref(\r
-    khm_handle credset,\r
-    khm_handle cred);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 256
+#define KCDB_CREDSET_GROWTH_FACTOR 256
+
+void kcdb_credset_init(void);
+void kcdb_credset_exit(void);
+khm_int32 kcdb_credset_update_cred_ref(
+    khm_handle credset,
+    khm_handle cred);
+
+#endif
index 89bd26b85dcfeef4b08df81a979721e49adbd010..b88852cfcbc4fe08fe9156e7d3767be5d7878cef 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kcreddbinternal.h>\r
-\r
-CRITICAL_SECTION cs_credtype;\r
-kcdb_credtype_i ** kcdb_credtype_tbl = NULL;\r
-kcdb_credtype_i * kcdb_credtypes = NULL;\r
-\r
-void kcdb_credtype_init(void)\r
-{\r
-    InitializeCriticalSection(&cs_credtype);\r
-    kcdb_credtypes = NULL;\r
-\r
-    kcdb_credtype_tbl = PMALLOC(sizeof(kcdb_credtype_i *) * (KCDB_CREDTYPE_MAX_ID+1));\r
-    ZeroMemory(kcdb_credtype_tbl, sizeof(kcdb_credtype_i *) * (KCDB_CREDTYPE_MAX_ID+1));\r
-}\r
-\r
-void kcdb_credtype_exit(void)\r
-{\r
-    /*TODO:Free up the cred types */\r
-    PFREE(kcdb_credtype_tbl);\r
-    DeleteCriticalSection(&cs_credtype);\r
-}\r
-\r
-/* Called with cs_credtype held */\r
-void kcdb_credtype_check_and_delete(khm_int32 id)\r
-{\r
-    kcdb_credtype_i * ict;\r
-    ict = kcdb_credtype_tbl[id];\r
-    if(!ict)\r
-        return;\r
-\r
-    if((ict->flags & KCDB_CTI_FLAG_DELETED) &&\r
-        !ict->refcount)\r
-    {\r
-        kcdb_credtype_tbl[id] = NULL;\r
-        LDELETE(&kcdb_credtypes, ict);\r
-\r
-        PFREE(ict->ct.name);\r
-        if(ict->ct.short_desc)\r
-            PFREE(ict->ct.short_desc);\r
-        if(ict->ct.long_desc)\r
-            PFREE(ict->ct.long_desc);\r
-        if(ict->ct.sub)\r
-            kmq_delete_subscription(ict->ct.sub);\r
-\r
-        PFREE(ict);\r
-    }\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credtype_register(const kcdb_credtype * type, khm_int32 * new_id) \r
-{\r
-    khm_int32 id;\r
-    kcdb_credtype_i * ict;\r
-    size_t cb_name;\r
-    size_t cb_short_desc;\r
-    size_t cb_long_desc;\r
-    int i;\r
-\r
-    if(!type)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(type->id >= KCDB_CREDTYPE_MAX_ID)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(type->name) {\r
-        if(FAILED(StringCbLength(type->name, KCDB_MAXCB_NAME, &cb_name)))\r
-            return KHM_ERROR_TOO_LONG;\r
-        cb_name += sizeof(wchar_t);\r
-    } else\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(type->short_desc) {\r
-        if(FAILED(StringCbLength(type->short_desc, KCDB_MAXCB_SHORT_DESC, &cb_short_desc)))\r
-            return KHM_ERROR_TOO_LONG;\r
-        cb_short_desc += sizeof(wchar_t);\r
-    } else\r
-        cb_short_desc = 0;\r
-\r
-    if(type->long_desc) {\r
-        if(FAILED(StringCbLength(type->long_desc, KCDB_MAXCB_LONG_DESC, &cb_long_desc)))\r
-            return KHM_ERROR_TOO_LONG;\r
-        cb_long_desc += sizeof(wchar_t);\r
-    } else\r
-        cb_long_desc = 0;\r
-\r
-    if(type->sub == NULL)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_credtype);\r
-\r
-    if(type->id < 0) {\r
-        if(KHM_FAILED(kcdb_credtype_get_next_free_id(&id))) {\r
-            LeaveCriticalSection(&cs_credtype);\r
-            return KHM_ERROR_NO_RESOURCES;\r
-        }\r
-    }\r
-    else\r
-        id = type->id;\r
-\r
-    if(kcdb_credtype_tbl[id]) {\r
-        LeaveCriticalSection(&cs_credtype);\r
-        return KHM_ERROR_DUPLICATE;\r
-    }\r
-\r
-    for(i=0;i<=KCDB_CREDTYPE_MAX_ID;i++) {\r
-        if(kcdb_credtype_tbl[i] && !wcscmp(kcdb_credtype_tbl[i]->ct.name, type->name)) {\r
-            LeaveCriticalSection(&cs_credtype);\r
-            return KHM_ERROR_DUPLICATE;\r
-        }\r
-    }\r
-\r
-    ict = PMALLOC(sizeof(kcdb_credtype_i));\r
-    ZeroMemory(ict, sizeof(kcdb_credtype_i));\r
-\r
-    ict->ct.name = PMALLOC(cb_name);\r
-    StringCbCopy(ict->ct.name, cb_name, type->name);\r
-\r
-    if(cb_short_desc) {\r
-        ict->ct.short_desc = PMALLOC(cb_short_desc);\r
-        StringCbCopy(ict->ct.short_desc, cb_short_desc, type->short_desc);\r
-    }\r
-\r
-    if(cb_long_desc) {\r
-        ict->ct.long_desc = PMALLOC(cb_long_desc);\r
-        StringCbCopy(ict->ct.long_desc, cb_long_desc, type->long_desc);\r
-    }\r
-\r
-    ict->ct.id = id;\r
-\r
-    ict->ct.icon = type->icon;\r
-\r
-    ict->ct.sub = type->sub;\r
-\r
-    ict->ct.is_equal = type->is_equal;\r
-\r
-    kcdb_credtype_tbl[id] = ict;\r
-\r
-    LPUSH(&kcdb_credtypes, ict);\r
-\r
-    LeaveCriticalSection(&cs_credtype);\r
-\r
-    kcdb_credtype_post_message(KCDB_OP_INSERT, &(ict->ct));\r
-\r
-    if (new_id)\r
-        *new_id = id;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_credtype_get_info(\r
-    khm_int32 id, \r
-    kcdb_credtype ** type)\r
-{\r
-    int found = 0;\r
-\r
-    if(id < 0 || id > KCDB_CREDTYPE_MAX_ID)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_credtype);\r
-    if(kcdb_credtype_tbl[id] && \r
-        !(kcdb_credtype_tbl[id]->flags & KCDB_CTI_FLAG_DELETED)) \r
-    {\r
-        found = 1;\r
-        if(type) {\r
-            *type = &(kcdb_credtype_tbl[id]->ct);\r
-            kcdb_credtype_hold(kcdb_credtype_tbl[id]);\r
-        }\r
-    } else {\r
-        if(type)\r
-            *type = NULL;\r
-    }\r
-    LeaveCriticalSection(&cs_credtype);\r
-\r
-    if(found)\r
-        return KHM_ERROR_SUCCESS;\r
-    else\r
-        return KHM_ERROR_NOT_FOUND;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_credtype_release_info(kcdb_credtype * type) \r
-{\r
-    kcdb_credtype_i * ict;\r
-\r
-    if(!type)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    ict = (kcdb_credtype_i *) type;\r
-    return kcdb_credtype_release(ict);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_credtype_unregister(khm_int32 id) \r
-{\r
-    kcdb_credtype_i * ict;\r
-\r
-    if(id < 0 || id > KCDB_CREDTYPE_MAX_ID)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_credtype);\r
-    ict = kcdb_credtype_tbl[id];\r
-    ict->flags |= KCDB_CTI_FLAG_DELETED;\r
-    kcdb_credtype_check_and_delete(id);\r
-    LeaveCriticalSection(&cs_credtype);\r
-\r
-    //kcdb_credtype_post_message(KCDB_OP_DELETE, &(ict->ct));\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_handle KHMAPI kcdb_credtype_get_sub(khm_int32 id)\r
-{\r
-    kcdb_credtype_i * t;\r
-    khm_handle s;\r
-\r
-    if(id < 0 || id > KCDB_CREDTYPE_MAX_ID)\r
-        return NULL;\r
-\r
-    EnterCriticalSection(&cs_credtype);\r
-    t = kcdb_credtype_tbl[id];\r
-    if(t)\r
-        s = t->ct.sub;\r
-    else\r
-        s = NULL;\r
-    LeaveCriticalSection(&cs_credtype);\r
-\r
-    return s;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_credtype_describe(\r
-    khm_int32 id,\r
-    wchar_t * buf,\r
-    khm_size * cbbuf,\r
-    khm_int32 flags)\r
-{\r
-    size_t s;\r
-    size_t maxs;\r
-    wchar_t * str;\r
-    kcdb_credtype_i * t;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if(!cbbuf || id < 0 || id > KCDB_CREDTYPE_MAX_ID)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_credtype);\r
-    t = kcdb_credtype_tbl[id];\r
-    if(t) {\r
-        if(flags & KCDB_TS_SHORT) {\r
-            str = (t->ct.short_desc)?t->ct.short_desc:t->ct.name;\r
-            maxs = (t->ct.short_desc)?KCDB_MAXCB_SHORT_DESC:KCDB_MAXCB_NAME;\r
-        } else {\r
-            str = (t->ct.long_desc)?t->ct.long_desc:((t->ct.short_desc)?t->ct.short_desc:t->ct.name);\r
-            maxs = (t->ct.long_desc)?KCDB_MAXCB_LONG_DESC:((t->ct.short_desc)?KCDB_MAXCB_SHORT_DESC:KCDB_MAXCB_NAME);\r
-        }\r
-        StringCbLength(str, maxs, &s);\r
-        s += sizeof(wchar_t);\r
-        if(!buf || *cbbuf < s) {\r
-            *cbbuf = s;\r
-            rv = KHM_ERROR_TOO_LONG;\r
-        } else {\r
-            StringCbCopy(buf, *cbbuf, str);\r
-            *cbbuf = s;\r
-        }\r
-    } else {\r
-        if(buf && *cbbuf > 0)\r
-            *buf = L'\0';\r
-        *cbbuf = 0;\r
-        rv = KHM_ERROR_NOT_FOUND;\r
-    }\r
-    LeaveCriticalSection(&cs_credtype);\r
-\r
-    return rv;\r
-}\r
-\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_credtype_get_name(\r
-    khm_int32 id,\r
-    wchar_t * buf,\r
-    khm_size * cbbuf)\r
-{\r
-    size_t s;\r
-    kcdb_credtype_i * t;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if(!cbbuf || id < 0 || id > KCDB_CREDTYPE_MAX_ID)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_credtype);\r
-    t = kcdb_credtype_tbl[id];\r
-    if(t) {\r
-        StringCbLength(t->ct.name, KCDB_MAXCB_NAME, &s);\r
-        s += sizeof(wchar_t);\r
-        if(!buf || *cbbuf < s) {\r
-            *cbbuf = s;\r
-            rv = KHM_ERROR_TOO_LONG;\r
-        } else {\r
-            StringCbCopy(buf, *cbbuf, t->ct.name);\r
-            *cbbuf = s;\r
-        }\r
-    } else {\r
-        *cbbuf = 0;\r
-        rv = KHM_ERROR_NOT_FOUND;\r
-    }\r
-    LeaveCriticalSection(&cs_credtype);\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_credtype_get_id(\r
-    const wchar_t * name, \r
-    khm_int32 * id)\r
-{\r
-    int i;\r
-\r
-    *id = 0;\r
-    if(!name)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_credtype);\r
-    for(i=0;i <= KCDB_CREDTYPE_MAX_ID; i++) {\r
-        if(kcdb_credtype_tbl[i] && !wcscmp(name, kcdb_credtype_tbl[i]->ct.name))\r
-            break;\r
-    }\r
-    LeaveCriticalSection(&cs_credtype);\r
-\r
-    if(i <= KCDB_CREDTYPE_MAX_ID) {\r
-        *id = i;\r
-        return KHM_ERROR_SUCCESS;\r
-    } else\r
-        return KHM_ERROR_NOT_FOUND;\r
-}\r
-\r
-khm_int32 kcdb_credtype_get_next_free_id(khm_int32 * id) \r
-{\r
-    int i;\r
-\r
-    EnterCriticalSection(&cs_credtype);\r
-    for(i=0;i <= KCDB_CREDTYPE_MAX_ID; i++) {\r
-        if(!kcdb_credtype_tbl[i])\r
-            break;\r
-    }\r
-    LeaveCriticalSection(&cs_credtype);\r
-\r
-    if(i <= KCDB_CREDTYPE_MAX_ID) {\r
-        *id = i;\r
-        return KHM_ERROR_SUCCESS;\r
-    } else {\r
-        *id = -1;\r
-        return KHM_ERROR_NO_RESOURCES;\r
-    }\r
-}\r
-\r
-khm_int32 kcdb_credtype_hold(kcdb_credtype_i * ict) {\r
-    \r
-    if(!ict)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_credtype);\r
-    ict->refcount++;\r
-    LeaveCriticalSection(&cs_credtype);\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-khm_int32 kcdb_credtype_release(kcdb_credtype_i * ict) {\r
-    \r
-    if(!ict)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_credtype);\r
-    ict->refcount--;\r
-    kcdb_credtype_check_and_delete(ict->ct.id);\r
-    LeaveCriticalSection(&cs_credtype);\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-void kcdb_credtype_msg_completion(kmq_message * m) \r
-{\r
-    kcdb_credtype_release((kcdb_credtype_i *) m->vparam);\r
-}\r
-\r
-void kcdb_credtype_post_message(khm_int32 op, kcdb_credtype * type)\r
-{\r
-    kcdb_credtype_hold((kcdb_credtype_i *) type);\r
-    kmq_post_message(KMSG_KCDB, KMSG_KCDB_CREDTYPE, op, (void *) type);\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 = PMALLOC(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 */
+    PFREE(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);
+
+        PFREE(ict->ct.name);
+        if(ict->ct.short_desc)
+            PFREE(ict->ct.short_desc);
+        if(ict->ct.long_desc)
+            PFREE(ict->ct.long_desc);
+        if(ict->ct.sub)
+            kmq_delete_subscription(ict->ct.sub);
+
+        PFREE(ict);
+    }
+}
+
+KHMEXP khm_int32 KHMAPI 
+kcdb_credtype_register(const 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_PARAM;
+
+    if(type->id >= KCDB_CREDTYPE_MAX_ID)
+        return KHM_ERROR_INVALID_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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 = PMALLOC(sizeof(kcdb_credtype_i));
+    ZeroMemory(ict, sizeof(kcdb_credtype_i));
+
+    ict->ct.name = PMALLOC(cb_name);
+    StringCbCopy(ict->ct.name, cb_name, type->name);
+
+    if(cb_short_desc) {
+        ict->ct.short_desc = PMALLOC(cb_short_desc);
+        StringCbCopy(ict->ct.short_desc, cb_short_desc, type->short_desc);
+    }
+
+    if(cb_long_desc) {
+        ict->ct.long_desc = PMALLOC(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;
+
+    ict->ct.is_equal = type->is_equal;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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 {
+            StringCbCopy(buf, *cbbuf, str);
+            *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_PARAM;
+
+    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 {
+            StringCbCopy(buf, *cbbuf, t->ct.name);
+            *cbbuf = s;
+        }
+    } else {
+        *cbbuf = 0;
+        rv = KHM_ERROR_NOT_FOUND;
+    }
+    LeaveCriticalSection(&cs_credtype);
+
+    return rv;
+}
+
+KHMEXP khm_int32 KHMAPI kcdb_credtype_get_id(
+    const wchar_t * name, 
+    khm_int32 * id)
+{
+    int i;
+
+    *id = 0;
+    if(!name)
+        return KHM_ERROR_INVALID_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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);
+}
index aa605d73078d277e67c021800250a3797917718c..3bb0a7d5e802bb00d47ae3e26b481edb692ae543 100644 (file)
@@ -1,55 +1,55 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KCDB_CREDTYPE_H\r
-#define __KHIMAIRA_KCDB_CREDTYPE_H \r
-\r
-/* credtype */\r
-typedef struct kcdb_credtype_i_t {\r
-    kcdb_credtype ct;\r
-    khm_int32 refcount;\r
-    khm_int32 flags;\r
-\r
-    struct kcdb_credtype_i_t * next;\r
-    struct kcdb_credtype_i_t * prev;\r
-} kcdb_credtype_i;\r
-\r
-#define KCDB_CTI_FLAG_DELETED 8\r
-\r
-extern CRITICAL_SECTION cs_credtype;\r
-extern kcdb_credtype_i * kcdb_credtypes;\r
-extern kcdb_credtype_i ** kcdb_credtype_tbl;\r
-\r
-void kcdb_credtype_init(void);\r
-void kcdb_credtype_exit(void);\r
-void kcdb_credtype_check_and_delete(khm_int32 id);\r
-khm_int32 kcdb_credtype_hold(kcdb_credtype_i * ict);\r
-khm_int32 kcdb_credtype_release(kcdb_credtype_i * ict);\r
-void kcdb_credtype_msg_completion(kmq_message * m);\r
-void kcdb_credtype_post_message(khm_int32 op, kcdb_credtype * type);\r
-khm_int32 kcdb_credtype_get_next_free_id(khm_int32 * id);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
index 5d76384d9e35f7c5f2c8dc4909961a6073c78bf2..e14ceec4c2c4c9f2a0ed7effc48a71d8f48a8664 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kcreddbinternal.h>\r
-#include<assert.h>\r
-\r
-static CRITICAL_SECTION cs_ident;\r
-hashtable * kcdb_identities_namemap = NULL;\r
-khm_int32 kcdb_n_identities = 0;\r
-kcdb_identity * kcdb_identities = NULL;\r
-kcdb_identity * kcdb_def_identity = NULL;\r
-khm_handle kcdb_ident_sub = NULL; /* identity provider */\r
-khm_int32  kcdb_ident_cred_type = KCDB_CREDTYPE_INVALID; \r
-/* primary credentials type */\r
-khm_ui_4 kcdb_ident_refresh_cycle = 0;\r
-khm_boolean kcdb_checked_config = FALSE;\r
-khm_boolean kcdb_checking_config = FALSE;\r
-\r
-KHMEXP khm_boolean KHMAPI\r
-kcdb_identity_is_equal(khm_handle identity1,\r
-                       khm_handle identity2)\r
-{\r
-\r
-    return (identity1 == identity2);\r
-\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_set_provider(khm_handle sub)\r
-{\r
-    EnterCriticalSection(&cs_ident);\r
-    if (sub != kcdb_ident_sub) {\r
-        if(kcdb_ident_sub != NULL) {\r
-            kmq_send_sub_msg(kcdb_ident_sub,\r
-                             KMSG_IDENT,\r
-                             KMSG_IDENT_EXIT,\r
-                             0,\r
-                             0);\r
-            kmq_delete_subscription(kcdb_ident_sub);\r
-        }\r
-        kcdb_ident_sub = sub;\r
-\r
-        if (kcdb_ident_sub)\r
-            kmq_send_sub_msg(kcdb_ident_sub,\r
-                             KMSG_IDENT,\r
-                             KMSG_IDENT_INIT,\r
-                             0,\r
-                             0);\r
-    }\r
-    LeaveCriticalSection(&cs_ident);\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_provider(khm_handle * sub)\r
-{\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-    if(kcdb_ident_sub != NULL)\r
-        rv = KHM_ERROR_SUCCESS;\r
-    else\r
-        rv = KHM_ERROR_NOT_FOUND;\r
-    if(sub != NULL)\r
-        *sub = kcdb_ident_sub;\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_set_type(khm_int32 cred_type)\r
-{\r
-    EnterCriticalSection(&cs_ident);\r
-    kcdb_ident_cred_type = cred_type;\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_type(khm_int32 * ptype)\r
-{\r
-    if (!ptype)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-    *ptype = kcdb_ident_cred_type;\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    if (*ptype >= 0)\r
-        return KHM_ERROR_SUCCESS;\r
-    else\r
-        return KHM_ERROR_NOT_FOUND;\r
-}\r
-\r
-/* message completion routine */\r
-void \r
-kcdbint_ident_msg_completion(kmq_message * m) {\r
-    kcdb_identity_release(m->vparam);\r
-}\r
-\r
-void \r
-kcdbint_ident_add_ref(const void * key, void * vid) {\r
-    /* References in the hashtable are not refcounted */\r
-\r
-    // kcdb_identity_hold(vid);\r
-}\r
-\r
-void \r
-kcdbint_ident_del_ref(const void * key, void * vid) {\r
-    /* References in the hashtable are not refcounted */\r
-\r
-    // kcdb_identity_release(vid);\r
-}\r
-\r
-void \r
-kcdbint_ident_init(void) {\r
-    InitializeCriticalSection(&cs_ident);\r
-    kcdb_identities_namemap = hash_new_hashtable(\r
-        KCDB_IDENT_HASHTABLE_SIZE,\r
-        hash_string,\r
-        hash_string_comp,\r
-        kcdbint_ident_add_ref,\r
-        kcdbint_ident_del_ref);\r
-}\r
-\r
-void \r
-kcdbint_ident_exit(void) {\r
-    EnterCriticalSection(&cs_ident);\r
-    hash_del_hashtable(kcdb_identities_namemap);\r
-    LeaveCriticalSection(&cs_ident);\r
-    DeleteCriticalSection(&cs_ident);\r
-}\r
-\r
-/* NOT called with cs_ident held */\r
-KHMEXP khm_boolean KHMAPI \r
-kcdb_identity_is_valid_name(const wchar_t * name)\r
-{\r
-    khm_int32 rv;\r
-\r
-    /* special case.  Note since the string we are comparing with is\r
-       of a known length we don't need to check the length of name. */\r
-    if (!wcscmp(name, L"_Schema"))\r
-        return FALSE;\r
-\r
-    rv = kcdb_identpro_validate_name(name);\r
-\r
-    if(rv == KHM_ERROR_NO_PROVIDER ||\r
-       rv == KHM_ERROR_NOT_IMPLEMENTED)\r
-        return TRUE;\r
-    else\r
-        return KHM_SUCCEEDED(rv);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_create(const wchar_t *name, \r
-                     khm_int32 flags, \r
-                     khm_handle * result) {\r
-    kcdb_identity * id = NULL;\r
-    kcdb_identity * id_tmp = NULL;\r
-    size_t namesize;\r
-\r
-    if(!result || !name)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    *result = NULL;\r
-\r
-    /* is it there already? */\r
-    EnterCriticalSection(&cs_ident);\r
-    id = hash_lookup(kcdb_identities_namemap, (void *) name);\r
-    if(id)\r
-        kcdb_identity_hold((khm_handle) id);\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    if(id) {\r
-        *result = (khm_handle) id;\r
-        return KHM_ERROR_SUCCESS;\r
-    } else if(!(flags & KCDB_IDENT_FLAG_CREATE)) {\r
-        return KHM_ERROR_NOT_FOUND;\r
-    }\r
-\r
-    flags &= ~KCDB_IDENT_FLAG_CREATE;\r
-\r
-    /* nope. create it */\r
-    if((flags & ~KCDB_IDENT_FLAGMASK_RDWR) ||\r
-       (flags & (KCDB_IDENT_FLAG_DEFAULT |\r
-                 KCDB_IDENT_FLAG_SEARCHABLE |\r
-                 KCDB_IDENT_FLAG_STICKY))) {\r
-        /* can't specify this flag in create */\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    if(!kcdb_identity_is_valid_name(name)) {\r
-        return KHM_ERROR_INVALID_NAME;\r
-    }\r
-\r
-    /* we expect the following will succeed since the above\r
-       test passed */\r
-    StringCbLength(name, KCDB_IDENT_MAXCB_NAME, &namesize);\r
-    namesize += sizeof(wchar_t);\r
-\r
-    id = PMALLOC(sizeof(kcdb_identity));\r
-    ZeroMemory(id, sizeof(kcdb_identity));\r
-    id->magic = KCDB_IDENT_MAGIC;\r
-    id->name = PMALLOC(namesize);\r
-    StringCbCopy(id->name, namesize, name);\r
-\r
-    id->flags = (flags & KCDB_IDENT_FLAGMASK_RDWR);\r
-    id->flags |= KCDB_IDENT_FLAG_ACTIVE | KCDB_IDENT_FLAG_EMPTY;\r
-    LINIT(id);\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-    id_tmp = hash_lookup(kcdb_identities_namemap, (void *) id->name);\r
-    if(id_tmp) {\r
-        /* lost a race */\r
-        kcdb_identity_hold((khm_handle) id_tmp);\r
-        *result = (khm_handle) id_tmp;\r
-\r
-        PFREE(id->name);\r
-        PFREE(id);\r
-\r
-        id = NULL;\r
-    } else {\r
-        khm_handle h_cfg;\r
-\r
-        kcdb_identity_hold((khm_handle) id);\r
-        hash_add(kcdb_identities_namemap, \r
-                 (void *) id->name, \r
-                 (void *) id);\r
-        LPUSH(&kcdb_identities, id);\r
-\r
-        if(KHM_SUCCEEDED(kcdb_identity_get_config((khm_handle) id, \r
-                                                  0,\r
-                                                  &h_cfg))) {\r
-            /* don't need to set the KCDB_IDENT_FLAG_CONFIG flags\r
-               since kcdb_identity_get_config() sets it for us. */\r
-            khm_int32 sticky;\r
-\r
-            if (KHM_SUCCEEDED(khc_read_int32(h_cfg, L"Sticky", &sticky)) &&\r
-                sticky) {\r
-                id->flags |= KCDB_IDENT_FLAG_STICKY;\r
-            }\r
-\r
-            khc_close_space(h_cfg);\r
-        }\r
-    }\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    if(id != NULL) {\r
-        *result = (khm_handle) id;\r
-\r
-        kcdb_identpro_notify_create((khm_handle) id);\r
-\r
-        kcdbint_ident_post_message(KCDB_OP_INSERT, id);\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_delete(khm_handle vid) {\r
-    kcdb_identity * id;\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-    if(!kcdb_is_identity(vid)) {\r
-        code = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit;\r
-    }\r
-\r
-    id = (kcdb_identity *) vid;\r
-\r
-    if (kcdb_is_active_identity(vid)) {\r
-\r
-        id->flags &= ~KCDB_IDENT_FLAG_ACTIVE;\r
-\r
-        hash_del(kcdb_identities_namemap, (void *) id->name);\r
-\r
-        LeaveCriticalSection(&cs_ident);\r
-\r
-        kcdbint_ident_post_message(KCDB_OP_DELETE, id);\r
-\r
-        /* Once everybody finishes dealing with the identity deletion,\r
-           we will get called again. */\r
-        return KHM_ERROR_SUCCESS;\r
-    } else if (id->refcount == 0) {\r
-        /* If the identity is not active, it is not in the hashtable\r
-           either */\r
-        LDELETE(&kcdb_identities, id);\r
-\r
-        if (id->name)\r
-            PFREE(id->name);\r
-        PFREE(id);\r
-    }\r
-    /* else, we have an identity that is not active, but has\r
-       outstanding references.  We have to wait until those references\r
-       are freed.  Once they are released, kcdb_identity_delete() will\r
-       be called again. */\r
-\r
- _exit:\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_set_flags(khm_handle vid, \r
-                        khm_int32 flag,\r
-                        khm_int32 mask) {\r
-    kcdb_identity * id;\r
-    khm_int32 oldflags;\r
-    khm_int32 newflags;\r
-    khm_int32 delta = 0;\r
-    khm_int32 rv;\r
-\r
-    if (mask == 0)\r
-        return KHM_ERROR_SUCCESS;\r
-\r
-    if(!kcdb_is_active_identity(vid))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    id = (kcdb_identity *) vid;\r
-\r
-    flag &= mask;\r
-\r
-    if((mask & ~KCDB_IDENT_FLAGMASK_RDWR) ||\r
-       ((flag & KCDB_IDENT_FLAG_INVALID) && (flag & KCDB_IDENT_FLAG_VALID)))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if((mask & KCDB_IDENT_FLAG_DEFAULT) &&\r
-       (flag & KCDB_IDENT_FLAG_DEFAULT)) {\r
-        /* kcdb_identity_set_default already does checking for\r
-           redundant transitions */\r
-        rv = kcdb_identity_set_default(vid);\r
-\r
-        if(KHM_FAILED(rv))\r
-            return rv;\r
-\r
-        mask &= ~KCDB_IDENT_FLAG_DEFAULT;\r
-        flag &= ~KCDB_IDENT_FLAG_DEFAULT;\r
-    }\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-\r
-    if(mask & KCDB_IDENT_FLAG_SEARCHABLE) {\r
-        if(!(flag & KCDB_IDENT_FLAG_SEARCHABLE)) {\r
-            if(id->flags & KCDB_IDENT_FLAG_SEARCHABLE) {\r
-                LeaveCriticalSection(&cs_ident);\r
-                rv = kcdb_identpro_set_searchable(vid, FALSE);\r
-                EnterCriticalSection(&cs_ident);\r
-                if(rv == KHM_ERROR_NO_PROVIDER ||\r
-                    KHM_SUCCEEDED(rv)) {\r
-                    id->flags &= ~KCDB_IDENT_FLAG_SEARCHABLE;\r
-                    delta |= KCDB_IDENT_FLAG_SEARCHABLE;\r
-                }\r
-            }\r
-        } else {\r
-            if(!(id->flags & KCDB_IDENT_FLAG_SEARCHABLE)) {\r
-                LeaveCriticalSection(&cs_ident);\r
-                rv = kcdb_identpro_set_searchable(vid, TRUE);\r
-                EnterCriticalSection(&cs_ident);\r
-                if(rv == KHM_ERROR_NO_PROVIDER ||\r
-                    KHM_SUCCEEDED(rv)) {\r
-                    id->flags |= KCDB_IDENT_FLAG_SEARCHABLE;\r
-                    delta |= KCDB_IDENT_FLAG_SEARCHABLE;\r
-                }\r
-            }\r
-        }\r
-\r
-        flag &= ~KCDB_IDENT_FLAG_SEARCHABLE;\r
-        mask &= ~KCDB_IDENT_FLAG_SEARCHABLE;\r
-    }\r
-\r
-    if (mask & KCDB_IDENT_FLAG_STICKY) {\r
-        if ((flag ^ id->flags) & KCDB_IDENT_FLAG_STICKY) {\r
-            khm_handle h_conf;\r
-\r
-            if (KHM_SUCCEEDED(kcdb_identity_get_config(vid, \r
-                                                       KHM_FLAG_CREATE, \r
-                                                       &h_conf))) {\r
-                khc_write_int32(h_conf, L"Sticky",\r
-                                !!(flag & KCDB_IDENT_FLAG_STICKY));\r
-                khc_close_space(h_conf);\r
-            }\r
-\r
-            id->flags = \r
-                ((id->flags & ~KCDB_IDENT_FLAG_STICKY) |\r
-                 (flag & KCDB_IDENT_FLAG_STICKY));\r
-\r
-            delta |= KCDB_IDENT_FLAG_STICKY;\r
-        }\r
-\r
-        flag &= ~KCDB_IDENT_FLAG_STICKY;\r
-        mask &= ~KCDB_IDENT_FLAG_STICKY;\r
-    }\r
-\r
-    /* deal with every other flag */\r
-\r
-    oldflags = id->flags;\r
-\r
-    id->flags = (id->flags & ~mask) | (flag & mask);\r
-\r
-    if (flag & KCDB_IDENT_FLAG_VALID) {\r
-        id->flags &= ~(KCDB_IDENT_FLAG_INVALID | KCDB_IDENT_FLAG_UNKNOWN);\r
-    }\r
-    if (flag & KCDB_IDENT_FLAG_INVALID) {\r
-        id->flags &= ~(KCDB_IDENT_FLAG_VALID | KCDB_IDENT_FLAG_UNKNOWN);\r
-    }\r
-\r
-    newflags = id->flags;\r
-\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    delta |= newflags ^ oldflags;\r
-\r
-    if((delta & KCDB_IDENT_FLAG_HIDDEN)) {\r
-        kcdbint_ident_post_message(\r
-            (newflags & KCDB_IDENT_FLAG_HIDDEN)?KCDB_OP_HIDE:KCDB_OP_UNHIDE, \r
-            vid);\r
-    }\r
-\r
-    if((delta & KCDB_IDENT_FLAG_SEARCHABLE)) {\r
-        kcdbint_ident_post_message(\r
-            (newflags & KCDB_IDENT_FLAG_SEARCHABLE)?KCDB_OP_SETSEARCH:KCDB_OP_UNSETSEARCH, \r
-            vid);\r
-    }\r
-\r
-    if(delta != 0)\r
-        kcdbint_ident_post_message(KCDB_OP_MODIFY, vid);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_flags(khm_handle vid, \r
-                        khm_int32 * flags) {\r
-    kcdb_identity * id;\r
-\r
-    *flags = 0;\r
-\r
-    if(!kcdb_is_active_identity(vid))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    id = (kcdb_identity *) vid;\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-    *flags = id->flags;\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_name(khm_handle vid, \r
-                       wchar_t * buffer, \r
-                       khm_size * pcbsize) {\r
-    size_t namesize;\r
-    kcdb_identity * id;\r
-\r
-    if(!kcdb_is_active_identity(vid) || !pcbsize)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    id = (kcdb_identity *) vid;\r
-\r
-    if(FAILED(StringCbLength(id->name, KCDB_IDENT_MAXCB_NAME, &namesize)))\r
-        return KHM_ERROR_UNKNOWN;\r
-\r
-    namesize += sizeof(wchar_t);\r
-\r
-    if(!buffer || namesize > *pcbsize) {\r
-        *pcbsize = namesize;\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-\r
-    StringCbCopy(buffer, *pcbsize, id->name);\r
-    *pcbsize = namesize;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_default(khm_handle * pvid) {\r
-    khm_handle def;\r
-\r
-    if (pvid == NULL)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-    def = kcdb_def_identity;\r
-    if (def != NULL)\r
-        kcdb_identity_hold(def);\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    *pvid = def;\r
-\r
-    if (def != NULL)\r
-        return KHM_ERROR_SUCCESS;\r
-    else\r
-        return KHM_ERROR_NOT_FOUND;\r
-}\r
-\r
-static khm_int32\r
-kcdbint_ident_set_default(khm_handle vid,\r
-                          khm_boolean invoke_identpro) {\r
-    kcdb_identity * new_def;\r
-    kcdb_identity * old_def;\r
-    khm_int32 rv;\r
-\r
-    if (vid != NULL && !kcdb_is_active_identity(vid))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    new_def = (kcdb_identity *)vid;\r
-\r
-    if (new_def != NULL && (new_def->flags & KCDB_IDENT_FLAG_DEFAULT))\r
-        return KHM_ERROR_SUCCESS;\r
-\r
-    if ((new_def == NULL && kcdb_def_identity == NULL) ||\r
-        (new_def == kcdb_def_identity))\r
-        return KHM_ERROR_SUCCESS;\r
-\r
-    /* first check with the identity provider if this operation\r
-       is permitted. */\r
-    if (invoke_identpro) {\r
-        rv = kcdb_identpro_set_default(vid);\r
-        if(rv != KHM_ERROR_NO_PROVIDER && KHM_FAILED(rv))\r
-            return rv;\r
-    }\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-\r
-    old_def = kcdb_def_identity;\r
-    kcdb_def_identity = new_def;\r
-\r
-    if(old_def != new_def) {\r
-        if(old_def) {\r
-            old_def->flags &= ~KCDB_IDENT_FLAG_DEFAULT;\r
-            kcdb_identity_release((khm_handle) old_def);\r
-        }\r
-\r
-        if(new_def) {\r
-            new_def->flags |= KCDB_IDENT_FLAG_DEFAULT;\r
-            kcdb_identity_hold((khm_handle) new_def);\r
-        }\r
-\r
-        LeaveCriticalSection(&cs_ident);\r
-\r
-        /* if (invoke_identpro) */\r
-        kcdbint_ident_post_message(KCDB_OP_NEW_DEFAULT, new_def);\r
-    } else {\r
-        LeaveCriticalSection(&cs_ident);\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_set_default(khm_handle vid) {\r
-    return kcdbint_ident_set_default(vid, TRUE);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-kcdb_identity_set_default_int(khm_handle vid) {\r
-    return kcdbint_ident_set_default(vid, FALSE);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_config(khm_handle vid, \r
-                         khm_int32 flags,\r
-                         khm_handle * result) {\r
-    khm_handle hkcdb;\r
-    khm_handle hidents = NULL;\r
-    khm_handle hident = NULL;\r
-    khm_int32 rv;\r
-    kcdb_identity * id;\r
-\r
-    if(kcdb_is_active_identity(vid)) {\r
-        id = (kcdb_identity *) vid;\r
-    } else {\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    hkcdb = kcdb_get_config();\r
-    if(hkcdb) {\r
-        rv = khc_open_space(hkcdb, L"Identity", 0, &hidents);\r
-        if(KHM_FAILED(rv))\r
-            goto _exit;\r
-\r
-        rv = khc_open_space(hidents,\r
-                            id->name, \r
-                            flags | KCONF_FLAG_NOPARSENAME,\r
-                            &hident);\r
-\r
-        if(KHM_FAILED(rv)) {\r
-            khm_int32 oldflags;\r
-            EnterCriticalSection(&cs_ident);\r
-            oldflags = id->flags;\r
-            id->flags &= ~KCDB_IDENT_FLAG_CONFIG;\r
-            LeaveCriticalSection(&cs_ident);\r
-            if (oldflags & KCDB_IDENT_FLAG_CONFIG)\r
-                kcdbint_ident_post_message(KCDB_OP_DELCONFIG, id);\r
-            goto _exit;\r
-        }\r
-\r
-        EnterCriticalSection(&cs_ident);\r
-        id->flags |= KCDB_IDENT_FLAG_CONFIG;\r
-        LeaveCriticalSection(&cs_ident);\r
-\r
-        *result = hident;\r
-    } else\r
-        rv = KHM_ERROR_UNKNOWN;\r
-\r
-_exit:\r
-    if(hidents)\r
-        khc_close_space(hidents);\r
-    if(hkcdb)\r
-        khc_close_space(hkcdb);\r
-    return rv;\r
-}\r
-\r
-/*! \note cs_ident must be available. */\r
-void \r
-kcdbint_ident_post_message(khm_int32 op, kcdb_identity * id) {\r
-    kcdb_identity_hold(id);\r
-    kmq_post_message(KMSG_KCDB, KMSG_KCDB_IDENT, op, (void *) id);\r
-}\r
-\r
-/*! \note cs_ident must be available. */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_hold(khm_handle vid) {\r
-    kcdb_identity * id;\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-    if(kcdb_is_active_identity(vid)) {\r
-        id = vid;\r
-        id->refcount++;\r
-    } else {\r
-        LeaveCriticalSection(&cs_ident);\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-    LeaveCriticalSection(&cs_ident);\r
-    return ERROR_SUCCESS;\r
-}\r
-\r
-/*! \note cs_ident must be available. */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_release(khm_handle vid) {\r
-    kcdb_identity * id;\r
-    khm_int32 refcount;\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-    if(kcdb_is_identity(vid)) {\r
-        id = vid;\r
-        refcount = --id->refcount;\r
-        if(refcount == 0) {\r
-            /* We only delete identities which do not have a\r
-               configuration. */\r
-            if (id->refcount == 0 &&\r
-                !(id->flags & KCDB_IDENT_FLAG_CONFIG))\r
-                kcdb_identity_delete(vid);\r
-        }\r
-    } else {\r
-        LeaveCriticalSection(&cs_ident);\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-    LeaveCriticalSection(&cs_ident);\r
-    return ERROR_SUCCESS;\r
-}\r
-\r
-struct kcdb_idref_result {\r
-    kcdb_identity * ident;\r
-    khm_int32 flags;\r
-    khm_size count;\r
-};\r
-\r
-static khm_int32 KHMAPI \r
-kcdbint_idref_proc(khm_handle cred, void * r) {\r
-    khm_handle vid;\r
-    struct kcdb_idref_result *result;\r
-    khm_int32 flags;\r
-\r
-    result = (struct kcdb_idref_result *) r;\r
-\r
-    if (KHM_SUCCEEDED(kcdb_cred_get_identity(cred, &vid))) {\r
-        if (result->ident == (kcdb_identity *) vid) {\r
-\r
-            result->count++;\r
-            kcdb_cred_get_flags(cred, &flags);\r
-\r
-            if (flags & KCDB_CRED_FLAG_RENEWABLE) {\r
-                result->flags |= KCDB_IDENT_FLAG_CRED_RENEW;\r
-                if (flags & KCDB_CRED_FLAG_INITIAL) {\r
-                    result->flags |= KCDB_IDENT_FLAG_RENEWABLE;\r
-                }\r
-            }\r
-\r
-            if (flags & KCDB_CRED_FLAG_EXPIRED) {\r
-                result->flags |= KCDB_IDENT_FLAG_CRED_EXP;\r
-                if (flags & KCDB_CRED_FLAG_INITIAL) {\r
-                    result->flags |= KCDB_IDENT_FLAG_EXPIRED;\r
-                }\r
-            }\r
-\r
-            if (flags & KCDB_CRED_FLAG_INITIAL) {\r
-                result->flags |= KCDB_IDENT_FLAG_VALID;\r
-            }\r
-        }\r
-\r
-        kcdb_identity_release(vid);\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_refresh(khm_handle vid) {\r
-    kcdb_identity * ident;\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    struct kcdb_idref_result result;\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-\r
-    if (!kcdb_is_active_identity(vid)) {\r
-        code = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit;\r
-    }\r
-\r
-    ident = (kcdb_identity *) vid;\r
-\r
-    result.ident = ident;\r
-    result.flags = 0;\r
-    result.count = 0;\r
-\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    kcdb_credset_apply(NULL, kcdbint_idref_proc, &result);\r
-\r
-    if (result.count == 0)\r
-        result.flags |= KCDB_IDENT_FLAG_EMPTY;\r
-\r
-    kcdb_identity_set_flags(vid, result.flags,\r
-                            KCDB_IDENT_FLAGMASK_RDWR &\r
-                            ~(KCDB_IDENT_FLAG_DEFAULT |\r
-                              KCDB_IDENT_FLAG_SEARCHABLE |\r
-                              KCDB_IDENT_FLAG_STICKY));\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-    ident->refresh_cycle = kcdb_ident_refresh_cycle;\r
-\r
- _exit:\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    if (code == 0)\r
-        code = kcdb_identpro_update(vid);\r
-\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_refresh_all(void) {\r
-    kcdb_identity * ident;\r
-    kcdb_identity * next;\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    int hit_count;\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-\r
-    kcdb_ident_refresh_cycle++;\r
-\r
-    /* The do-while loop is here to account for race conditions.  We\r
-       release cs_ident in the for loop, so we don't actually have a\r
-       guarantee that we traversed the whole identity list at the end.\r
-       We repeat until all the identities are uptodate. */\r
-\r
-    do {\r
-        hit_count = 0;\r
-\r
-        for (ident = kcdb_identities; \r
-             ident != NULL;\r
-             ident = next) {\r
-\r
-            if (!kcdb_is_active_identity(ident) ||\r
-                ident->refresh_cycle == kcdb_ident_refresh_cycle) {\r
-                next = LNEXT(ident);\r
-                continue;\r
-            }\r
-\r
-            kcdb_identity_hold((khm_handle) ident);\r
-\r
-            LeaveCriticalSection(&cs_ident);\r
-\r
-            kcdb_identity_refresh((khm_handle) ident);\r
-\r
-            EnterCriticalSection(&cs_ident);\r
-\r
-            next = LNEXT(ident);\r
-            kcdb_identity_release((khm_handle) ident);\r
-\r
-            hit_count++;\r
-        }\r
-\r
-    } while (hit_count > 0);\r
-\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    return code;\r
-}\r
-\r
-/*****************************************/\r
-/* Custom property functions             */\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_set_attr(khm_handle vid,\r
-                       khm_int32 attr_id,\r
-                       void * buffer,\r
-                       khm_size cbbuf)\r
-{\r
-    kcdb_identity * id = NULL;\r
-    kcdb_attrib * attrib = NULL;\r
-    kcdb_type * type = NULL;\r
-    khm_size slot;\r
-    khm_size cbdest;\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-    if(!kcdb_is_active_identity(vid)) {\r
-        LeaveCriticalSection(&cs_ident);\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    id = (kcdb_identity *) vid;\r
-\r
-    if(!(id->flags & KCDB_IDENT_FLAG_ATTRIBS)) {\r
-        kcdb_buf_new(&id->buf, KCDB_BUF_DEFAULT);\r
-        id->flags |= KCDB_IDENT_FLAG_ATTRIBS;\r
-    }\r
-\r
-    if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib))) {\r
-        LeaveCriticalSection(&cs_ident);\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-#if 0\r
-    /* actually, even if an attribute is computed, we still allow\r
-       those values to be set.  This is because computing values\r
-       is only for credentials.  If a computed value is used as a\r
-       property in any other object, it is treated as a regular value\r
-       */\r
-    if(attrib->flags & KCDB_ATTR_FLAG_COMPUTED)\r
-    {\r
-        LeaveCriticalSection(&cs_ident);\r
-        kcdb_attrib_release_info(attrib);\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-    }\r
-#endif\r
-\r
-    if (buffer == NULL) {\r
-        /* we are removing a value */\r
-        slot = kcdb_buf_slot_by_id(&id->buf, attr_id);\r
-        if (slot != KCDB_BUF_INVALID_SLOT &&\r
-            kcdb_buf_exist(&id->buf, slot))\r
-            kcdb_buf_alloc(&id->buf, slot, attr_id, 0);\r
-        code = KHM_ERROR_SUCCESS;\r
-        goto _exit;\r
-    }\r
-\r
-    if(KHM_FAILED(kcdb_type_get_info(attrib->type, &type))) {\r
-        LeaveCriticalSection(&cs_ident);\r
-        kcdb_attrib_release_info(attrib);\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    if(!(type->isValid(buffer,cbbuf))) {\r
-        code = KHM_ERROR_TYPE_MISMATCH;\r
-        goto _exit;\r
-    }\r
-\r
-    if((type->dup(buffer, cbbuf, NULL, &cbdest)) != KHM_ERROR_TOO_LONG) {\r
-        code = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit;\r
-    }\r
-\r
-    kcdb_buf_alloc(&id->buf, KCDB_BUF_APPEND, attr_id, cbdest);\r
-    slot = kcdb_buf_slot_by_id(&id->buf, attr_id);\r
-    if(slot == KCDB_BUF_INVALID_SLOT || !kcdb_buf_exist(&id->buf, slot)) {\r
-        code = KHM_ERROR_NO_RESOURCES;\r
-        goto _exit;\r
-    }\r
-\r
-    if(KHM_FAILED(code =\r
-        type->dup(buffer, cbbuf, kcdb_buf_get(&id->buf, slot), &cbdest)))\r
-    {\r
-        kcdb_buf_alloc(&id->buf, slot, attr_id, 0);\r
-        goto _exit;\r
-    }\r
-\r
-    kcdb_buf_set_value_flag(&id->buf, slot);\r
-\r
-_exit:\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    if(attrib)\r
-        kcdb_attrib_release_info(attrib);\r
-    if(type)\r
-        kcdb_type_release_info(type);\r
-\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_set_attrib(khm_handle vid,\r
-                         const wchar_t * attr_name,\r
-                         void * buffer,\r
-                         khm_size cbbuf)\r
-{\r
-    khm_int32 attr_id = -1;\r
-\r
-    if(KHM_FAILED(kcdb_attrib_get_id(attr_name, &attr_id)))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    return kcdb_identity_set_attr(\r
-        vid,\r
-        attr_id,\r
-        buffer,\r
-        cbbuf);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_attr(khm_handle vid,\r
-                       khm_int32 attr_id,\r
-                       khm_int32 * attr_type,\r
-                       void * buffer,\r
-                       khm_size * pcbbuf)\r
-{\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    kcdb_identity * id = NULL;\r
-    kcdb_attrib * attrib = NULL;\r
-    kcdb_type * type = NULL;\r
-    khm_size slot;\r
-\r
-    if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib))) {\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    if(KHM_FAILED(kcdb_type_get_info(attrib->type, &type))) {\r
-        kcdb_attrib_release_info(attrib);\r
-        return KHM_ERROR_UNKNOWN;\r
-    }\r
-\r
-    if(attr_type)\r
-        *attr_type = attrib->type;\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-\r
-    if(!kcdb_is_active_identity(vid)) {\r
-        code = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit;\r
-    }\r
-\r
-    id = (kcdb_identity *) vid;\r
-\r
-    if(!(id->flags & KCDB_IDENT_FLAG_ATTRIBS) ||\r
-        (slot = kcdb_buf_slot_by_id(&id->buf, attr_id)) == KCDB_BUF_INVALID_SLOT ||\r
-        !kcdb_buf_val_exist(&id->buf, slot)) \r
-    {\r
-        code = KHM_ERROR_NOT_FOUND;\r
-        goto _exit;\r
-    }\r
-\r
-    if(!buffer && !pcbbuf) {\r
-        /* in this case the caller is only trying to determine if the field\r
-            contains data.  If we get here, then the value exists. */\r
-        code = KHM_ERROR_SUCCESS;\r
-        goto _exit;\r
-    }\r
-\r
-#if 0\r
-    if(attrib->flags & KCDB_ATTR_FLAG_COMPUTED) {\r
-        /* we should never hit this case */\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-        code = KHM_ERROR_INVALID_OPERATION;\r
-    } else {\r
-#endif\r
-        code = type->dup(\r
-            kcdb_buf_get(&id->buf, slot),\r
-            kcdb_buf_size(&id->buf, slot),\r
-            buffer,\r
-            pcbbuf);\r
-#if 0\r
-    }\r
-#endif\r
-\r
-_exit:\r
-    LeaveCriticalSection(&cs_ident);\r
-    if(type)\r
-        kcdb_type_release_info(type);\r
-    if(attrib)\r
-        kcdb_attrib_release_info(attrib);\r
-\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_attrib(khm_handle vid,\r
-                         const wchar_t * attr_name,\r
-                         khm_int32 * attr_type,\r
-                         void * buffer,\r
-                         khm_size * pcbbuf)\r
-{\r
-    khm_int32 attr_id = -1;\r
-\r
-    if(KHM_FAILED(kcdb_attrib_get_id(attr_name, &attr_id)))\r
-        return KHM_ERROR_NOT_FOUND;\r
-\r
-    return kcdb_identity_get_attr(vid,\r
-                                  attr_id,\r
-                                  attr_type,\r
-                                  buffer,\r
-                                  pcbbuf);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_attr_string(khm_handle vid,\r
-                              khm_int32 attr_id,\r
-                              wchar_t * buffer,\r
-                              khm_size * pcbbuf,\r
-                              khm_int32 flags)\r
-{\r
-    khm_int32 code = KHM_ERROR_SUCCESS;\r
-    kcdb_identity * id = NULL;\r
-    kcdb_attrib * attrib = NULL;\r
-    kcdb_type * type = NULL;\r
-    khm_size slot;\r
-\r
-    if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib))) {\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    if(KHM_FAILED(kcdb_type_get_info(attrib->type, &type))) {\r
-        kcdb_attrib_release_info(attrib);\r
-        return KHM_ERROR_UNKNOWN;\r
-    }\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-\r
-    if(!kcdb_is_active_identity(vid)) {\r
-        code = KHM_ERROR_INVALID_PARAM;\r
-        goto _exit;\r
-    }\r
-\r
-    id = (kcdb_identity *) vid;\r
-\r
-    if(!(id->flags & KCDB_IDENT_FLAG_ATTRIBS) ||\r
-        (slot = kcdb_buf_slot_by_id(&id->buf, attr_id)) == KCDB_BUF_INVALID_SLOT ||\r
-        !kcdb_buf_val_exist(&id->buf, slot)) \r
-    {\r
-        code = KHM_ERROR_NOT_FOUND;\r
-        goto _exit;\r
-    }\r
-\r
-    if(!buffer && !pcbbuf) {\r
-        /* in this case the caller is only trying to determine if the field\r
-            contains data.  If we get here, then the value exists */\r
-        code = KHM_ERROR_SUCCESS;\r
-        goto _exit;\r
-    }\r
-\r
-#if 0\r
-    if(attrib->flags & KCDB_ATTR_FLAG_COMPUTED) {\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-        code = KHM_ERROR_INVALID_OPERATION;\r
-    } else {\r
-#endif\r
-        if(kcdb_buf_exist(&id->buf, slot)) {\r
-            code = type->toString(\r
-                kcdb_buf_get(&id->buf, slot),\r
-                kcdb_buf_size(&id->buf, slot),\r
-                buffer,\r
-                pcbbuf,\r
-                flags);\r
-        } else\r
-            code = KHM_ERROR_NOT_FOUND;\r
-#if 0\r
-    }\r
-#endif\r
-\r
-_exit:\r
-    LeaveCriticalSection(&cs_ident);\r
-    if(type)\r
-        kcdb_type_release_info(type);\r
-    if(attrib)\r
-        kcdb_attrib_release_info(attrib);\r
-\r
-    return code;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_attrib_string(khm_handle vid,\r
-                                const wchar_t * attr_name,\r
-                                wchar_t * buffer,\r
-                                khm_size * pcbbuf,\r
-                                khm_int32 flags)\r
-{\r
-    khm_int32 attr_id = -1;\r
-\r
-    if(KHM_FAILED(kcdb_attrib_get_id(attr_name, &attr_id)))\r
-        return KHM_ERROR_NOT_FOUND;\r
-\r
-    return kcdb_identity_get_attr_string(\r
-        vid,\r
-        attr_id,\r
-        buffer,\r
-        pcbbuf,\r
-        flags);\r
-}\r
-\r
-/*****************************************/\r
-/* Identity provider interface functions */\r
-\r
-/* NOT called with cs_ident held */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identpro_validate_name(const wchar_t * name)\r
-{\r
-    kcdb_ident_name_xfer namex;\r
-    khm_handle sub;\r
-    khm_size cch;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    /* we need to verify the length and the contents of the string\r
-       before calling the identity provider */\r
-    if(FAILED(StringCchLength(name, KCDB_IDENT_MAXCCH_NAME, &cch)))\r
-        return KHM_ERROR_TOO_LONG;\r
-\r
-    /* We can't really make an assumption about the valid characters\r
-       in an identity.  So we let the identity provider decide */\r
-#ifdef VALIDATE_IDENTIY_CHARACTERS\r
-    if(wcsspn(name, KCDB_IDENT_VALID_CHARS) != cch)\r
-        return KHM_ERROR_INVALID_NAME;\r
-#endif\r
-    \r
-    EnterCriticalSection(&cs_ident);\r
-    if(kcdb_ident_sub != NULL) {\r
-        sub = kcdb_ident_sub;\r
-    } else {\r
-        sub = NULL;\r
-        rv = KHM_ERROR_NO_PROVIDER;\r
-    }\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    if(sub != NULL) {\r
-        ZeroMemory(&namex, sizeof(namex));\r
-\r
-        namex.name_src = name;\r
-        namex.result = KHM_ERROR_NOT_IMPLEMENTED;\r
-\r
-        kmq_send_sub_msg(sub,\r
-                         KMSG_IDENT,\r
-                         KMSG_IDENT_VALIDATE_NAME,\r
-                         0,\r
-                         (void *) &namex);\r
-\r
-        rv = namex.result;\r
-    }\r
-    \r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identpro_validate_identity(khm_handle identity)\r
-{\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    khm_handle sub;\r
-\r
-    if(!kcdb_is_active_identity(identity))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-    if(kcdb_ident_sub != NULL) {\r
-        sub = kcdb_ident_sub;\r
-    } else {\r
-        sub = NULL;\r
-        rv = KHM_ERROR_NO_PROVIDER;\r
-    }\r
-    LeaveCriticalSection(&cs_ident);\r
-    \r
-    if(sub != NULL) {\r
-        rv = kmq_send_sub_msg(sub,\r
-                              KMSG_IDENT,\r
-                              KMSG_IDENT_VALIDATE_IDENTITY,\r
-                              0,\r
-                              (void *) identity);\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identpro_canon_name(const wchar_t * name_in, \r
-                         wchar_t * name_out, \r
-                         khm_size * cb_name_out)\r
-{\r
-    khm_handle sub;\r
-    kcdb_ident_name_xfer namex;\r
-    wchar_t name_tmp[KCDB_IDENT_MAXCCH_NAME];\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    khm_size cch;\r
-\r
-    if(cb_name_out == 0 ||\r
-       FAILED(StringCchLength(name_in, KCDB_IDENT_MAXCCH_NAME, &cch)))\r
-        return KHM_ERROR_INVALID_NAME;\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-    if(kcdb_ident_sub != NULL) {\r
-        sub = kcdb_ident_sub;\r
-    } else {\r
-        sub = NULL;\r
-        rv = KHM_ERROR_NO_PROVIDER;\r
-    }\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    if(sub != NULL) {\r
-        ZeroMemory(&namex, sizeof(namex));\r
-        ZeroMemory(name_tmp, sizeof(name_tmp));\r
-\r
-        namex.name_src = name_in;\r
-        namex.name_dest = name_tmp;\r
-        namex.cb_name_dest = sizeof(name_tmp);\r
-        namex.result = KHM_ERROR_NOT_IMPLEMENTED;\r
-\r
-        rv = kmq_send_sub_msg(sub,\r
-                              KMSG_IDENT,\r
-                              KMSG_IDENT_CANON_NAME,\r
-                              0,\r
-                              (void *) &namex);\r
-        \r
-        if(KHM_SUCCEEDED(namex.result)) {\r
-            const wchar_t * name_result;\r
-            khm_size cb;\r
-\r
-            if(name_in[0] != 0 && name_tmp[0] == 0)\r
-                name_result = name_tmp;\r
-            else\r
-                name_result = name_in;\r
-                       \r
-            if(FAILED(StringCbLength(name_result, KCDB_IDENT_MAXCB_NAME, &cb)))\r
-                rv = KHM_ERROR_UNKNOWN;\r
-            else {\r
-                cb += sizeof(wchar_t);\r
-                if(name_out == 0 || *cb_name_out < cb) {\r
-                    rv = KHM_ERROR_TOO_LONG;\r
-                    *cb_name_out = cb;\r
-                } else {\r
-                    StringCbCopy(name_out, *cb_name_out, name_result);\r
-                    *cb_name_out = cb;\r
-                    rv = KHM_ERROR_SUCCESS;\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identpro_compare_name(const wchar_t * name1,\r
-                           const wchar_t * name2)\r
-{\r
-    khm_handle sub;\r
-    kcdb_ident_name_xfer namex;\r
-    khm_int32 rv = 0;\r
-\r
-    /* Generally in kcdb_identpro_* functions we don't emulate\r
-       any behavior if the provider is not available, but lacking\r
-       a way to make this known, we emulate here */\r
-    rv = wcscmp(name1, name2);\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-    if(kcdb_ident_sub != NULL) {\r
-        sub = kcdb_ident_sub;\r
-    } else {\r
-        sub = NULL;\r
-    }\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    if(sub != NULL) {\r
-        ZeroMemory(&namex, sizeof(namex));\r
-        namex.name_src = name1;\r
-        namex.name_alt = name2;\r
-        namex.result = rv;\r
-\r
-        kmq_send_sub_msg(sub,\r
-                         KMSG_IDENT,\r
-                         KMSG_IDENT_COMPARE_NAME,\r
-                         0,\r
-                         (void *) &namex);\r
-\r
-        rv = namex.result;\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identpro_set_default(khm_handle identity)\r
-{\r
-    khm_handle sub;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if((identity != NULL) && \r
-       !kcdb_is_active_identity(identity))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-    if(kcdb_ident_sub != NULL) {\r
-        sub = kcdb_ident_sub;\r
-    } else {\r
-        sub = NULL;\r
-        rv = KHM_ERROR_NO_PROVIDER;\r
-    }\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    if(sub != NULL) {\r
-        rv = kmq_send_sub_msg(sub,\r
-                              KMSG_IDENT,\r
-                              KMSG_IDENT_SET_DEFAULT,\r
-                              (identity != NULL),\r
-                              (void *) identity);\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identpro_set_searchable(khm_handle identity,\r
-                             khm_boolean searchable)\r
-{\r
-    khm_handle sub;\r
-       khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-       if(!kcdb_is_active_identity(identity))\r
-               return KHM_ERROR_INVALID_PARAM;\r
-\r
-       EnterCriticalSection(&cs_ident);\r
-       if(kcdb_ident_sub != NULL) {\r
-        sub = kcdb_ident_sub;\r
-       } else {\r
-        sub = NULL;\r
-               rv = KHM_ERROR_NO_PROVIDER;\r
-       }\r
-       LeaveCriticalSection(&cs_ident);\r
-\r
-    if(sub != NULL) {\r
-        rv = kmq_send_sub_msg(\r
-                              sub,\r
-                              KMSG_IDENT,\r
-                              KMSG_IDENT_SET_SEARCHABLE,\r
-                              searchable,\r
-                              (void *) identity);\r
-    }\r
-\r
-       return rv;\r
-}\r
-\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identpro_update(khm_handle identity)\r
-{\r
-    khm_handle sub;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if(!kcdb_is_active_identity(identity))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-    if(kcdb_ident_sub != NULL) {\r
-        sub = kcdb_ident_sub;\r
-    } else {\r
-        sub = NULL;\r
-        rv = KHM_ERROR_NO_PROVIDER;\r
-    }\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    if(sub != NULL) {\r
-        rv = kmq_send_sub_msg(sub,\r
-                              KMSG_IDENT,\r
-                              KMSG_IDENT_UPDATE,\r
-                              0,\r
-                              (void *) identity);\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identpro_notify_create(khm_handle identity)\r
-{\r
-    khm_handle sub;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if(!kcdb_is_active_identity(identity))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-    if(kcdb_ident_sub != NULL) {\r
-        sub = kcdb_ident_sub;\r
-    } else {\r
-        sub = NULL;\r
-        rv = KHM_ERROR_NO_PROVIDER;\r
-    }\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    if(sub != NULL) {\r
-        rv = kmq_send_sub_msg(\r
-            sub,\r
-            KMSG_IDENT,\r
-            KMSG_IDENT_NOTIFY_CREATE,\r
-            0,\r
-            (void *) identity);\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identpro_get_ui_cb(void * rock)\r
-{\r
-    khm_handle sub;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-    if(kcdb_ident_sub != NULL) {\r
-        sub = kcdb_ident_sub;\r
-    } else {\r
-        sub = NULL;\r
-        rv = KHM_ERROR_NO_PROVIDER;\r
-    }\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    if(sub != NULL) {\r
-        rv = kmq_send_sub_msg(\r
-            sub,\r
-            KMSG_IDENT,\r
-            KMSG_IDENT_GET_UI_CALLBACK,\r
-            0,\r
-            rock);\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_enum(khm_int32 and_flags,\r
-                   khm_int32 eq_flags,\r
-                   wchar_t * name_buf,\r
-                   khm_size * pcb_buf,\r
-                   khm_size * pn_idents)\r
-{\r
-    kcdb_identity * id;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    khm_size cb_req = 0;\r
-    khm_size n_idents = 0;\r
-    size_t cb_curr;\r
-    size_t cch_curr;\r
-    size_t cch_left;\r
-    HRESULT hr;\r
-\r
-    if ((name_buf == NULL && pcb_buf == NULL && pn_idents == NULL) ||\r
-        (name_buf != NULL && pcb_buf == NULL))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    eq_flags &= and_flags;\r
-\r
-    EnterCriticalSection(&cs_ident);\r
-\r
-    if (!kcdb_checked_config) {\r
-        khm_handle h_kcdb = NULL;\r
-        khm_handle h_idents = NULL;\r
-        khm_handle h_ident = NULL;\r
-\r
-        kcdb_checked_config = TRUE;\r
-        kcdb_checking_config = TRUE;\r
-\r
-        h_kcdb = kcdb_get_config();\r
-        if (!h_kcdb)\r
-            goto _config_check_cleanup;\r
-        if(KHM_FAILED(khc_open_space(h_kcdb, L"Identity", 0, &h_idents)))\r
-            goto _config_check_cleanup;\r
-\r
-        while(KHM_SUCCEEDED(khc_enum_subspaces(h_idents,\r
-                                               h_ident,\r
-                                               &h_ident))) {\r
-\r
-            wchar_t wname[KCDB_IDENT_MAXCCH_NAME];\r
-            khm_size cb;\r
-            khm_handle t_id;\r
-\r
-            cb = sizeof(wname);\r
-            if (KHM_FAILED(khc_get_config_space_name(h_ident,\r
-                                                     wname,\r
-                                                     &cb)))\r
-                continue;\r
-\r
-            LeaveCriticalSection(&cs_ident);\r
-\r
-            if (KHM_SUCCEEDED(kcdb_identity_create(wname,\r
-                                                   KCDB_IDENT_FLAG_CREATE,\r
-                                                   &t_id)))\r
-                kcdb_identity_release(t_id);\r
-\r
-            EnterCriticalSection(&cs_ident);\r
-        }\r
-\r
-    _config_check_cleanup:\r
-        if (h_kcdb)\r
-            khc_close_space(h_kcdb);\r
-        if (h_idents)\r
-            khc_close_space(h_idents);\r
-\r
-        kcdb_checking_config = FALSE;\r
-    }\r
-\r
-    for ( id = kcdb_identities;\r
-          id != NULL;\r
-          id = LNEXT(id) ) {\r
-        if (((id->flags & KCDB_IDENT_FLAG_ACTIVE) == \r
-             KCDB_IDENT_FLAG_ACTIVE) &&\r
-            ((id->flags & and_flags) == eq_flags)) {\r
-            n_idents ++;\r
-            hr = StringCbLength(id->name, KCDB_IDENT_MAXCB_NAME, &cb_curr);\r
-#ifdef DEBUG\r
-            assert(SUCCEEDED(hr));\r
-#endif\r
-            cb_req += cb_curr + sizeof(wchar_t);\r
-        } \r
-    }\r
-\r
-    cb_req += sizeof(wchar_t);\r
-\r
-    if (pn_idents != NULL)\r
-        *pn_idents = n_idents;\r
-\r
-    if (pcb_buf != NULL && (name_buf == NULL || *pcb_buf < cb_req)) {\r
-        *pcb_buf = cb_req;\r
-\r
-        rv = KHM_ERROR_TOO_LONG;\r
-    } else if(name_buf != NULL) {\r
-        cch_left = (*pcb_buf) / sizeof(wchar_t);\r
-\r
-        for (id = kcdb_identities;\r
-             id != NULL;\r
-             id = LNEXT(id)) {\r
-            if (((id->flags & KCDB_IDENT_FLAG_ACTIVE) == \r
-                 KCDB_IDENT_FLAG_ACTIVE) &&\r
-                ((id->flags & and_flags) == eq_flags)) {\r
-                StringCchLength(id->name, KCDB_IDENT_MAXCCH_NAME, \r
-                                &cch_curr);\r
-                cch_curr++;\r
-                StringCchCopy(name_buf, cch_left, id->name);\r
-                cch_left -= cch_curr;\r
-                name_buf += cch_curr; \r
-            }\r
-        }\r
-\r
-        *name_buf = L'\0';\r
-        *pcb_buf = cb_req;\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_ident);\r
-\r
-    return rv;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+
+static 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;
+khm_boolean kcdb_checking_config = FALSE;
+
+KHMEXP khm_boolean KHMAPI
+kcdb_identity_is_equal(khm_handle identity1,
+                       khm_handle identity2)
+{
+
+    return (identity1 == identity2);
+
+}
+
+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_send_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_send_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_PARAM;
+
+    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);
+}
+
+/* NOT called with cs_ident held */
+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 = NULL;
+    kcdb_identity * id_tmp = NULL;
+    size_t namesize;
+
+    if(!result || !name)
+        return KHM_ERROR_INVALID_PARAM;
+
+    *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 |
+                 KCDB_IDENT_FLAG_STICKY))) {
+        /* can't specify this flag in create */
+        return KHM_ERROR_INVALID_PARAM;
+    }
+
+    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 = PMALLOC(sizeof(kcdb_identity));
+    ZeroMemory(id, sizeof(kcdb_identity));
+    id->magic = KCDB_IDENT_MAGIC;
+    id->name = PMALLOC(namesize);
+    StringCbCopy(id->name, namesize, name);
+
+    id->flags = (flags & KCDB_IDENT_FLAGMASK_RDWR);
+    id->flags |= KCDB_IDENT_FLAG_ACTIVE | KCDB_IDENT_FLAG_EMPTY;
+    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;
+
+        PFREE(id->name);
+        PFREE(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_config() 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_PARAM;
+        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)
+            PFREE(id->name);
+        PFREE(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. */
+
+ _exit:
+    LeaveCriticalSection(&cs_ident);
+
+    return code;
+}
+
+KHMEXP khm_int32 KHMAPI 
+kcdb_identity_set_flags(khm_handle vid, 
+                        khm_int32 flag,
+                        khm_int32 mask) {
+    kcdb_identity * id;
+    khm_int32 oldflags;
+    khm_int32 newflags;
+    khm_int32 delta = 0;
+    khm_int32 rv;
+
+    if (mask == 0)
+        return KHM_ERROR_SUCCESS;
+
+    if(!kcdb_is_active_identity(vid))
+        return KHM_ERROR_INVALID_PARAM;
+
+    id = (kcdb_identity *) vid;
+
+    flag &= mask;
+
+    if((mask & ~KCDB_IDENT_FLAGMASK_RDWR) ||
+       ((flag & KCDB_IDENT_FLAG_INVALID) && (flag & KCDB_IDENT_FLAG_VALID)))
+        return KHM_ERROR_INVALID_PARAM;
+
+    if((mask & KCDB_IDENT_FLAG_DEFAULT) &&
+       (flag & KCDB_IDENT_FLAG_DEFAULT)) {
+        /* kcdb_identity_set_default already does checking for
+           redundant transitions */
+        rv = kcdb_identity_set_default(vid);
+
+        if(KHM_FAILED(rv))
+            return rv;
+
+        mask &= ~KCDB_IDENT_FLAG_DEFAULT;
+        flag &= ~KCDB_IDENT_FLAG_DEFAULT;
+    }
+
+    EnterCriticalSection(&cs_ident);
+
+    if(mask & KCDB_IDENT_FLAG_SEARCHABLE) {
+        if(!(flag & KCDB_IDENT_FLAG_SEARCHABLE)) {
+            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;
+                }
+            }
+        } else {
+            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;
+                }
+            }
+        }
+
+        flag &= ~KCDB_IDENT_FLAG_SEARCHABLE;
+        mask &= ~KCDB_IDENT_FLAG_SEARCHABLE;
+    }
+
+    if (mask & KCDB_IDENT_FLAG_STICKY) {
+        if ((flag ^ id->flags) & KCDB_IDENT_FLAG_STICKY) {
+            khm_handle h_conf;
+
+            if (KHM_SUCCEEDED(kcdb_identity_get_config(vid, 
+                                                       KHM_FLAG_CREATE, 
+                                                       &h_conf))) {
+                khc_write_int32(h_conf, L"Sticky",
+                                !!(flag & KCDB_IDENT_FLAG_STICKY));
+                khc_close_space(h_conf);
+            }
+
+            id->flags = 
+                ((id->flags & ~KCDB_IDENT_FLAG_STICKY) |
+                 (flag & KCDB_IDENT_FLAG_STICKY));
+
+            delta |= KCDB_IDENT_FLAG_STICKY;
+        }
+
+        flag &= ~KCDB_IDENT_FLAG_STICKY;
+        mask &= ~KCDB_IDENT_FLAG_STICKY;
+    }
+
+    /* deal with every other flag */
+
+    oldflags = id->flags;
+
+    id->flags = (id->flags & ~mask) | (flag & mask);
+
+    if (flag & KCDB_IDENT_FLAG_VALID) {
+        id->flags &= ~(KCDB_IDENT_FLAG_INVALID | KCDB_IDENT_FLAG_UNKNOWN);
+    }
+    if (flag & KCDB_IDENT_FLAG_INVALID) {
+        id->flags &= ~(KCDB_IDENT_FLAG_VALID | KCDB_IDENT_FLAG_UNKNOWN);
+    }
+
+    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_PARAM;
+
+    id = (kcdb_identity *) vid;
+
+    EnterCriticalSection(&cs_ident);
+    *flags = id->flags;
+    LeaveCriticalSection(&cs_ident);
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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) ||
+        (new_def == kcdb_def_identity))
+        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_PARAM;
+    }
+
+    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)) {
+            khm_int32 oldflags;
+            EnterCriticalSection(&cs_ident);
+            oldflags = id->flags;
+            id->flags &= ~KCDB_IDENT_FLAG_CONFIG;
+            LeaveCriticalSection(&cs_ident);
+            if (oldflags & KCDB_IDENT_FLAG_CONFIG)
+                kcdbint_ident_post_message(KCDB_OP_DELCONFIG, id);
+            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;
+
+    EnterCriticalSection(&cs_ident);
+    if(kcdb_is_active_identity(vid)) {
+        id = vid;
+        id->refcount++;
+    } else {
+        LeaveCriticalSection(&cs_ident);
+        return KHM_ERROR_INVALID_PARAM;
+    }
+    LeaveCriticalSection(&cs_ident);
+    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;
+
+    EnterCriticalSection(&cs_ident);
+    if(kcdb_is_identity(vid)) {
+        id = vid;
+        refcount = --id->refcount;
+        if(refcount == 0) {
+            /* We only delete identities which do not have a
+               configuration. */
+            if (id->refcount == 0 &&
+                !(id->flags & KCDB_IDENT_FLAG_CONFIG))
+                kcdb_identity_delete(vid);
+        }
+    } else {
+        LeaveCriticalSection(&cs_ident);
+        return KHM_ERROR_INVALID_PARAM;
+    }
+    LeaveCriticalSection(&cs_ident);
+    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;
+
+    EnterCriticalSection(&cs_ident);
+
+    if (!kcdb_is_active_identity(vid)) {
+        code = KHM_ERROR_INVALID_PARAM;
+        goto _exit;
+    }
+
+    ident = (kcdb_identity *) vid;
+
+    result.ident = ident;
+    result.flags = 0;
+    result.count = 0;
+
+    LeaveCriticalSection(&cs_ident);
+
+    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_IDENT_FLAGMASK_RDWR &
+                            ~(KCDB_IDENT_FLAG_DEFAULT |
+                              KCDB_IDENT_FLAG_SEARCHABLE |
+                              KCDB_IDENT_FLAG_STICKY));
+
+    EnterCriticalSection(&cs_ident);
+    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;
+    kcdb_identity * next;
+    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 = next) {
+
+            if (!kcdb_is_active_identity(ident) ||
+                ident->refresh_cycle == kcdb_ident_refresh_cycle) {
+                next = LNEXT(ident);
+                continue;
+            }
+
+            kcdb_identity_hold((khm_handle) ident);
+
+            LeaveCriticalSection(&cs_ident);
+
+            kcdb_identity_refresh((khm_handle) ident);
+
+            EnterCriticalSection(&cs_ident);
+
+            next = LNEXT(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 = NULL;
+    kcdb_attrib * attrib = NULL;
+    kcdb_type * type = NULL;
+    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_PARAM;
+    }
+
+    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_PARAM;
+    }
+
+#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_PARAM;
+    }
+
+    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_PARAM;
+        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,
+                         const 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_PARAM;
+
+    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_PARAM;
+    }
+
+    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_PARAM;
+        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);
+#endif
+        code = KHM_ERROR_INVALID_OPERATION;
+    } 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,
+                         const 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_PARAM;
+    }
+
+    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_PARAM;
+        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);
+#endif
+        code = KHM_ERROR_INVALID_OPERATION;
+    } 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,
+                                const 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 */
+
+/* NOT called with cs_ident held */
+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;
+
+    /* We can't really make an assumption about the valid characters
+       in an identity.  So we let the identity provider decide */
+#ifdef VALIDATE_IDENTIY_CHARACTERS
+    if(wcsspn(name, KCDB_IDENT_VALID_CHARS) != cch)
+        return KHM_ERROR_INVALID_NAME;
+#endif
+    
+    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_PARAM;
+
+    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)))
+        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;
+
+    /* 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);
+
+    EnterCriticalSection(&cs_ident);
+    if(kcdb_ident_sub != NULL) {
+        sub = kcdb_ident_sub;
+    } else {
+        sub = NULL;
+    }
+    LeaveCriticalSection(&cs_ident);
+
+    if(sub != NULL) {
+        ZeroMemory(&namex, sizeof(namex));
+        namex.name_src = name1;
+        namex.name_alt = name2;
+        namex.result = rv;
+
+        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_PARAM;
+
+    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_PARAM;
+
+       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_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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;
+
+        kcdb_checked_config = TRUE;
+        kcdb_checking_config = TRUE;
+
+        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;
+
+            LeaveCriticalSection(&cs_ident);
+
+            if (KHM_SUCCEEDED(kcdb_identity_create(wname,
+                                                   KCDB_IDENT_FLAG_CREATE,
+                                                   &t_id)))
+                kcdb_identity_release(t_id);
+
+            EnterCriticalSection(&cs_ident);
+        }
+
+    _config_check_cleanup:
+        if (h_kcdb)
+            khc_close_space(h_kcdb);
+        if (h_idents)
+            khc_close_space(h_idents);
+
+        kcdb_checking_config = FALSE;
+    }
+
+    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;
+}
index be0205d9637ea89b06a655f11cc7b89a40a4efad..ba55a201ba0bb0883696aefea57e2c2118be65ca 100644 (file)
@@ -1,60 +1,60 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KCDB_IDENTITY_H\r
-#define __KHIMAIRA_KCDB_IDENTITY_H\r
-\r
-/* Identity */\r
-\r
-#define KCDB_IDENT_HASHTABLE_SIZE 31\r
-\r
-typedef struct kcdb_identity_t {\r
-    khm_int32 magic;\r
-    wchar_t * name;\r
-    khm_int32 flags;\r
-    khm_int32 refcount;\r
-    kcdb_buf  buf;\r
-    khm_ui_4  refresh_cycle;\r
-    LDCL(struct kcdb_identity_t);\r
-} kcdb_identity;\r
-\r
-#define KCDB_IDENT_MAGIC 0x31938d4f\r
-\r
-extern hashtable * kcdb_identities_namemap;\r
-extern khm_int32 kcdb_n_identities;\r
-extern kcdb_identity * kcdb_identities; /* all identities */\r
-extern kcdb_identity * kcdb_def_identity; /* default identity */\r
-extern khm_ui_4 kcdb_ident_refresh_cycle;\r
-\r
-void kcdbint_ident_init(void);\r
-void kcdbint_ident_exit(void);\r
-void kcdbint_ident_msg_completion(kmq_message * m);\r
-void kcdbint_ident_post_message(khm_int32 op, kcdb_identity * id);\r
-\r
-#define kcdb_is_identity(id) ((id) && ((kcdb_identity *)(id))->magic == KCDB_IDENT_MAGIC)\r
-#define kcdb_is_active_identity(id) (kcdb_is_identity(id) && (((kcdb_identity *)(id))->flags & KCDB_IDENT_FLAG_ACTIVE))\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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;
+    LDCL(struct kcdb_identity_t);
+} kcdb_identity;
+
+#define KCDB_IDENT_MAGIC 0x31938d4f
+
+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
index 13ef4da5524333792e8a6642c15ead3b38d1f8f3..cea97a8c76c91cb797678a5fb1c30f730d49d806 100644 (file)
@@ -1,91 +1,91 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kcreddbinternal.h>\r
-\r
-/* set to TRUE when the configuration is loaded */\r
-static int kcdb_config_loaded = 0;\r
-\r
-/* global state cs */\r
-static CRITICAL_SECTION cs_kcdb_global;\r
-\r
-/* forward dcl */\r
-void KHMAPI kcdb_msg_completion(kmq_message * m);\r
-\r
-void kcdb_init(void) {\r
-    /* setup the critical sections */\r
-    InitializeCriticalSection(&cs_kcdb_global);\r
-\r
-    kmq_set_completion_handler(KMSG_KCDB, kcdb_msg_completion);\r
-\r
-    kcdb_credtype_init();\r
-    kcdbint_ident_init();\r
-    kcdb_credset_init();\r
-    kcdb_cred_init();\r
-    kcdb_type_init();\r
-    kcdb_attrib_init();\r
-}\r
-\r
-void kcdb_exit(void) {\r
-\r
-    kcdb_attrib_exit();\r
-    kcdb_type_exit();\r
-    kcdb_cred_exit();\r
-    kcdb_credset_exit();\r
-    kcdbint_ident_exit();\r
-    kcdb_credtype_exit();\r
-\r
-    kmq_set_completion_handler(KMSG_KCDB, NULL);\r
-\r
-    DeleteCriticalSection(&cs_kcdb_global);\r
-}\r
-\r
-khm_handle kcdb_get_config(void) {\r
-    khm_handle space = NULL;\r
-\r
-    EnterCriticalSection(&cs_kcdb_global);\r
-    if(!kcdb_config_loaded) {\r
-        khc_load_schema(NULL, schema_kcdbconfig);\r
-        kcdb_config_loaded = 1;\r
-    }\r
-    khc_open_space(NULL, L"KCDB", 0, &space);\r
-    LeaveCriticalSection(&cs_kcdb_global);\r
-\r
-    return space;\r
-}\r
-\r
-void KHMAPI kcdb_msg_completion(kmq_message * m) {\r
-    if(!m)\r
-        return;\r
-    if(m->subtype == KMSG_KCDB_IDENT)\r
-        kcdbint_ident_msg_completion(m);\r
-    else if(m->subtype == KMSG_KCDB_ATTRIB)\r
-        kcdb_attrib_msg_completion(m);\r
-    else if(m->subtype == KMSG_KCDB_TYPE)\r
-        kcdb_type_msg_completion(m);\r
-    else if(m->subtype == KMSG_KCDB_CREDTYPE)\r
-        kcdb_credtype_msg_completion(m);\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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);
+}
index 4b15a245f61eb9a727b3ac796db33992114abcc6..9d54105c45cb04540fdc71026f031d4d5d35c6d6 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KCREDDB_H__\r
-#define __KHIMAIRA_KCREDDB_H__\r
-\r
-#include<khdefs.h>\r
-#include<time.h>\r
-\r
-\r
-/*! \defgroup kcdb NetIDMgr Credentials Database */\r
-/*@{*/\r
-\r
-/*! \brief Maximum length in characters of short description \r
-\r
-    The length includes the terminating \a NULL character.\r
-    */\r
-#define KCDB_MAXCCH_SHORT_DESC  256\r
-\r
-/*! \brief Maximum length in bytes of short description \r
-\r
-    The length includes the terminating \a NULL character.\r
-    */\r
-#define KCDB_MAXCB_SHORT_DESC   (sizeof(wchar_t) * KCDB_MAXCCH_SHORT_DESC)\r
-\r
-/*! \brief Maximum length in characters of long description \r
-\r
-    The length includes the terminating \a NULL character.\r
-    */\r
-#define KCDB_MAXCCH_LONG_DESC   8192\r
-\r
-/*! \brief Maximum length in characters of long description \r
-\r
-    The length includes the terminating \a NULL character.\r
-    */\r
-#define KCDB_MAXCB_LONG_DESC    (sizeof(wchar_t) * KCDB_MAXCCH_LONG_DESC)\r
-\r
-/*! \brief Maximum length in characters of name \r
-\r
-    The length includes the terminating \a NULL character.\r
-    */\r
-#define KCDB_MAXCCH_NAME        256\r
-\r
-/*! \brief Maximum length in bytes of short description \r
-\r
-    The length includes the terminating \a NULL character.\r
-    */\r
-#define KCDB_MAXCB_NAME         (sizeof(wchar_t) * KCDB_MAXCCH_NAME)\r
-\r
-/*! \brief Automatically determine the number of bytes required\r
-\r
-    Can be used in most places where a count of bytes is required.\r
-    For many objects, the number of bytes that are required can be\r
-    determined through context and may be ommited.  In such cases you\r
-    can use the \a KCDB_CBSIZE_AUTO value to specify that the function\r
-    is to determine the size automatically.\r
-\r
-    \note Not all functions that take a count of bytes support the \a\r
-        KCDB_CBSIZE_AUTO value.\r
-*/\r
-#define KCDB_CBSIZE_AUTO (-1)\r
-\r
-/*!\r
-\defgroup kcdb_ident Identities\r
-\r
-Functions, macros etc. for manipulating identities.\r
-*/\r
-\r
-/*@{*/\r
-\r
-/*! \brief The maximum number of characters (including terminator) that can\r
-           be specified as an identity name */\r
-#define KCDB_IDENT_MAXCCH_NAME 256\r
-\r
-/*! \brief The maximum number of bytes that can be specified as an identity\r
-           name */\r
-#define KCDB_IDENT_MAXCB_NAME (sizeof(wchar_t) * KCDB_IDENT_MAXCCH_NAME)\r
-\r
-/*! \brief Valid characters in an identity name */\r
-#define KCDB_IDENT_VALID_CHARS L"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._@-/"\r
-\r
-/*!\r
-\name Flags for identities */\r
-/*@{*/\r
-\r
-/*! \brief Create the identity if it doesn't already exist. \r
-    \note  Only to be used with kcdb_identity_create() */\r
-#define KCDB_IDENT_FLAG_CREATE      0x10000000L\r
-\r
-/*! \brief Has configuration information\r
-\r
-    Indicates that the identity has persistent configuration\r
-    information associated with it.\r
- */\r
-#define KCDB_IDENT_FLAG_CONFIG      0x00800000L\r
-\r
-/*! \brief Marks the identity as active.\r
-\r
-    An active identity is one that is in active use within NetIDMgr.\r
-\r
-    \note This flag is readonly and cannot be specified when creating\r
-        or modifying an identity. Once an identity is deleted, it will\r
-        no longer have this flag. */\r
-#define KCDB_IDENT_FLAG_ACTIVE      0x02000000L\r
-\r
-\r
-/*! \brief The identity has custom attributes assigned\r
- */\r
-#define KCDB_IDENT_FLAG_ATTRIBS     0x08000000L\r
-\r
-/*! \brief This is the default identity. \r
-\r
-    At most one identity will have this flag set at any given time.\r
-    To set or reset the flag, use kcdb_identity_set_default() */\r
-#define KCDB_IDENT_FLAG_DEFAULT     0x00000001L\r
-\r
-/*! \brief This identity can be searched.\r
-\r
-    The meaning of this flag is left to be interpreted by individual\r
-    plugins. */\r
-#define KCDB_IDENT_FLAG_SEARCHABLE  0x00000002L\r
-\r
-/*! \brief Hidden identity.\r
-\r
-    The identity will not show up in the identity list window.  Once\r
-    the hidden is switched off, the identity (and all associated\r
-    credentials) will re-appear in the window */\r
-#define KCDB_IDENT_FLAG_HIDDEN      0x00000004L\r
-\r
-/*! \brief Invalid identity\r
-\r
-    For one reason or another, this identity is invalid.  This flag\r
-    can be set by an identity provider to indicate that this identity\r
-    does not correspond to an actual identity because an external\r
-    entity (such as a KDC) has denied it's existence.\r
-\r
-    The absence of this flag does not imply that the identity is\r
-    valid.  The ::KCDB_IDENT_FLAG_VALID bit must be set for that to be\r
-    the case.  If neither flag is set, then the status of the identity\r
-    is not known.\r
-*/\r
-#define KCDB_IDENT_FLAG_INVALID     0x00000008L\r
-\r
-/*! \brief Valid identity\r
-\r
-    The identity has been validated through an external entity, or\r
-    it's validity implied through the existence of credentials for the\r
-    identity.\r
-\r
-    The absence of this flag does not imply that the identity is\r
-    invalid.  The ::KCDB_IDENT_FLAG_INVALID bit must be set for that\r
-    to be the case.  If neither flag is set, then the status of the\r
-    identity is not known.\r
- */\r
-#define KCDB_IDENT_FLAG_VALID       0x00000010L\r
-\r
-/*! \brief Expired identity\r
-\r
-    This identity has expired and can not be actively used to obtain\r
-    credentials.  This determination is made based on the input of\r
-    some external entity.  This flag may only be set by an identity\r
-    provider.\r
-*/\r
-#define KCDB_IDENT_FLAG_EXPIRED     0x00000020L\r
-\r
-/*! \brief Empty identity\r
-\r
-    The identity does not have actual credentials associated with it.\r
- */\r
-#define KCDB_IDENT_FLAG_EMPTY       0x00000040L\r
-\r
-/*! \brief Renewable identity\r
-\r
-    The initial credentials associated with this identity are\r
-    renewable.  Thus making the whole identity renewable.\r
- */\r
-#define KCDB_IDENT_FLAG_RENEWABLE   0x00000080L\r
-\r
-/*! \brief Required user interaction\r
-\r
-    The identity is in a state which requires user interaction to\r
-    activate.  Currently, the identity may not be in a state where it\r
-    can be used to obtain credentials.\r
-\r
-    A typical example of this is when the primary password for an\r
-    identity has expired.\r
- */\r
-#define KCDB_IDENT_FLAG_INTERACT    0x00000100L\r
-\r
-/*! \brief Has expired credentials\r
-\r
-    The identity has expired credentials associated with it.\r
- */\r
-#define KCDB_IDENT_FLAG_CRED_EXP    0x00000200L\r
-\r
-/*! \brief Has renewable credentials\r
-\r
-    The identity has renewable credentials associated with it.  If the\r
-    initial credentials of the identity are renewable, then identity\r
-    is renewable.  Hence the ::KCDB_IDENT_FLAG_RENEWABLE should also\r
-    be set.\r
- */\r
-#define KCDB_IDENT_FLAG_CRED_RENEW  0x00000400L\r
-\r
-/*! \brief Sticky identity\r
-\r
-    Sticky identities are identities that are always visible in the\r
-    credentials display even if no credentials are associated with it.\r
- */\r
-#define KCDB_IDENT_FLAG_STICKY      0x00000800L\r
-\r
-/*! \brief Unknown state\r
-\r
-    The validity of the identity cannot be determined.  This usually\r
-    means that an authority could not be contacted.  This flag is to\r
-    be treated as transient.  If ::KCDB_IDENT_FLAG_INVALID or\r
-    ::KCDB_IDENT_FLAG_VALID is set for the identity, this flag is to\r
-    be ignored.\r
- */\r
-#define KCDB_IDENT_FLAG_UNKNOWN     0x00001000L\r
-\r
-/*! \brief Read/write flags mask.\r
-\r
-    A bitmask that correspond to all the read/write flags in the mask.\r
-*/\r
-#define KCDB_IDENT_FLAGMASK_RDWR    0x00001fffL\r
-\r
-/*@}*/\r
-\r
-/*! \name Identity Provider Data Structures\r
-@{*/\r
-\r
-/*! \brief Name transfer structure\r
-\r
-    Used when the KCDB is communicating with the identity provider to\r
-    exchange string names of identities.  See individual ::KMSG_IDENT\r
-    message subtypes for the usage of this structure.\r
- */\r
-typedef struct tag_kcdb_ident_name_xfer {\r
-    const wchar_t * name_src;   /*!< An identity name.  Does not\r
-                                     exceed KCDB_IDENT_MAXCCH_NAME\r
-                                     characters including terminating\r
-                                     NULL. */\r
-    const wchar_t * name_alt;   /*!< An identity name.  Does not\r
-                                     exceed KCDB_IDENT_MAXCCH_NAME\r
-                                     characters including terminating\r
-                                     NULL. */\r
-    wchar_t *       name_dest;  /*!< Pointer to a buffer that is to\r
-                                     receive a response string.  The\r
-                                     size of the buffer in bytes is\r
-                                     specified in \a cb_name_dest. */\r
-    khm_size        cb_name_dest; /*!< Size of buffer pointed to by \a\r
-                                     name_dest in bytes. */\r
-    khm_int32       result;     /*!< Receives a result value, which is\r
-                                     usually an error code defined in\r
-                                     kherror.h, though it is not\r
-                                     always. */\r
-} kcdb_ident_name_xfer;\r
-\r
-typedef struct tag_kcdb_ident_info {\r
-    khm_handle      identity;\r
-    khm_int32       fields;\r
-\r
-    FILETIME        expiration;\r
-} kcdb_ident_info;\r
-\r
-/*@}*/\r
-\r
-/*! \name Identity provider interface functions\r
-\r
-    These functions encapsulate safe calls to the current identity\r
-    provider.  While these functions are exported, applications should\r
-    not call these functions directly.  They are provided for use by\r
-    the NetIDMgr core application.\r
-@{*/\r
-\r
-/*! \brief Validate an identity name\r
-\r
-    The name that is provided will be passed through sets of\r
-    validations.  One set, which doesn't depend on the identity\r
-    provider checks whether the length of the identity name and\r
-    whether there are any invalid characters in the identity name.  If\r
-    the name passes those tests, then the name is passed down to the\r
-    identity provider's name validation handler.\r
-\r
-    \retval KHM_ERROR_SUCCESS The name is valid\r
-    \retval KHM_ERROR_TOO_LONG Too many characters in name\r
-    \retval KHM_ERROR_INVALID_NAME There were invalid characters in the name.\r
-    \retval KHM_ERROR_NO_PROVIDER There is no identity provider;\r
-        however the name passed the length and character tests.\r
-    \retval KHM_ERROR_NOT_IMPLEMENTED The identity provider doesn't\r
-        implement a name validation handler; however the name passed\r
-        the length and character tests.\r
-\r
-    \see ::KMSG_IDENT_VALIDATE_NAME\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identpro_validate_name(const wchar_t * name);\r
-\r
-/*! \brief Validate an identity\r
-\r
-    The identity itself needs to be validated.  This may involve\r
-    communicating with an external entity.\r
-\r
-    \see ::KMSG_IDENT_VALIDATE_IDENTITY\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identpro_validate_identity(khm_handle identity);\r
-\r
-/*! \brief Canonicalize the name \r
-\r
-\r
-    \see ::KMSG_IDENT_CANON_NAME\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identpro_canon_name(const wchar_t * name_in, \r
-                         wchar_t * name_out, \r
-                         khm_size * cb_name_out);\r
-\r
-/*! \brief Compare two identity names \r
-\r
-    \see ::KMSG_IDENT_COMPARE_NAME\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identpro_compare_name(const wchar_t * name1,\r
-                           const wchar_t * name2);\r
-\r
-/*! \brief Set the specified identity as the default \r
-\r
-    \see ::KMSG_IDENT_SET_DEFAULT\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identpro_set_default(khm_handle identity);\r
-\r
-/*! \brief Set the specified identity as searchable \r
-\r
-    \see ::KMSG_IDENT_SET_SEARCHABLE\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identpro_set_searchable(khm_handle identity,\r
-                             khm_boolean searchable);\r
-\r
-/*! \brief Update the specified identity \r
-\r
-    \see ::KMSG_IDENT_UPDATE\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identpro_update(khm_handle identity);\r
-\r
-/*! \brief Obtain the UI callback\r
-\r
-    \a rock is actually a pointer to a ::khui_ident_new_creds_cb which\r
-    is to receive the callback.\r
-\r
-    \see ::KMSG_IDENT_GET_UI_CALLBACK\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identpro_get_ui_cb(void * rock);\r
-\r
-/*! \brief Notify an identity provider of the creation of a new identity \r
-\r
-    \see ::KMSG_IDENT_NOTIFY_CREATE\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identpro_notify_create(khm_handle identity);\r
-\r
-/*@}*/\r
-\r
-/*! \brief Check if the given name is a valid identity name\r
-\r
-    \return TRUE or FALSE to the question, is this valid?\r
-*/\r
-KHMEXP khm_boolean KHMAPI \r
-kcdb_identity_is_valid_name(const wchar_t * name);\r
-\r
-/*! \brief Create or open an identity.\r
-\r
-    If the KCDB_IDENT_FLAG_CREATE flag is specified in the flags\r
-    parameter a new identity will be created if one does not already\r
-    exist with the given name.  If an identity by that name already\r
-    exists, then the existing identity will be opened. The result\r
-    parameter will receive a held reference to the opened identity.\r
-    Use kcdb_identity_release() to release the handle.\r
-\r
-    \param[in] name Name of identity to create\r
-    \param[in] flags If KCDB_IDENT_FLAG_CREATE is specified, then the\r
-        identity will be created if it doesn't already exist.\r
-        Additional flags can be set here which will be assigned to the\r
-        identity if it is created.  Additional flags have no effect if\r
-        an existing identity is opened.\r
-    \param[out] result If the call is successful, this receives a held\r
-        reference to the identity.  The caller should call\r
-        kcdb_identity_release() to release the identity once it is no\r
-        longer needed.\r
-    */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_create(const wchar_t *name, \r
-                     khm_int32 flags, \r
-                     khm_handle * result);\r
-\r
-/*! \brief Mark an identity for deletion.\r
-\r
-    The identity will be marked for deletion.  The\r
-    KCDB_IDENT_FLAG_ACTIVE will no longer be present for this\r
-    identity.  Once all references to the identity are released, it\r
-    will be removed from memory.  All associated credentials will also\r
-    be removed. */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_delete(khm_handle id);\r
-\r
-/*! \brief Set or unset the specified flags in the specified identity.\r
-\r
-    Only flags that are in KCDB_IDENT_FLAGMASK_RDWR can be specifed in\r
-    the \a flags parameter or the \a mask parameter.  The flags set in\r
-    the \a mask parameter of the identity will be set to the\r
-    corresponding values in the \a flags parameter.\r
-\r
-    If ::KCDB_IDENT_FLAG_INVALID is set using this function, then the\r
-    ::KCDB_IDENT_FLAG_VALID will be automatically reset, and vice\r
-    versa.  Resetting either bit does not undo this change, and will\r
-    leave the identity's validity unspecified.  Setting either of\r
-    ::KCDB_IDENT_FLAG_INVALID or ::KCDB_IDENT_FLAG_VALID will\r
-    automatically reset ::KCDB_IDENT_FLAG_UNKNOWN.\r
-\r
-    Note that setting or resetting certain flags have other semantic\r
-    side-effects:\r
-\r
-    - ::KCDB_IDENT_FLAG_DEFAULT : Setting this is equivalent to\r
-      calling kcdb_identity_set_default() with \a id.  Resetting this\r
-      is equivalent to calling kcdb_identity_set_default() with NULL.\r
-\r
-    - ::KCDB_IDENT_FLAG_SEARCHABLE : Setting this will result in the\r
-      identity provider getting notified of the change. If the\r
-      identity provider indicates that searchable flag should not be\r
-      set or reset on the identity, then kcdb_identity_set_flags()\r
-      will return an error.\r
-\r
-    \note kcdb_identity_set_flags() is not atomic.  Even if the\r
-    function returns a failure code, some flags in the identity may\r
-    have been set.  When calling kcdb_identity_set_flags() always\r
-    check the flags in the identity using kcdb_identity_get_flags() to\r
-    check which flags have been set and which have failed.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_set_flags(khm_handle id, \r
-                        khm_int32 flags,\r
-                        khm_int32 mask);\r
-\r
-/*! \brief Return all the flags for the identity\r
-\r
-    The returned flags may include internal flags.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_flags(khm_handle id, \r
-                        khm_int32 * flags);\r
-\r
-/*! \brief Return the name of the identity \r
-\r
-    \param[out] buffer Buffer to copy the identity name into.  The\r
-        maximum size of an identity name is \a KCDB_IDENT_MAXCB_NAME.\r
-        If \a buffer is \a NULL, then the required size of the buffer\r
-        is returned in \a pcbsize.\r
-\r
-    \param[in,out] pcbsize Size of buffer in bytes. */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_name(khm_handle id, \r
-                       wchar_t * buffer, \r
-                       khm_size * pcbsize);\r
-\r
-/*! \brief Set the specified identity as the default.\r
-\r
-    Specifying NULL effectively makes none of the identities the\r
-    default.\r
-\r
-    \see kcdb_identity_set_flags()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_set_default(khm_handle id);\r
-\r
-/*! \brief Mark the specified identity as the default.\r
-\r
-    This API is reserved for use by identity providers as a means of\r
-    specifying which identity is default.  The difference between\r
-    kcdb_identity_set_default() and kcdb_identity_set_default_int() is\r
-    in semantics.  \r
-\r
-    - kcdb_identity_set_default() is used to request the KCDB to\r
-      designate the specified identity as the default.  When\r
-      processing the request, the KCDB invokes the identity provider\r
-      to do the necessary work to make the identity the default.\r
-\r
-    - kcdb_identity_set_default_int() is used by the identity provider\r
-      to notify the KCDB that the specified identity is the default.\r
-      This does not result in the invocation of any other semantics to\r
-      make the identity the default other than releasing the previous\r
-      defualt identity and making the specified one the default.\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-kcdb_identity_set_default_int(khm_handle id);\r
-\r
-/*! \brief Get the default identity\r
-\r
-    Obtain a held handle to the default identity if there is one.  The\r
-    handle must be freed using kcdb_identity_release().\r
-\r
-    If there is no default identity, then the handle pointed to by \a\r
-    pvid is set to \a NULL and the function returns\r
-    KHM_ERROR_NOT_FOUND. */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_default(khm_handle * pvid);\r
-\r
-/*! \brief Get the configuration space for the identity. \r
-\r
-    If the configuration space for the identity does not exist and the\r
-    flags parameter does not specify ::KHM_FLAG_CREATE, then the\r
-    function will return a failure code as specified in\r
-    ::khc_open_space().  Depending on whether or not a configuration\r
-    space was found, the ::KCDB_IDENT_FLAG_CONFIG flag will be set or\r
-    reset for the identity.\r
-\r
-    \param[in] id Identity for which the configuraiton space is requested\r
-\r
-    \param[in] flags Flags used when calling khc_open_space().  If \a\r
-        flags specifies KHM_FLAG_CREATE, then the configuration space\r
-        is created.\r
-\r
-    \param[out] result The resulting handle.  If the call is\r
-        successful, this receives a handle to the configuration space.\r
-        Use khc_close_space() to close the handle.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_config(khm_handle id,\r
-                         khm_int32 flags,\r
-                         khm_handle * result);\r
-\r
-/*! \brief Hold a reference to an identity.\r
-\r
-    A reference to an identity (a handle) is only valid while it is\r
-    held.  \note Once the handle is released, it can not be\r
-    revalidated by calling kcdb_identity_hold().  Doing so would lead\r
-    to unpredictable consequences. */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_hold(khm_handle id);\r
-\r
-/*! \brief Release a reference to an identity.\r
-    \see kcdb_identity_hold() */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_release(khm_handle id);\r
-\r
-/*! \brief Set the identity provider subscription\r
-\r
-    If there was a previous subscription, that subscription will be\r
-    automatically deleted.\r
-\r
-    \param[in] sub New identity provider subscription\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_set_provider(khm_handle sub);\r
-\r
-/*! \brief Set the primary credentials type\r
-\r
-    The primary credentials type is designated by the identity\r
-    provider.  As such, this function should only be called by an\r
-    identity provider.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_set_type(khm_int32 cred_type);\r
-\r
-/*! \brief Retrieve the identity provider subscription\r
-\r
-    \param[out] sub Receives the current identity provider\r
-        subscription.  Set to NULL if only the existence of an\r
-        identity provider needs to be checked.\r
-\r
-    \retval KHM_ERROR_SUCCESS An identity provider exists.  If \a sub\r
-        was not NULL, the subscription has been copied there.\r
-\r
-    \retval KHM_ERROR_NOT_FOUND There is currently no registered\r
-        identity provider.  If \a sub was not NULL, the handle it\r
-        points to has been set to NULL.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_provider(khm_handle * sub);\r
-\r
-/*! \brief Retrieve the identity provider credentials type\r
-\r
-    This is the credentials type that the identity provider has\r
-    designated as the primary credentials type.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_type(khm_int32 * ptype);\r
-\r
-/*! \brief Returns TRUE if the two identities are equal\r
-\r
-    Also returns TRUE if both identities are NULL.\r
- */\r
-KHMEXP khm_boolean KHMAPI\r
-kcdb_identity_is_equal(khm_handle identity1,\r
-                       khm_handle identity2);\r
-\r
-/*! \brief Set an attribute in an identity by attribute id\r
-\r
-    \param[in] buffer A pointer to a buffer containing the data to\r
-        assign to the attribute.  Setting \a buffer to NULL has the\r
-        effect of removing any data that is already assigned to the\r
-        attribute.  If \a buffer is non-NULL, then \a cbbuf should\r
-        specify the number of bytes in \a buffer.\r
-\r
-    \param[in] cbbuf Number of bytes of data in \a buffer.  The\r
-        individual data type handlers may copy in less than this many\r
-        bytes in to the credential.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_set_attr(khm_handle identity,\r
-                       khm_int32 attr_id,\r
-                       void * buffer,\r
-                       khm_size cbbuf);\r
-\r
-/*! \brief Set an attribute in an identity by name\r
-\r
-    The attribute name has to be a KCDB registered attribute or\r
-    property.\r
-\r
-    \param[in] cbbuf Number of bytes of data in \a buffer.  The\r
-        individual data type handlers may copy in less than this many\r
-        bytes in to the credential.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_set_attrib(khm_handle identity,\r
-                         const wchar_t * attr_name,\r
-                         void * buffer,\r
-                         khm_size cbbuf);\r
-\r
-/*! \brief Get an attribute from an identity by attribute id.\r
-\r
-    \param[in] buffer The buffer that is to receive the attribute\r
-        value.  Set this to NULL if only the required buffer size is\r
-        to be returned.\r
-\r
-    \param[in,out] cbbuf The number of bytes available in \a buffer.\r
-        If \a buffer is not sufficient, returns KHM_ERROR_TOO_LONG and\r
-        sets this to the required buffer size.\r
-\r
-    \param[out] attr_type Receives the data type of the attribute.\r
-        Set this to NULL if the type is not required.\r
-\r
-    \note Set both \a buffer and \a cbbuf to NULL if only the\r
-        existence of the attribute is to be checked.  If the attribute\r
-        exists in this identity then the function will return\r
-        KHM_ERROR_SUCCESS, otherwise it returns KHM_ERROR_NOT_FOUND.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_attr(khm_handle identity,\r
-                       khm_int32 attr_id,\r
-                       khm_int32 * attr_type,\r
-                       void * buffer,\r
-                       khm_size * pcbbuf);\r
-\r
-/*! \brief Get an attribute from an identity by name.\r
-\r
-    \param[in] buffer The buffer that is to receive the attribute\r
-        value.  Set this to NULL if only the required buffer size is\r
-        to be returned.\r
-\r
-    \param[in,out] cbbuf The number of bytes available in \a buffer.\r
-        If \a buffer is not sufficient, returns KHM_ERROR_TOO_LONG and\r
-        sets this to the required buffer size.\r
-\r
-    \note Set both \a buffer and \a cbbuf to NULL if only the\r
-        existence of the attribute is to be checked.  If the attribute\r
-        exists in this identity then the function will return\r
-        KHM_ERROR_SUCCESS, otherwise it returns KHM_ERROR_NOT_FOUND.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_attrib(khm_handle identity,\r
-                         const wchar_t * attr_name,\r
-                         khm_int32 * attr_type,\r
-                         void * buffer,\r
-                         khm_size * pcbbuf);\r
-\r
-/*! \brief Get the string representation of an identity attribute.\r
-\r
-    A shortcut function which generates the string representation of\r
-    an identity attribute directly.\r
-\r
-    \param[in] identity A handle to an identity\r
-\r
-    \param[in] attr_id The attribute to retrieve\r
-\r
-    \param[out] buffer A pointer to a string buffer which receives the\r
-        string form of the attribute.  Set this to NULL if you only\r
-        want to determine the size of the required buffer.\r
-\r
-    \param[in,out] pcbbuf A pointer to a #khm_int32 that, on entry,\r
-        holds the size of the buffer pointed to by \a buffer, and on\r
-        exit, receives the actual number of bytes that were copied.\r
-\r
-    \param[in] flags Flags for the string conversion. Can be set to\r
-        one of KCDB_TS_LONG or KCDB_TS_SHORT.  The default is\r
-        KCDB_TS_LONG.\r
-\r
-    \retval KHM_ERROR_SUCCESS Success\r
-    \retval KHM_ERROR_NOT_FOUND The given attribute was either invalid\r
-        or was not defined for this identity\r
-    \retval KHM_ERROR_INVALID_PARAM One or more parameters were invalid\r
-    \retval KHM_ERROR_TOO_LONG Either \a buffer was NULL or the\r
-        supplied buffer was insufficient\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_attr_string(khm_handle identity,\r
-                              khm_int32 attr_id,\r
-                              wchar_t * buffer,\r
-                              khm_size * pcbbuf,\r
-                              khm_int32 flags);\r
-\r
-/*! \brief Get the string representation of an identity attribute by name.\r
-\r
-    A shortcut function which generates the string representation of\r
-    an identity attribute directly.\r
-\r
-    \param[in] identity A handle to an identity\r
-\r
-    \param[in] attrib The name of the attribute to retrieve\r
-\r
-    \param[out] buffer A pointer to a string buffer which receives the\r
-        string form of the attribute.  Set this to NULL if you only\r
-        want to determine the size of the required buffer.\r
-\r
-    \param[in,out] pcbbuf A pointer to a #khm_int32 that, on entry,\r
-        holds the size of the buffer pointed to by \a buffer, and on\r
-        exit, receives the actual number of bytes that were copied.\r
-\r
-    \param[in] flags Flags for the string conversion. Can be set to\r
-        one of KCDB_TS_LONG or KCDB_TS_SHORT.  The default is\r
-        KCDB_TS_LONG.\r
-\r
-    \see kcdb_identity_get_attr_string()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_get_attrib_string(khm_handle identity,\r
-                                const wchar_t * attr_name,\r
-                                wchar_t * buffer,\r
-                                khm_size * pcbbuf,\r
-                                khm_int32 flags);\r
-\r
-/*! \brief Enumerate identities\r
-\r
-    Enumerates all the active identities that match the criteria\r
-    specified using \a and_flags and \a eq_flags.  The condition is\r
-    applied to all active identities as follows:\r
-\r
-    \code\r
-    (identity->flags & and_flags) == (eq_flags & and_flags)\r
-    \endcode\r
-\r
-    Essentially, if a flag is set in \a and_flags, then that flag in\r
-    the identity should equal the setting in \a eq_flags.\r
-\r
-    \param[in] and_flags See above\r
-\r
-    \param[in] eq_flags See above\r
-\r
-    \param[out] name_buf Buffer to receive the list of identity names.\r
-        Can be NULL if only the required size of the buffer or the\r
-        number of matching identities is required.  The list is\r
-        returned as a multi string.\r
-\r
-    \param[in,out] pcb_buf Number of bytes in buffer pointed to by \a\r
-        name_buf on entry.  On exit, will receive the number of bytes\r
-        copied.  Can be NULL only if \a name_buf is also NULL.  If \a\r
-        name_buf is NULL or if \a pcb_buf indicates that the buffer is\r
-        insufficient, this will receive the number of bytes required\r
-        and the return value of the function will be\r
-        KHM_ERROR_TOO_LONG\r
-\r
-    \param[out] pn_idents Receives the number of identities that match\r
-        the given criteria.\r
-\r
-    \retval KHM_ERROR_SUCCESS If \a name_buf was valid, the buffer now\r
-        contains a multi string of identities that matched.  If \a\r
-        pn_idents was valid, it contains the number of identities\r
-        matched.\r
-\r
-    \retval KHM_ERROR_TOO_LONG No buffer was supplied or the supplied\r
-        buffer was insufficient.  If \a pn_idents was valid, it\r
-        contains the number of identities.\r
-\r
-    \retval KHM_ERROR_INVALID_PARAM None of the parameters \a name_buf,\r
-        \a pcb_buf and \a pn_idents were supplied, or \a pcb_buf was\r
-        NULL when \a name_buf was not.\r
-\r
-    \note Calling this function to obtain the required size of the\r
-        buffer and then calling it with a that sized buffer is not\r
-        guaranteed to work since the list of identities may change\r
-        between the two calls.\r
-  */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_enum(khm_int32 and_flags,\r
-                   khm_int32 eq_flags,\r
-                   wchar_t * name_buf,\r
-                   khm_size * pcb_buf,\r
-                   khm_size * pn_idents);\r
-\r
-/*! \brief Refresh identity attributes based on root credential set\r
-\r
-    Several flags in an identity are dependent on the credentials that\r
-    are associated with it in the root credential set.  In addition,\r
-    other flags in an identity depend on external factors that need to\r
-    be verfied once in a while.  This API goes through the root\r
-    credential set as well as consulting the identity provider to\r
-    update an identity.\r
-\r
-    \see kcdb_identity_refresh()\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_refresh(khm_handle vid);\r
-\r
-/*! \brief Refresh all identities\r
-\r
-    Equivalent to calling kcdb_identity_refresh() for all active\r
-    identities.\r
-\r
-    \see kcdb_identityt_refresh()\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_identity_refresh_all(void);\r
-\r
-/* KSMG_KCDB_IDENT notifications are structured as follows:\r
-   type=KMSG_KCDB\r
-   subtype=KMSG_KCDB_IDENT\r
-   uparam=one of KCDB_OP_*\r
-   blob=handle to identity in question */\r
-\r
-/*@}*/\r
-\r
-\r
-/*********************************************************************/\r
-\r
-\r
-/*!\r
-\defgroup kcdb_creds Credential sets and individual credentials\r
-\r
-@{\r
-*/\r
-\r
-\r
-/*! \brief Credentials process function\r
-\r
-    This function is called for each credential in a credential set\r
-    when supplied to kcdb_credset_apply().  It should return\r
-    KHM_ERROR_SUCCESS to continue the operation, or any other value to\r
-    terminate the processing.\r
-\r
-    \see kcdb_credset_apply()\r
-*/\r
-typedef khm_int32 \r
-(KHMAPI *kcdb_cred_apply_func)(khm_handle cred, \r
-                               void * rock);\r
-\r
-/*! \brief Credentials filter function.\r
-\r
-    Should return non-zero if the credential passed as \a cred is to\r
-    be "accepted".  The precise consequence of a non-zero return value\r
-    is determined by the individual function that this call back is\r
-    passed into.\r
-\r
-    This function should not call any other function which may modify\r
-    \a cred.\r
-\r
-    \see kcdb_credset_collect_filtered()\r
-    \see kcdb_credset_extract_filtered()\r
-*/\r
-typedef khm_int32 \r
-(KHMAPI *kcdb_cred_filter_func)(khm_handle cred, \r
-                                khm_int32 flags, \r
-                                void * rock);\r
-\r
-/*! \brief Credentials compare function.\r
-\r
-    Asserts a weak ordering on the credentials that are passed in as\r
-    \a cred1 and \a cred2.  It should return:\r
-\r
-    - a negative value if \a cred1 < \a cred2\r
-    - zero if \a cred1 == \a cred2\r
-    - a postive value if \a cred1 > \a cred2\r
-    \see kcdb_credset_sort()\r
-    \see ::kcdb_credtype\r
-*/\r
-typedef khm_int32 \r
-(KHMAPI *kcdb_cred_comp_func)(khm_handle cred1, \r
-                              khm_handle cred2, \r
-                              void * rock);\r
-\r
-/*! \defgroup kcdb_credset Credential sets */\r
-/*@{*/\r
-\r
-/*! \brief Create a credential set.\r
-\r
-    Credential sets are temporary containers for credentials.  These\r
-    can be used by plug-ins to store credentials while they are being\r
-    enumerated from an external source.  Once all the credentials have\r
-    been collected into the credential set, the plug-in may call\r
-    kcdb_credset_collect() to collect the credentials into the root\r
-    credential store.\r
-\r
-    The user interface will only display credentials that are in the\r
-    root credential store.  No notifications are generated for changes\r
-    to a non-root credential set.\r
-\r
-    Use kcdb_credset_delete() to delete the credential set once it is\r
-    created.\r
-\r
-    \see kcdb_credset_delete()\r
-    \see kcdb_credset_collect()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_create(khm_handle * result);\r
-\r
-/** \brief Delete a credential set\r
-\r
-    \see kcdb_credset_create()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_delete(khm_handle credset);\r
-\r
-/** \brief Collect credentials from a credential set to another credential set.\r
-\r
-    Collecting a subset of credentials from credential set \a cs_src\r
-    into credential set \a cs_dest involves the following steps:\r
-\r
-    - Select all credentials from \a cs_src that matches the \a\r
-      identity and \a type specified in the function call and add them\r
-      to the \a cs_dest credential set if they are not there already.\r
-      Note that if neither credential set is not the root credential\r
-      store, then the credentials will be added by reference, while if\r
-      it is the root credential store, the credentials will be\r
-      duplicated, and the copies will be added to \a cs_dest.\r
-\r
-    - If a selected credential in \a cs_src already exists in \a\r
-      cs_dest, then update the credential in \a cs_dest with the\r
-      credential fields in \a cs_src.  In other words, once a\r
-      credential is found to exist in both \a cs_src and \a cs_dest,\r
-      all the non-null fields from the credential in \a cs_src will be\r
-      copied to the credential in \a cs_dest.  Fields which are null\r
-      (undefined) in \a cs_src and are non-null in \a cs_dest will be\r
-      left unmodified in \a cs_dest.\r
-\r
-      One notable exception is the credentials' flags.  All flags in\r
-      \a cs_src which are not included in\r
-      ::KCDB_CRED_FLAGMASK_ADDITIVE will be copied to the\r
-      corresponding bits in the flags of \a cs_dest.  However, flags\r
-      that are included in ::KCDB_CRED_FLAGMASK_ADDITIVE will be added\r
-      to the corresponding bits in \a cs_dest.\r
-\r
-      (See notes below)\r
-\r
-    - Remove all credentials from \a cs_dest that match the \a\r
-      identity and \a type that do not appear in \a cs_src. (see notes\r
-      below)\r
-\r
-    For performance reasons, plugins should use kcdb_credset_collect()\r
-    to update the root credentials store instead of adding and\r
-    removing individual credentials from the root store.\r
-\r
-    Only credentials that are associated with active identities are\r
-    affected by kcdb_credset_collect().\r
-\r
-    \param[in] cs_dest A handle to the destination credential set.  If\r
-        this is \a NULL, then it is assumed to refer to the root\r
-        credential store.\r
-\r
-    \param[in] cs_src A handle to the source credential set.  If this\r
-        is NULL, then it is assumed to refer to the root credential\r
-        store.\r
-\r
-    \param[in] identity A handle to an identity.  Setting this to NULL\r
-        collects all identities in the credential set.\r
-\r
-    \param[in] type A credentials type.  Setting this to\r
-        KCDB_CREDTYPE_ALL collects all credential types in the set.\r
-\r
-    \param[out] delta A bit mask that indicates the modifications that\r
-        were made to \a cs_dest as a result of the collect operation.\r
-        This is a combination of KCDB_DELTA_* values.  This parameter\r
-        can be \a NULL if the value is not required.\r
-\r
-    \warning If \a identity and \a type is set to a wildcard, all\r
-        credentials in the root store that are not in this credentials\r
-        set will be deleted.\r
-\r
-    \note Two credentials \a A and \a B are considered equal if:\r
-        - They refer to the same identity\r
-        - Both have the same credential type\r
-        - Both have the same name\r
-\r
-    \note This is the only supported way of modifying the root\r
-        credential store.\r
-\r
-    \note \a cs_src and \a cs_dest can not refer to the same\r
-        credentials set.\r
-\r
-    \note The destination credential set cannot be sealed.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_collect(khm_handle cs_dest,\r
-                     khm_handle cs_src,\r
-                     khm_handle identity, \r
-                     khm_int32 type,\r
-                     khm_int32 * delta);\r
-\r
-/*! \brief Credentials were added\r
-    \see kcdb_credset_collect() */\r
-#define KCDB_DELTA_ADD      1\r
-\r
-/*! \brief Credentials were deleted \r
-    \see kcdb_credset_collect() */\r
-#define KCDB_DELTA_DEL      2\r
-\r
-/*! \brief Credentials were modified\r
-    \see kcdb_credset_collect() */\r
-#define KCDB_DELTA_MODIFY   4\r
-\r
-/*! \brief Indicates that the credential to be filtered is from the root store.\r
-\r
-    \see kcdb_credset_collect_filtered()\r
-*/\r
-#define KCDB_CREDCOLL_FILTER_ROOT   1\r
-\r
-/*! \brief Indicates that the credential to be filtered is from the source\r
-        credential set \r
-        \r
-    \see kcdb_credset_collect_filtered() */\r
-#define KCDB_CREDCOLL_FILTER_SRC    2\r
-\r
-/*! \brief Indicates that the credential to be filtered is from the destination\r
-        credential set \r
-        \r
-    \see kcdb_credset_collect_filtered() */\r
-#define KCDB_CREDCOLL_FILTER_DEST   4\r
-\r
-/*! \brief Collect credentials from one credential set to another using a filter.\r
-\r
-    Similar to kcdb_credset_collect() except instead of selecting\r
-    credentials by matching against an identity and/or type, a filter\r
-    function is called.  If the filter function returns non-zero for a\r
-    credential, that credential is selected.\r
-\r
-    Credentials in the source and destination credential sets are\r
-    passed into the filter function.  Depending on whether the\r
-    credential is in the source credential set or destination\r
-    credential set, the \a flag parameter may have either \a\r
-    KCDB_CREDCOLL_FILTER_SRC or \a KCDB_CREDCOLL_FILTER_DEST bits set.\r
-    Also, if either one of the credential sets is the root credential\r
-    store, then additionally \a KCDB_CREDCOLL_FILTER_ROOT would also\r
-    be set.\r
-\r
-    See the kcdb_credset_collect() documentation for explanations of\r
-    the \a cs_src, \a cs_dest and \a delta parameters which perform\r
-    identical functions.\r
-\r
-    \param[in] filter The filter of type ::kcdb_cred_filter_func\r
-    \param[in] rock A custom argument to be passed to the filter function.\r
-\r
-    \see kcdb_credset_collect()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_collect_filtered(khm_handle cs_dest,\r
-                              khm_handle cs_src,\r
-                              kcdb_cred_filter_func filter,\r
-                              void * rock,\r
-                              khm_int32 * delta);\r
-\r
-/*! \brief Flush all credentials from a credential set\r
-\r
-    Deletes all the crednetials from the credential set.\r
-\r
-    \param[in] credset A handle to a credential set.  Cannot be NULL.\r
-\r
-    \note The credential set cannot be sealed\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_flush(khm_handle credset);\r
-\r
-/*! \brief Extract credentials from one credential set to another\r
-\r
-    Credentials from the source credential set are selected based on\r
-    the \a identity and \a type arguements.  If a credential is\r
-    matched, then it is added to the \a destcredset.\r
-\r
-    If the \a sourcecredset is the root credential set, the added\r
-    credentials are copies of the actual credentials in the root\r
-    credential set.  Otherwise the credentials are references to the\r
-    original credentials in the \a sourcecredset .\r
-\r
-    \param[in] destcredset Destination credential set.  Must be valid.\r
-\r
-    \param[in] sourcecredset The source credential set.  If set to\r
-        NULL, extracts from the root credential set.\r
-\r
-    \param[in] identity The identity to match in the source credential\r
-        set.  If set to NULL, matches all identities.\r
-\r
-    \param[in] type The credential type to match in the source credential set.\r
-        If set to KCDB_CREDTYPE_INVALID, matches all types.\r
-\r
-    \note This function does not check for duplicate credentials.\r
-\r
-    \note The destination credential set cannot be sealed.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_extract(khm_handle destcredset, \r
-                     khm_handle sourcecredset, \r
-                     khm_handle identity, \r
-                     khm_int32 type);\r
-\r
-/*! \brief Extract credentials from one credential set to another using a filter.\r
-\r
-    Similar to kcdb_credset_extract() except a filter function is used\r
-    to determine which credentials should be selected.\r
-\r
-    \param[in] rock A custom argument to be passed in to the filter function.\r
-\r
-    \note The destination credential set cannot be sealed.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_extract_filtered(khm_handle destcredset,\r
-                              khm_handle sourcecredset,\r
-                              kcdb_cred_filter_func filter,\r
-                              void * rock);\r
-\r
-/*! \brief Retrieve a held reference to a credential in a credential set based on index.\r
-\r
-    \param[in] idx The index of the credential to retrieve.  This is a\r
-        zero based index which goes from 0 ... (size of credset - 1).\r
-\r
-    \param[out] cred The held reference to a credential.  Call \r
-        kcdb_cred_release() to release the credential.\r
-\r
-    \retval KHM_ERROR_SUCCESS Success. \a cred has a held reference to the credential.\r
-    \retval KHM_ERROR_OUT_OF_BOUNDS The index specified in \a idx is out of bounds.\r
-    \retval KHM_ERROR_DELETED The credential at index \a idx has been marked as deleted.\r
-\r
-    \see kcdb_cred_release()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_get_cred(khm_handle credset,\r
-                      khm_int32 idx,\r
-                      khm_handle * cred);\r
-\r
-/*! \brief Search a credential set for a specific credential\r
-\r
-    The credential set indicated by \a credset is searched for a\r
-    credential that satisfies the predicate function \a f.  Each\r
-    credential starting at \a idx_start is passed into the predicate\r
-    function until it returns a non-zero value.  At this point, that\r
-    credential is passed in to the \a cred parameter, and the index of\r
-    the credential is passed into the \a idx parameter.\r
-\r
-    \param[in] credset The credential set to search on.  Specify NULL\r
-        if you want to search teh root credential set.\r
-\r
-    \param[in] idx_start The index at which to start the search after.\r
-        The first credential passed to the predicate function will be\r
-        at \a idx_start + 1.  Specify -1 to start from the beginning\r
-        of the credential set.\r
-\r
-    \param[in] f The predicate function.  The \a flags parameter of\r
-        the predicate function will always receive 0.\r
-\r
-    \param[in] rock An opaque parameter to be passed to the predicate\r
-        function \a f.\r
-\r
-    \param[out] cred A held reference to the credential that satisfied\r
-        the predicate function or NULL if no such credential was\r
-        found.  Note that if a valid credential is returned, the\r
-        calling function must release the credential using\r
-        kcdb_cred_release().\r
-\r
-    \param[out] idx The index of the credential passed in \a cred.\r
-        Specify NULL if the index is not required.\r
-\r
-    \retval KHM_ERROR_SUCCESS A credential that satisfied the\r
-        predicate function was found and was assigned to \a cred.\r
-\r
-    \retval KHM_ERROR_NOT_FOUND No credential was found that matched\r
-        the predicate function.\r
-\r
-    \note When querying credential sets that are shared between\r
-        threads, it is possible that another thread modifies the\r
-        credential set between successive calls to\r
-        kcdb_credset_find_filtered().  Therefore a continued sequences of\r
-        searches are not guaranteed to exhastively cover the\r
-        credential set nor to not return duplicate matches.  Duplicate\r
-        matches are possible if the order of the credentials in the\r
-        set was changed.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_find_filtered(khm_handle credset,\r
-                           khm_int32 idx_start,\r
-                           kcdb_cred_filter_func f,\r
-                           void * rock,\r
-                           khm_handle * cred,\r
-                           khm_int32 * idx);\r
-\r
-/*! \brief Find matching credential\r
-\r
-    Searches a credential set for a credential that matches the\r
-    specified credential.  For a credential to be a match, it must\r
-    have the same identity, credential type and name.\r
-\r
-    \param[in] credset Credential set to search \r
-\r
-    \param[in] cred_src Credetial to search on\r
-\r
-    \param[out] cred_dest receieves the matching credential if the\r
-        search is successful.  If a handle is returend, the\r
-        kcdb_cred_release() must be used to release the handle.  If\r
-        the matching credential is not required, you can pass in NULL.\r
-\r
-    \retval KHM_ERROR_SUCCESS The search was successful.  A credential\r
-        was assigned to \a cred_dest\r
-\r
-    \retval KHM_ERROR_NOT_FOUND A matching credential was not found.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_find_cred(khm_handle credset,\r
-                       khm_handle cred_src,\r
-                       khm_handle *cred_dest);\r
-                                               \r
-\r
-/*! \brief Delete a credential from a credential set.\r
-\r
-    The credential at index \a idx will be deleted.  All the\r
-    credentials that are at indices \a idx + 1 and above will be moved\r
-    down to fill the gap and the size of the credential set will\r
-    decrease by one.\r
-\r
-    Use kcdb_credset_del_cred_ref() to delete a credential by\r
-    reference.  Using kcdb_credset_del_cred() is faster than\r
-    kcdb_credset_del_cred_ref().\r
-\r
-    If you call kcdb_credset_del_cred() or kcdb_credset_del_cred_ref()\r
-    from within kcdb_credset_apply(), the credential will only be\r
-    marked as deleted.  They will not be removed.  This means that the\r
-    size of the credential set will not decrease.  To purge the\r
-    deleted credentials from the set, call kcdb_credset_purge() after\r
-    kcdb_credset_apply() completes.\r
-\r
-    \note The credential set cannot be sealed.\r
-\r
-    \see kcdb_credset_del_cred_ref()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_del_cred(khm_handle credset,\r
-                      khm_int32 idx);\r
-\r
-/*! \brief Delete a credential from a credential set by reference.\r
-\r
-    See kcdb_credset_del_cred() for description of what happens when a\r
-    credential is deleted from a credential set.\r
-\r
-    \note The credential set cannot be sealed.\r
-\r
-    \see kcdb_credset_del_cred()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_del_cred_ref(khm_handle credset,\r
-                          khm_handle cred);\r
-\r
-/*! \brief Add a credential to a credential set.\r
-\r
-    The credential is added by reference.  In other words, no copy of\r
-    the credential is made.\r
-\r
-    \param[in] idx Index of the new credential.  This must be a value\r
-        in the range 0..(previous size of credential set) or -1.  If\r
-        -1 is specifed, then the credential is appended at the end of\r
-        the set.\r
-\r
-    \note The credential set cannot be sealed.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_add_cred(khm_handle credset,\r
-                      khm_handle cred,\r
-                      khm_int32 idx);\r
-\r
-/*! \brief Get the number of credentials in a credential set.\r
-\r
-    Credentials in a credential set may be volatile.  When\r
-    kcdb_credeset_get_size() is called, the credential set is\r
-    compacted to only include credentials that are active at the time.\r
-    However, when you are iterating through the credential set, it\r
-    might be the case that some credentials would get marked as\r
-    deleted.  These credentials will remain in the credential set\r
-    until the credential set is discarded or another call to\r
-    kcdb_credset_get_size() or kdcb_credset_purge() is made.\r
-\r
-    If the credential set is sealed, then it will not be compacted and\r
-    will include deleted credentials as well.\r
-\r
-    \see kcdb_credset_purge()\r
-    \see kcdb_credset_get_cred()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_get_size(khm_handle credset,\r
-                      khm_size * size);\r
-\r
-/*! \brief Removes credentials that have been marked as deleted from a credential set.\r
-\r
-    See description of \a kcdb_credset_purge() for a description of\r
-    what happens when credntials that are contained in a credential\r
-    set are deleted by an external entity.\r
-\r
-    \note The credential set cannot be sealed.\r
-\r
-    \see kcdb_credset_get_size()\r
-    \see kcdb_credset_get_cred()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_purge(khm_handle credset);\r
-\r
-/*! \brief Applies a function to all the credentials in a credentials set\r
-\r
-    The given function is called for each credential in a credential\r
-    set.  With each iteration, the function is called with a handle to\r
-    the credential and the user defined parameter \a rock.  If the\r
-    function returns anything other than KHM_ERROR_SUCCESS, the\r
-    processing stops.\r
-\r
-    \param[in] credset The credential set to apply the function to, or\r
-        NULL if you want to apply this to the root credential set.\r
-\r
-    \param[in] f Function to call for each credential\r
-\r
-    \param[in] rock An opaque parameter which is to be passed to 'f'\r
-        as the second argument.\r
-\r
-    \retval KHM_ERROR_SUCCESS All the credentials were processed.\r
-\r
-    \retval KHM_ERROR_EXIT The supplied function signalled the\r
-        processing to be aborted.\r
-\r
-    \retval KHM_ERROR_INVALID_PARAM One or more parameters were invalid.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_apply(khm_handle credset, \r
-                   kcdb_cred_apply_func f, \r
-                   void * rock);\r
-\r
-/*! \brief Sort the contents of a credential set.\r
-\r
-    \param[in] rock A custom argument to be passed in to the \a comp function.\r
-\r
-    \note The credential set cannot be sealed.\r
-\r
-    \see kcdb_cred_comp_generic()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_sort(khm_handle credset,\r
-                  kcdb_cred_comp_func comp,\r
-                  void * rock);\r
-\r
-/*! \brief Seal a credential set\r
-\r
-    Sealing a credential set makes it read-only.  To unseal a\r
-    credential set, call kcdb_credset_unseal().\r
-\r
-    Sealing is an additive operation.  kcdb_credset_seal() can be\r
-    called muliple times.  However, for every call to\r
-    kcdb_credset_seal() a call to kcdb_credset_unseal() must be made\r
-    to undo the seal.  The credential set will become unsealed when\r
-    all the seals are released.\r
-\r
-    Once sealed, the credential set will not allow any operation that\r
-    might change its contents.  However, a selaed credential set can\r
-    still be delted.\r
-\r
-    \see kcdb_credset_unseal()\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credset_seal(khm_handle credset);\r
-\r
-/*! \brief Unseal a credential set\r
-\r
-    Undoes what kcdb_credset_seal() did.  This does not guarantee that\r
-    the credential set is unsealed since there may be other seals.\r
-\r
-    \see kcdb_credset_seal()\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-kcdb_credset_unseal(khm_handle credset);\r
-\r
-/*! \brief Defines a sort criterion for kcdb_cred_comp_generic()\r
-\r
-    \see kcdb_cred_comp_generic()\r
-*/\r
-typedef struct tag_kcdb_cred_comp_field {\r
-    khm_int32 attrib; /*!< a valid attribute ID */\r
-    khm_int32 order; /*!< one of KCDB_CRED_COMP_INCREASING or\r
-                       KCDB_CRED_COMP_DECREASING.  Optionally,\r
-                       KCDB_CRED_COMP_INITIAL_FIRST may be combined\r
-                       with either. */\r
-} kcdb_cred_comp_field;\r
-\r
-/*! \brief Defines the sort order for a field in ::kcdb_cred_comp_field \r
-\r
-    Sorts lexicographically ascending by string representation of field.\r
-*/\r
-#define KCDB_CRED_COMP_INCREASING 0\r
-\r
-/*! \brief Defines the sort order for a field in ::kcdb_cred_comp_field\r
-\r
-    Sorts lexicographically descending by string representation of\r
-    field.\r
- */\r
-#define KCDB_CRED_COMP_DECREASING 1\r
-\r
-/*! \brief Defines the sort order for a field in ::kcdb_cred_comp_field \r
-\r
-    Any credentials which have the ::KCDB_CRED_FLAG_INITIAL will be\r
-    grouped above any that don't.\r
-\r
-    If that does not apply, then credentials from the primary\r
-    credentials type will be sorted before others.\r
-*/\r
-#define KCDB_CRED_COMP_INITIAL_FIRST 2\r
-\r
-/*! \brief Defines the sort criteria for kcdb_cred_comp_generic()\r
-\r
-    \see kcdb_cred_comp_generic()\r
-*/\r
-typedef struct tag_kcdb_cred_comp_order {\r
-    khm_int32 nFields;\r
-    kcdb_cred_comp_field * fields;\r
-} kcdb_cred_comp_order;\r
-\r
-/*! \brief A generic compare function for comparing credentials.\r
-\r
-    This function can be passed as a parameter to kcdb_credset_sort().\r
-\r
-    The \a rock parameter to this function should be a pointer to a\r
-    ::kcdb_cred_comp_order object.  The \a fields member of the\r
-    ::kcdb_cred_comp_order object should point to an array of\r
-    ::kcdb_cred_comp_field objects, each of which specifies the sort\r
-    order in decreasing order of priority.  The number of\r
-    ::kcdb_cred_comp_field objects in the array should correspond to\r
-    the \a nFields member in the ::kcdb_cred_comp_order object.\r
-\r
-    The array of ::kcdb_cred_comp_field objects define the sort\r
-    criteria, in order.  The \a attrib member should be a valid\r
-    attribute ID, while the \a order member determines whether the\r
-    sort order is increasing or decreasing.  The exact meaning or\r
-    increasing or decreasing depends on the data type of the\r
-    attribute.\r
-\r
-    \param[in] rock a pointer to a ::kcdb_cred_comp_order object\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_comp_generic(khm_handle cred1, \r
-                       khm_handle cred2, \r
-                       void * rock);\r
-\r
-/*@}*/\r
-\r
-/*! \defgroup kcdb_cred Credentials */\r
-/*@{*/\r
-\r
-/*! \brief Maximum number of characters in a credential name */\r
-#define KCDB_CRED_MAXCCH_NAME 256\r
-\r
-/*! \brief Maximum number of bytes in a credential name */\r
-#define KCDB_CRED_MAXCB_NAME (sizeof(wchar_t) * KCDB_CRED_MAXCCH_NAME)\r
-\r
-/*! \brief Marked as deleted */\r
-#define KCDB_CRED_FLAG_DELETED     0x00000008\r
-\r
-/*! \brief Renewable */\r
-#define KCDB_CRED_FLAG_RENEWABLE   0x00000010\r
-\r
-/*! \brief Initial\r
-\r
-    Initial credentials form the basis of an identity.  Some\r
-    properties of an initial credential, such as being renewable, are\r
-    directly inherited by the identity.  An identity is also\r
-    automatically considered valid if it contains a valid initial\r
-    credential.\r
- */\r
-#define KCDB_CRED_FLAG_INITIAL     0x00000020\r
-\r
-/*! \brief Expired\r
-\r
-    The credential's lifetime has ended.\r
- */\r
-#define KCDB_CRED_FLAG_EXPIRED     0x00000040\r
-\r
-/*! \brief Invalid\r
-\r
-    The credential can no longer serve its intended function.  This\r
-    may be because it is expired and is not renewable, or its\r
-    renewable time period has also expired, or for some other reason.\r
- */\r
-#define KCDB_CRED_FLAG_INVALID     0x00000080\r
-\r
-/*! \brief Credential is selected\r
-\r
-    Indicates that the credential is selected.  Note that using this\r
-    flag may be subject to race conditions.\r
- */\r
-#define KCDB_CRED_FLAG_SELECTED    0x00000100\r
-\r
-/*! \brief Bitmask indicating all known credential flags\r
- */\r
-#define KCDB_CRED_FLAGMASK_ALL     0x0000ffff\r
-\r
-/*! \brief External flags\r
-\r
-    These are flags that are provided by the credentials providers.\r
-    The other flags are internal to KCDB and should not be modified.\r
- */\r
-#define KCDB_CRED_FLAGMASK_EXT     (KCDB_CRED_FLAG_INITIAL | KCDB_CRED_FLAG_EXPIRED | KCDB_CRED_FLAG_INVALID | KCDB_CRED_FLAG_RENEWABLE)\r
-\r
-/*! \brief Bitmask indicating dditive flags \r
-\r
-    Additive flags are special flags which are added to exiting\r
-    credentials based on new credentials when doing a collect\r
-    operation.  See details on kcdb_credset_collect()\r
-\r
-    \see kcdb_credset_collect()\r
-*/\r
-#define KCDB_CRED_FLAGMASK_ADDITIVE KCDB_CRED_FLAG_SELECTED\r
-\r
-/*! \brief Generic credentials request\r
-\r
-    This data structure is used as the format for a generic\r
-    credentials reqeust for a ::KMSG_KCDB_REQUEST message.  A plugin\r
-    typically publishes this message so that a credentials provider\r
-    may handle it and in response, obtain the specified credential.\r
-\r
-    While the \a identity, \a type and \a name members of the\r
-    structure are all optional, typically one would specify all three\r
-    or at least two for a credential provider to be able to provide\r
-    the credential unambigously.\r
-\r
-    Credential providers do not need to respond to ::KMSG_KCDB_REQUEST\r
-    messages.  However, if they do, they should make sure that they\r
-    are the only credential provider that is responding by setting the\r
-    \a semaphore member to a non-zero value.  The \a semaphore is set\r
-    to zero when a request is initially sent out.  When incrementing\r
-    the semaphore, the plugin should use a thread safe mechanism to\r
-    ensure that there are no race conditions that would allow more\r
-    than one provider to respond to the message.\r
- */\r
-typedef struct tag_kcdb_cred_request {\r
-    khm_handle identity;        /*!< Identity of the credential.  Set\r
-                                  to NULL if not specified. */\r
-    khm_int32  type;            /*!< Type of the credential.  Set to\r
-                                  KCDB_CREDTYPE_INVALID if not\r
-                                  specified.  */\r
-    wchar_t *  name;            /*!< Name of the credential.  Set to\r
-                                  NULL if not specified.  */\r
-\r
-    khm_handle dest_credset;    /*!< If non-NULL, instructs whoever is\r
-                                  handling the request that the\r
-                                  credential thus obtained be placed\r
-                                  in this credential set in addition\r
-                                  to whereever it may place newly\r
-                                  acquired credentials.  Note that\r
-                                  while this can be NULL if the new\r
-                                  credential does not need to be\r
-                                  placed in a credential set, it can\r
-                                  not equal the root credential\r
-                                  set.  */\r
-\r
-    void *     vparam;        /*!< An unspecified\r
-                                  parameter. Specific credential types\r
-                                  may specify how this field is to be\r
-                                  used. */\r
-\r
-    long       semaphore;       /*!< Incremented by one when this\r
-                                  request is answered.  Only one\r
-                                  credential provider is allowed to\r
-                                  answer a ::KMSG_KCDB_REQUEST\r
-                                  message.  Initially, when the\r
-                                  message is sent out, this member\r
-                                  should be set to zero. */\r
-} kcdb_cred_request;\r
-\r
-/*! \brief Create a new credential\r
-\r
-    \param[in] name Name of credential.  \a name cannot be NULL and cannot\r
-        exceed \a KCDB_CRED_MAXCCH_NAME unicode characters including the \r
-        \a NULL terminator.\r
-    \param[in] identity A reference to an identity.\r
-    \param[in] cred_type A credentials type identifier for the credential.\r
-    \param[out] result Gets a held reference to the newly created credential.\r
-        Call kcdb_cred_release() or kcdb_cred_delete() to release the \r
-        reference.\r
-    \see kcdb_cred_release()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_create(const wchar_t *   name, \r
-                 khm_handle  identity,\r
-                 khm_int32   cred_type,\r
-                 khm_handle * result);\r
-\r
-/*! \brief Duplicate an existing credential.\r
-\r
-    \param[out] newcred A held reference to the new credential if the call\r
-        succeeds.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_dup(khm_handle cred,\r
-              khm_handle * newcred);\r
-\r
-/*! \brief Updates one credential using field values from another\r
-\r
-    All fields that exist in \a vsrc will get copied to \a vdest and will\r
-    overwrite any values that are already there in \a vdest.  However any\r
-    values that exist in \a vdest taht do not exist in \a vsrc will not be\r
-    modified.\r
-\r
-    \retval KHM_ERROR_SUCCESS vdest was successfully updated\r
-    \retval KHM_ERROR_EQUIVALENT all fields in vsrc were present and equivalent in vdest\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_update(khm_handle vdest,\r
-                 khm_handle vsrc);\r
-\r
-/*! \brief Set an attribute in a credential by name\r
-\r
-    \r
-\r
-    \param[in] cbbuf Number of bytes of data in \a buffer.  The\r
-        individual data type handlers may copy in less than this many\r
-        bytes in to the credential.  For some data types where the\r
-        size of the buffer is fixed or can be determined from its\r
-        contents, you can specify ::KCDB_CBSIZE_AUTO for this\r
-        parameter.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_set_attrib(khm_handle cred, \r
-                     const wchar_t * name, \r
-                     void * buffer, \r
-                     khm_size cbbuf);\r
-\r
-/*! \brief Set an attribute in a credential by attribute id\r
-\r
-    \param[in] buffer A pointer to a buffer containing the data to\r
-        assign to the attribute.  Setting this to NULL has the effect\r
-        of removing any data that is already assigned to the\r
-        attribute.  If \a buffer is non-NULL, then \a cbbuf should\r
-        specify the number of bytes in \a buffer.\r
-\r
-    \param[in] cbbuf Number of bytes of data in \a buffer.  The\r
-        individual data type handlers may copy in less than this many\r
-        bytes in to the credential.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_set_attr(khm_handle cred, \r
-                   khm_int32 attr_id, \r
-                   void * buffer, \r
-                   khm_size cbbuf);\r
-\r
-/*! \brief Get an attribute from a credential by name.\r
-\r
-    \param[in] buffer The buffer that is to receive the attribute\r
-        value.  Set this to NULL if only the required buffer size is\r
-        to be returned.\r
-\r
-    \param[in,out] cbbuf The number of bytes available in \a buffer.\r
-        If \a buffer is not sufficient, returns KHM_ERROR_TOO_LONG and\r
-        sets this to the required buffer size.\r
-\r
-    \note Set both \a buffer and \a cbbuf to NULL if only the\r
-        existence of the attribute is to be checked.  If the attribute\r
-        exists in this credential then the function will return\r
-        KHM_ERROR_SUCCESS, otherwise it returns KHM_ERROR_NOT_FOUND.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_get_attrib(khm_handle cred, \r
-                     const wchar_t * name, \r
-                     khm_int32 * attr_type,\r
-                     void * buffer, \r
-                     khm_size * cbbuf);\r
-\r
-/*! \brief Get an attribute from a credential by attribute id.\r
-\r
-    \param[in] buffer The buffer that is to receive the attribute\r
-        value.  Set this to NULL if only the required buffer size is\r
-        to be returned.\r
-\r
-    \param[in,out] cbbuf The number of bytes available in \a buffer.\r
-        If \a buffer is not sufficient, returns KHM_ERROR_TOO_LONG and\r
-        sets this to the required buffer size.\r
-\r
-    \param[out] attr_type Receives the data type of the attribute.\r
-        Set this to NULL if the type is not required.\r
-\r
-    \note Set both \a buffer and \a cbbuf to NULL if only the\r
-        existence of the attribute is to be checked.  If the attribute\r
-        exists in this credential then the function will return\r
-        KHM_ERROR_SUCCESS, otherwise it returns KHM_ERROR_NOT_FOUND.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_get_attr(khm_handle cred, \r
-                   khm_int32 attr_id,\r
-                   khm_int32 * attr_type,\r
-                   void * buffer, \r
-                   khm_size * cbbuf);\r
-\r
-/*! \brief Get the name of a credential.\r
-\r
-    \param[in] buffer The buffer that is to receive the credential\r
-        name.  Set this to NULL if only the required buffer size is to\r
-        be returned.\r
-\r
-    \param[in,out] cbbuf The number of bytes available in \a buffer.\r
-        If \a buffer is not sufficient, returns KHM_ERROR_TOO_LONG and\r
-        sets this to the required buffer size.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_get_name(khm_handle cred, \r
-                   wchar_t * buffer, \r
-                   khm_size * cbbuf);\r
-\r
-/*! \brief Get the string representation of a credential attribute.\r
-\r
-    A shortcut function which generates the string representation of a\r
-    credential attribute directly.\r
-\r
-    \param[in] vcred A handle to a credential\r
-\r
-    \param[in] attr_id The attribute to retrieve\r
-\r
-    \param[out] buffer A pointer to a string buffer which receives the\r
-        string form of the attribute.  Set this to NULL if you only\r
-        want to determine the size of the required buffer.\r
-\r
-    \param[in,out] pcbbuf A pointer to a #khm_int32 that, on entry,\r
-        holds the size of the buffer pointed to by \a buffer, and on\r
-        exit, receives the actual number of bytes that were copied.\r
-\r
-    \param[in] flags Flags for the string conversion. Can be set to\r
-        one of KCDB_TS_LONG or KCDB_TS_SHORT.  The default is\r
-        KCDB_TS_LONG.\r
-\r
-    \retval KHM_ERROR_SUCCESS Success\r
-    \retval KHM_ERROR_NOT_FOUND The given attribute was either invalid\r
-        or was not defined for this credential\r
-    \retval KHM_ERROR_INVALID_PARAM One or more parameters were invalid\r
-    \retval KHM_ERROR_TOO_LONG Either \a buffer was NULL or the\r
-        supplied buffer was insufficient\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_get_attr_string(khm_handle vcred, \r
-                          khm_int32 attr_id,\r
-                          wchar_t * buffer, \r
-                          khm_size * pcbbuf,\r
-                          khm_int32 flags);\r
-\r
-/*! \brief Get the string representation of a credential attribute by name.\r
-\r
-    A shortcut function which generates the string representation of a\r
-    credential attribute directly.\r
-\r
-    \param[in] vcred A handle to a credential\r
-\r
-    \param[in] attrib The name of the attribute to retrieve\r
-\r
-    \param[out] buffer A pointer to a string buffer which receives the\r
-        string form of the attribute.  Set this to NULL if you only\r
-        want to determine the size of the required buffer.\r
-\r
-    \param[in,out] pcbbuf A pointer to a #khm_int32 that, on entry,\r
-        holds the size of the buffer pointed to by \a buffer, and on\r
-        exit, receives the actual number of bytes that were copied.\r
-\r
-    \param[in] flags Flags for the string conversion. Can be set to\r
-        one of KCDB_TS_LONG or KCDB_TS_SHORT.  The default is\r
-        KCDB_TS_LONG.\r
-\r
-    \see kcdb_cred_get_attr_string()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_get_attrib_string(khm_handle cred, \r
-                            const wchar_t * name, \r
-                            wchar_t * buffer, \r
-                            khm_size * cbbuf,\r
-                            khm_int32 flags) ;\r
-\r
-\r
-/*! \brief Get a held reference to the identity associated with a credential\r
-\r
-    Use kcdb_identity_release() to release the reference that is\r
-    returned.\r
-\r
-    \see kcdb_identity_relase()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_get_identity(khm_handle cred, \r
-                       khm_handle * identity);\r
-\r
-/*! \brief Set the identity of a credential\r
-\r
-    While it is ill-advised to change the identity of a credential\r
-    that has been placed in one or more credential sets, there can be\r
-    legitimate reasons for doing so.  Only change the identity of a\r
-    credential that is not placed in a credential set or placed in a\r
-    credential set that is only used by a single entity.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_set_identity(khm_handle vcred,\r
-                       khm_handle id);\r
-\r
-/*! \brief Get the serial number for the credential.\r
-\r
-    Each credential gets assigned a serial number at the time it is\r
-    created.  This will stay with the credential for its lifetime.\r
-\r
-    \param[out] pserial Receives the serial number. Cannot be NULL.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_get_serial(khm_handle cred,\r
-                     khm_ui_8 * pserial);\r
-\r
-/*! \brief Get the type of the credential.\r
-\r
-    The returned type is a credential type. Doh.\r
-\r
-    \param[out] type Receives the type.  Cannot be NULL.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_get_type(khm_handle cred,\r
-                   khm_int32 * type);\r
-\r
-/*! \brief Retrieve flags from a credential\r
-\r
-    The flags returned will be place in the location pointed to by \a\r
-    flags.  Note that the specified credential must be an active\r
-    credential for the operation to succeed.  This means the\r
-    ::KCDB_CRED_FLAG_DELETED will never be retured by this function.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_get_flags(khm_handle cred,\r
-                    khm_int32 * flags);\r
-\r
-/*! \brief Set the flags of a credential\r
-\r
-    The flags specified in the \a mask parameter will be set to the\r
-    values specified in the \a flags parameter.  The flags that are\r
-    not included in \a mask will not be modified.\r
-\r
-    This function can not be used to set the ::KCDB_CRED_FLAG_DELETED\r
-    flag.  If this bit is specified in either \a flags or \a mask, it\r
-    will be ignored.\r
-\r
-    \see ::KCDB_CRED_FLAGMASK_ALL\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_set_flags(khm_handle cred,\r
-                    khm_int32 flags,\r
-                    khm_int32 mask);\r
-\r
-/*! \brief Hold a reference to a credential.\r
-\r
-    Use kcdb_cred_release() to release the reference.\r
-\r
-    \see kcdb_cred_release()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_hold(khm_handle cred);\r
-\r
-/*! \brief Release a held reference to a credential.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_release(khm_handle cred);\r
-\r
-/*! \brief Delete a credential.\r
-\r
-    The credential will be marked for deletion and will continue to\r
-    exist until all held references are released.  If the credential\r
-    is bound to a credential set or the root credential store, it will\r
-    be removed from the respective container.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_cred_delete(khm_handle cred);\r
-\r
-/*! \brief Compare an attribute of two credentials by name.\r
-\r
-    \return The return value is dependent on the type of the attribute\r
-    and indicate a weak ordering of the attribute values of the two\r
-    credentials.  If one or both credentials do not contain the\r
-    attribute, the return value is 0, which signifies that no ordering\r
-    can be determined.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_creds_comp_attrib(khm_handle cred1, \r
-                       khm_handle cred2, \r
-                       const wchar_t * name);\r
-\r
-/*! \brief Compare an attribute of two credentials by attribute id.\r
-\r
-    \return The return value is dependent on the type of the attribute\r
-    and indicate a weak ordering of the attribute values of the two\r
-    credentials.  If one or both credentials do not contain the\r
-    attribute, the return value is 0, which signifies that no ordering\r
-    can be determined.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_creds_comp_attr(khm_handle cred1, \r
-                     khm_handle cred2, \r
-                     khm_int32 attr_id);\r
-\r
-/*! \brief Compare two credentials for equivalence\r
-\r
-    \return Non-zero if the two credentials are equal.  Zero otherwise.\r
-    \note Two credentials are considered equal if all the following hold:\r
-        - Both refer to the same identity.\r
-        - Both have the same name.\r
-        - Both have the same type.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_creds_is_equal(khm_handle cred1,\r
-                    khm_handle cred2);\r
-\r
-/*@}*/\r
-/*@}*/\r
-\r
-/********************************************************************/\r
-\r
-/*! \defgroup kcdb_type Credential attribute types\r
-\r
-@{*/\r
-\r
-/*! \brief Convert a field to a string\r
-\r
-    Provides a string representation of a field in a credential.  The\r
-    data buffer can be assumed to be valid.\r
-\r
-    On entry, \a s_buf can be NULL if only the required size of the\r
-    buffer is to be returned.  \a pcb_s_buf should be non-NULL and\r
-    should point to a valid variable of type ::khm_size that will, on\r
-    entry, contain the size of the buffer pointed to by \a s_buf if \a\r
-    s_buf is not \a NULL, and on exit will contain the number of bytes\r
-    consumed in \a s_buf, or the required size of the buffer if \a\r
-    s_buf was NULL or the size of the buffer was insufficient.\r
-\r
-    The implementation should verify the parameters that are passed in\r
-    to the function.\r
-\r
-    The data pointed to by \a data should not be modified in any way.\r
-\r
-    \param[in] data Valid pointer to a block of data\r
-\r
-    \param[in] cb_data Number of bytes in data block pointed to by \a\r
-        data\r
-\r
-    \param[out] s_buf Buffer to receive the string representation of\r
-        data.  If the data type flags has KCDB_TYPE_FLAG_CB_AUTO, then\r
-        this parameter could be set to KCDB_CBSIZE_AUTO.  In this\r
-        case, the function should compute the size of the input buffer\r
-        assuming that the input buffer is valid.\r
-\r
-    \param[in,out] pcb_s_buf On entry, contains the size of the buffer\r
-        pointed to by \a s_buf, and on exit, contains the number of\r
-        bytes used by the string representation of the data including\r
-        the NULL terminator\r
-\r
-    \param[in] flags Flags for formatting the string\r
-\r
-    \retval KHM_ERROR_SUCCESS The string representation of the data\r
-        field was successfully copied to \a s_buf and the size of the\r
-        buffer used was copied to \a pcb_s_buf.\r
-\r
-    \retval KHM_ERROR_INVALID_PARAM One or more parameters were invalid\r
-\r
-    \retval KHM_ERROR_TOO_LONG Either \a s_buf was \a NULL or the size\r
-        indicated by \a pcb_s_buf was too small to contain the string\r
-        representation of the value.  The required size of the buffer\r
-        is in \a pcb_s_buf.\r
-\r
-    \note This documents the expected behavior of this prototype function\r
-\r
-    \see ::kcdb_type\r
- */\r
-typedef khm_int32 \r
-(KHMAPI *kcdb_dtf_toString)(const void *     data,\r
-                            khm_size         cb_data,\r
-                            wchar_t *        s_buf,\r
-                            khm_size *       pcb_s_buf,\r
-                            khm_int32        flags);\r
-\r
-/*! \brief Verifies whetehr the given buffer contains valid data\r
-\r
-    The function should examine the buffer and the size of the buffer\r
-    and determine whether or not the buffer contains valid data for\r
-    this data type.\r
-\r
-    The data field pointed to by \a data should not be modified in any\r
-    way.\r
-\r
-    \param[in] data A pointer to a data buffer\r
-\r
-    \param[in] cb_data The number of bytes in the data buffer. If the\r
-        data type flags has KCDB_TYPE_FLAG_CB_AUTO, then this\r
-        parameter could be set to KCDB_CBSIZE_AUTO.  In this case, the\r
-        function should compute the size of the input buffer assuming\r
-        that the input buffer is valid.\r
-\r
-    \return TRUE if the data is valid, FALSE otherwise.\r
-\r
-    \note This documents the expected behavior of this prototype function\r
-\r
-    \see ::kcdb_type\r
-*/\r
-typedef khm_boolean \r
-(KHMAPI *kcdb_dtf_isValid)(const void *     data,\r
-                           khm_size         cb_data);\r
-\r
-/*! \brief Compare two fields\r
-\r
-    Compare the two data fields and return a value indicating their\r
-    relative ordering.  The return value follows the same\r
-    specification as strcmp().\r
-\r
-    Both data buffers that are passed in can be assumed to be valid.\r
-\r
-    None of the data buffers should be modified in any way.\r
-\r
-    \param[in] data_l Valid pointer to first data buffer\r
-\r
-    \param[in] cb_data_l Number of bytes in \a data_l. If the data\r
-        type flags has KCDB_TYPE_FLAG_CB_AUTO, then this parameter\r
-        could be set to KCDB_CBSIZE_AUTO.  In this case, the function\r
-        should compute the size of the input buffer assuming that the\r
-        input buffer is valid.\r
-\r
-    \param[in] data_r Valid pointer to second data buffer\r
-\r
-    \param[in] cb_data_r Number of bytes in \a data_r. If the data\r
-        type flags has KCDB_TYPE_FLAG_CB_AUTO, then this parameter\r
-        could be set to KCDB_CBSIZE_AUTO.  In this case, the function\r
-        should compute the size of the input buffer assuming that the\r
-        input buffer is valid.\r
-\r
-    \return The return value should be\r
-        - Less than zero if \a data_l &lt; \a data_r\r
-        - Equal to zero if \a data_l == \a data_r or if this data type can not be compared\r
-        - Greater than zero if \a data_l &gt; \a data_r\r
-\r
-    \note This documents the expected behavior of this prototype function\r
-\r
-    \see ::kcdb_type\r
-*/\r
-typedef khm_int32 \r
-(KHMAPI *kcdb_dtf_comp)(const void *     data_l,\r
-                        khm_size         cb_data_l,\r
-                        const void *     data_r,\r
-                        khm_size         cb_data_r);\r
-\r
-/*! \brief Duplicate a data field\r
-\r
-    Duplicates a data field.  The buffer pointed to by \a data_src\r
-    contains a valid field.  The function should copy the field with\r
-    appropriate adjustments to \a data_dst.\r
-\r
-    The \a data_dst parameter can be NULL if only the required size of\r
-    the buffer is needed.  In this case, teh function should set \a\r
-    pcb_data_dst to the number of bytes required and then return\r
-    KHM_ERROR_TOO_LONG.\r
-\r
-    \param[in] data_src Pointer to a valid data buffer\r
-\r
-    \param[in] cb_data_src Number of bytes in \a data_src. If the data\r
-        type flags has KCDB_TYPE_FLAG_CB_AUTO, then this parameter\r
-        could be set to KCDB_CBSIZE_AUTO.  In this case, the function\r
-        should compute the size of the input buffer assuming that the\r
-        input buffer is valid.\r
-\r
-    \param[out] data_dst Poitner to destination buffer.  Could be NULL\r
-       if only the required size of the destination buffer is to be\r
-       returned.\r
-\r
-    \param[in,out] pcb_data_dst On entry specifies the number of bytes\r
-        in \a data_dst, and on exit should contain the number of bytes\r
-        copied.\r
-\r
-    \retval KHM_ERROR_SUCCESS The data was successfully copied.  The\r
-        number of bytes copied is in \a pcb_data_dst\r
-\r
-    \retval KHM_ERROR_INVALID_PARAM One or more parameters is incorrect.\r
-\r
-    \retval KHM_ERROR_TOO_LONG Either \a data_dst was NULL or the size\r
-        of the buffer was insufficient.  The required size is in \a\r
-        pcb_data_dst\r
-\r
-    \note This documents the expected behavior of this prototype function\r
-\r
-    \see ::kcdb_type\r
- */\r
-typedef khm_int32 \r
-(KHMAPI *kcdb_dtf_dup)(const void * data_src,\r
-                       khm_size cb_data_src,\r
-                       void * data_dst,\r
-                       khm_size * pcb_data_dst);\r
-\r
-/*! \brief A data type descriptor.\r
-\r
-    Handles basic operation for a specific data type.\r
-\r
-    \see \ref cred_data_types\r
-*/\r
-typedef struct tag_kcdb_type {\r
-    wchar_t *   name;\r
-    khm_int32   id;\r
-    khm_int32   flags;\r
-\r
-    khm_size    cb_min;\r
-    khm_size    cb_max;\r
-\r
-    kcdb_dtf_toString    toString;\r
-        /*!< Provides a string representation for a value.  */\r
-\r
-    kcdb_dtf_isValid     isValid;\r
-        /*!< Returns true of the value is valid for this data type */\r
-\r
-    kcdb_dtf_comp        comp;\r
-        /*!< Compare two values and return \a strcmp style return value */\r
-\r
-    kcdb_dtf_dup         dup;\r
-        /*!< Duplicate a value into a secondary buffer */\r
-} kcdb_type;\r
-\r
-/*! \name Flags for kcdb_type::toString\r
-@{*/\r
-/*! \brief Specify that the short form of the string representation should be returned. \r
-\r
-    Flags for #kcdb_type::toString.  The flag specifies how long the\r
-    string representation should be.  The specific length of a short\r
-    or long description is not restricted and it is up to the\r
-    implementation to choose how to interpret the flags.\r
-\r
-    Usually, KCDB_TS_SHORT is specified when the amount of space that\r
-    is available to display the string is very restricted.  It may be\r
-    the case that the string is truncated to facilitate displaying in\r
-    a constrainted space.  \r
-*/\r
-#define KCDB_TS_SHORT   1\r
-\r
-/*! \brief Specify that the long form of the string representation should be returned \r
-\r
-    Flags for #kcdb_type::toString.  The flag specifies how long the\r
-    string representation should be.  The specific length of a short\r
-    or long description is not restricted and it is up to the\r
-    implementation to choose how to interpret the flags.\r
-\r
-*/\r
-#define KCDB_TS_LONG    0\r
-/*@}*/\r
-\r
-/*! \brief The maximum number of bytes allowed for a value of any type */\r
-#define KCDB_TYPE_MAXCB 16384\r
-\r
-/*! \name Flags for kcdb_type\r
-@{*/\r
-\r
-/*! \brief The type supports KCDB_CBSIZE_AUTO.\r
-\r
-    Used for types where the size of the object can be determined\r
-    through context or by the object content.  Such as for objects\r
-    that have a fixed size or unicode strings that have a terminator.\r
-\r
-    This implies that ALL the object manipulation callbacks that are\r
-    defined in this type definition support the KCDB_CBSIZE_AUTO\r
-    value.\r
-*/\r
-#define KCDB_TYPE_FLAG_CB_AUTO      16\r
-\r
-/*! \brief The \a cb_min member is valid.\r
-\r
-    The \a cb_min member defines the minimum number of bytes that an\r
-    object of this type will consume.\r
-\r
-    \note If this flag is used in conjunction with \a\r
-    KCDB_TYPE_FLAG_CB_MAX then, \a cb_min must be less than or equal\r
-    to \a cb_max. \r
-*/\r
-#define KCDB_TYPE_FLAG_CB_MIN       128\r
-\r
-/*! \brief The \a cb_max member is valid.\r
-\r
-    The \a cb_max member defines the maximum number of bytes that an\r
-    object of this type will consume.\r
-\r
-    \note If this flag is used in conjunction with \a\r
-        KCDB_TYPE_FLAG_CB_MIN then, \a cb_min must be less than or\r
-        equal to \a cb_max. */\r
-#define KCDB_TYPE_FLAG_CB_MAX       256\r
-\r
-/*! \brief Denotes that objects of this type have a fixed size.\r
-\r
-    If this flags is specified, then the type definition must also\r
-    specify cb_min and cb_max, which must both be the same value.\r
-\r
-    \note Implies \a KCDB_TYPE_FLAG_CB_AUTO, \a KCDB_TYPE_FLAG_CB_MIN\r
-        and \a KCDB_TYPE_FLAG_CB_MAX. Pay special attention to the\r
-        implication of \a KCDB_TYPE_FLAG_AUTO.\r
-*/\r
-#define KCDB_TYPE_FLAG_CB_FIXED (KCDB_TYPE_FLAG_CB_AUTO|KCDB_TYPE_FLAG_CB_MIN|KCDB_TYPE_FLAG_CB_MAX)\r
-\r
-/*@}*/\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_type_get_id(const wchar_t *name, khm_int32 * id);\r
-\r
-/*! \brief Return the type descriptor for a given type id\r
-\r
-    \param[out] info Receives a held reference to a type descriptor.\r
-        Use kcdb_type_release_info() to release the handle.  If the \a\r
-        info parameter is NULL, the function returns KHM_ERROR_SUCCESS\r
-        if \a id is a valid type id, and returns KHM_ERROR_NOT_FOUND\r
-        otherwise.\r
-\r
-    \see kcdb_type_release_info()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_type_get_info(khm_int32 id, kcdb_type ** info);\r
-\r
-/*! \brief Release a reference to a type info structure\r
-\r
-    Releases the reference to the type information obtained with a\r
-    prior call to kcdb_type_get_info().\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_type_release_info(kcdb_type * info);\r
-\r
-/*! \brief Get the name of a type\r
-\r
-    Retrieves the non-localized name of the specified type.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_type_get_name(khm_int32 id, \r
-                   wchar_t * buffer, \r
-                   khm_size * cbbuf);\r
-\r
-/*! \brief Register a credentials attribute type\r
-\r
-    The credentials type record pointed to by \a type defines a new\r
-    credential attribute type.  The \a id member of \a type may be set\r
-    to KCDB_TYPE_INVALID to indicate that an attribute ID is to be\r
-    generated automatically.\r
-\r
-    \param[in] type The type descriptor\r
-    \param[out] new_id Receives the identifier for the credential attribute type.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_type_register(const kcdb_type * type, \r
-                   khm_int32 * new_id);\r
-\r
-/*! \brief Unregister a credential attribute type\r
-\r
-    Removes the registration for the specified credentials attribute\r
-    type.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_type_unregister(khm_int32 id);\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_type_get_next_free(khm_int32 * id);\r
-\r
-/*! \name Conversion functions\r
-@{*/\r
-/*! \brief Convert a time_t value to FILETIME\r
-*/\r
-KHMEXP void KHMAPI \r
-TimetToFileTime( time_t t, LPFILETIME pft );\r
-\r
-/*! \brief Convert a time_t interval to a FILETIME interval\r
-*/\r
-KHMEXP void KHMAPI \r
-TimetToFileTimeInterval(time_t t, LPFILETIME pft);\r
-\r
-/*! \brief Convert a FILETIME interval to seconds\r
-*/\r
-KHMEXP long KHMAPI \r
-FtIntervalToSeconds(LPFILETIME pft);\r
-\r
-/*! \brief Convert a FILETIME interval to milliseconds\r
-*/\r
-KHMEXP long KHMAPI \r
-FtIntervalToMilliseconds(LPFILETIME pft);\r
-\r
-/*! \brief Compare two FILETIME values\r
-\r
-    The return value is similar to the return value of strcmp(), based\r
-    on the comparison of the two FILETIME values.\r
- */\r
-KHMEXP long KHMAPI \r
-FtCompare(LPFILETIME pft1, LPFILETIME pft2);\r
-\r
-/*! \brief Convert a FILETIME to a 64 bit int\r
-*/\r
-KHMEXP khm_int64 KHMAPI FtToInt(LPFILETIME pft);\r
-\r
-/*! \brief Convert a 64 bit int to a FILETIME\r
-*/\r
-KHMEXP FILETIME KHMAPI IntToFt(khm_int64 i);\r
-\r
-/*! \brief Calculate the difference between two FILETIMEs\r
-\r
-    Returns the value of ft1 - ft2\r
- */\r
-KHMEXP FILETIME KHMAPI FtSub(LPFILETIME ft1, LPFILETIME ft2);\r
-\r
-/*! \brief Calculate the sum of two FILETIMEs\r
-\r
-    Return the value of ft1 + ft2\r
- */\r
-KHMEXP FILETIME KHMAPI FtAdd(LPFILETIME ft1, LPFILETIME ft2);\r
-\r
-/*! \brief Convert a FILETIME inverval to a string\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-FtIntervalToString(LPFILETIME data, \r
-                   wchar_t * buffer, \r
-                   khm_size * cb_buf);\r
-\r
-/*! \brief Parse a string representing an interval into a FILETIME interval\r
-\r
-    The string is a localized string which should look like the\r
-    following:\r
-\r
-    \code\r
-    [number unit] [number unit]...\r
-    \endcode\r
-\r
-    where \a number is an integer while \a unit is a localized\r
-    (possibly abbreviated) unit specification.  The value of the\r
-    described interval is calculated as the sum of each \a number in\r
-    \a units.  For example :\r
-\r
-    \code\r
-    1 hour 36 minutes\r
-    \endcode\r
-\r
-    would result in an interval specification that's equivalent to 1\r
-    hour and 36 minutes.  Of course there is no restriction on the\r
-    order in which the \a number \a unit specifications are given and\r
-    the same unit may be repeated multiple times.\r
-\r
-    \retval KHM_ERROR_INVALID_PARAM The given string was invalid or had\r
-        a token that could not be parsed.  It can also mean that \a\r
-        pft was NULL or \a str was NULL.\r
-\r
-    \retval KHM_ERROR_SUCCESS The string was successfully parsed and\r
-        the result was placed in \a pft.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-IntervalStringToFt(FILETIME * pft, wchar_t * str);\r
-\r
-/*! \brief Return number of milliseconds till next representation change\r
-\r
-   Returns the number of milliseconds that must elapse away from the\r
-   interval specified in pft \a for the representation of pft to change\r
-   from whatever it is right now.\r
-\r
-   Returns 0 if the representation is not expected to change.\r
-*/\r
-KHMEXP long KHMAPI \r
-FtIntervalMsToRepChange(LPFILETIME pft);\r
-\r
-/*! \brief Convert a safe ANSI string to a Unicode string\r
-\r
-    The resulting string is guaranteed to be NULL terminated and\r
-    within the size limit set by \a cbwstr.\r
-\r
-    If the whole string cannot be converted, \a wstr is set to an\r
-    empty string.\r
-\r
-    \return the number of characters converted.  This is always either\r
-        the length of the string \a astr or 0.\r
-*/\r
-KHMEXP int KHMAPI \r
-AnsiStrToUnicode( wchar_t * wstr, size_t cbwstr, const char * astr);\r
-\r
-/*! \brief Convert a Unicode string to ANSI\r
-\r
-    The resulting string is guaranteed to be NULL terminated and\r
-    within the size limit set by \a cbdest.\r
-\r
-    \return the number of characters converted.  This is always either\r
-        the length of the string \a src or 0.\r
-*/\r
-KHMEXP int KHMAPI \r
-UnicodeStrToAnsi( char * dest, size_t cbdest, const wchar_t * src);\r
-/*@}*/\r
-\r
-/*! \name Standard type identifiers and names \r
-@{*/\r
-\r
-/*! Maximum identifier number */\r
-#define KCDB_TYPE_MAX_ID 255\r
-\r
-/*! \brief Invalid type\r
-\r
-    Used by functions that return a type identifier to indicate that\r
-    the returned type identifier is invalid.  Also used to indicate\r
-    that a type identifier is not available */\r
-#define KCDB_TYPE_INVALID (-1)\r
-\r
-/*! \brief All types\r
-\r
-    Used by filters to indicate that all types are allowed.\r
-*/\r
-#define KCDB_TYPE_ALL       KCDB_TYPE_INVALID\r
-\r
-/*! \brief Void\r
-\r
-    No data.  This is not an actual data type.\r
- */\r
-#define KCDB_TYPE_VOID      0\r
-\r
-/*! \brief String\r
-\r
-    NULL terminated Unicode string.  The byte count for a string\r
-    attribute always includes the terminating NULL.\r
- */\r
-#define KCDB_TYPE_STRING    1\r
-\r
-/*! \brief Data\r
-\r
-    A date/time represented in FILETIME format.\r
- */\r
-#define KCDB_TYPE_DATE      2\r
-\r
-/*! \brief Interval\r
-\r
-    An interval of time represented as the difference between two\r
-    FILETIME values.\r
- */\r
-#define KCDB_TYPE_INTERVAL  3\r
-\r
-/*! \brief 32-bit integer\r
-\r
-    A 32-bit signed integer.\r
- */\r
-#define KCDB_TYPE_INT32     4\r
-\r
-/*! \brief 64-bit integer\r
-\r
-    A 64-bit integer.\r
- */\r
-#define KCDB_TYPE_INT64     5\r
-\r
-/*! \brief Raw data\r
-\r
-    A raw data buffer.\r
- */\r
-#define KCDB_TYPE_DATA      6\r
-\r
-#define KCDB_TYPENAME_VOID      L"Void"\r
-#define KCDB_TYPENAME_STRING    L"String"\r
-#define KCDB_TYPENAME_DATE      L"Date"\r
-#define KCDB_TYPENAME_INTERVAL  L"Interval"\r
-#define KCDB_TYPENAME_INT32     L"Int32"\r
-#define KCDB_TYPENAME_INT64     L"Int64"\r
-#define KCDB_TYPENAME_DATA      L"Data"\r
-/*@}*/\r
-/*@}*/\r
-\r
-/********************************************************************/\r
-\r
-/*! \defgroup kcdb_credattr Credential attributes */\r
-/*@{*/\r
-\r
-/*! \brief Prototype callback function for computed data types.\r
-\r
-    If the flags for a particular attribute specifies that the value\r
-    is computed, then a callback function should be specified.  The\r
-    callback function will be called with a handle to a credential\r
-    along with the attribute ID for the requested attribute.  The\r
-    function should place the computed value in \a buffer.  The size\r
-    of the buffer in bytes is specifed in \a cbsize.  However, if \a\r
-    buffer is \a NULL, then the required buffer size should be placed\r
-    in \a cbsize.\r
- */\r
-typedef khm_int32 \r
-(KHMAPI *kcdb_attrib_compute_cb)(khm_handle cred, \r
-                                 khm_int32 id,\r
-                                 void * buffer,\r
-                                 khm_size * cbsize);\r
-\r
-/*! \brief Credential attribute descriptor\r
-\r
-    \see kcdb_attrib_register()\r
-*/\r
-typedef struct tag_kcdb_attrib {\r
-    wchar_t * name;             /*!< Name.  (Not localized,\r
-                                  required) */\r
-    khm_int32 id;               /*!< Identifier.  When registering,\r
-                                  this can be set to\r
-                                  ::KCDB_ATTR_INVALID if a unique\r
-                                  identifier is to be generated. */\r
-    khm_int32 alt_id;           /*!< Alternate identifier.  If the \a\r
-                                  flags specify\r
-                                  ::KCDB_ATTR_FLAG_ALTVIEW, then this\r
-                                  field should specify the identifier\r
-                                  of the canonical attribute from\r
-                                  which this attribute is derived. */\r
-    khm_int32 flags;            /*!< Flags. Combination of \ref\r
-                                  kcdb_credattr_flags "attribute\r
-                                  flags" */\r
-\r
-    khm_int32 type;             /*!< Type of the attribute.  Must be valid. */\r
-\r
-    wchar_t * short_desc;       /*!< Short description. (Localized,\r
-                                  optional) */\r
-\r
-    wchar_t * long_desc;        /*!< Long description. (Localized,\r
-                                  optional) */\r
-\r
-    kcdb_attrib_compute_cb compute_cb;\r
-                                /*!< Callback.  Required if \a flags\r
-                                  specify ::KCDB_ATTR_FLAG_COMPUTED. */\r
-\r
-    khm_size compute_min_cbsize;\r
-                                /*!< Minimum number of bytes required\r
-                                  to store this attribute.  Required\r
-                                  if ::KCDB_ATTR_FLAG_COMPUTED is\r
-                                  specified.*/\r
-    khm_size compute_max_cbsize;\r
-                                /*!< Maximum number of bytes required\r
-                                  to store this attribute.  Required\r
-                                  if ::KCDB_ATTR_FLAG_COMPUTED is\r
-                                  specified.*/\r
-} kcdb_attrib;\r
-\r
-/*! \brief Retrieve the ID of a named attribute */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_attrib_get_id(const wchar_t *name, \r
-                   khm_int32 * id);\r
-\r
-/*! \brief Register an attribute\r
-\r
-    \param[out] new_id Receives the ID of the newly registered\r
-        attribute.  If the \a id member of the ::kcdb_attrib object is\r
-        set to KCDB_ATTR_INVALID, then a unique ID is generated. */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_attrib_register(const kcdb_attrib * attrib, \r
-                     khm_int32 * new_id);\r
-\r
-/*! \brief Retrieve the attribute descriptor for an attribute \r
-\r
-    The descriptor that is returned must be released through a call to\r
-    kcdb_attrib_release_info()\r
-\r
-    If only the validity of the attribute identifier needs to be\r
-    checked, you can pass in NULL for \a attrib.  In this case, if the\r
-    identifier is valid, then the funciton will return\r
-    KHM_ERROR_SUCCESS, otherwise it will return KHM_ERROR_NOT_FOUND.\r
-    \r
-    \see kcdb_attrib_release_info()\r
-    */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_attrib_get_info(khm_int32 id, \r
-                     kcdb_attrib ** attrib);\r
-\r
-/*! \brief Release an attribute descriptor\r
-\r
-    \see kcdb_attrib_get_info()\r
-    */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_attrib_release_info(kcdb_attrib * attrib);\r
-\r
-/*! \brief Unregister an attribute \r
-\r
-    Once an attribute ID has been unregistered, it may be reclaimed by\r
-    a subsequent call to kcdb_attrib_register().\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_attrib_unregister(khm_int32 id);\r
-\r
-/*! \brief Retrieve the description of an attribute \r
-\r
-    \param[in] flags Specify \a KCDB_TS_SHORT to retrieve the short description. */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_attrib_describe(khm_int32 id, \r
-                     wchar_t * buffer, \r
-                     khm_size * cbsize, \r
-                     khm_int32 flags);\r
-\r
-/*! \brief Count attributes\r
-\r
-    Counts the number of attributes that match the given criteria.\r
-    The criteria is specified against the flags of the attribute.  An\r
-    attribute is a match if its flags satisfy the condition below:\r
-\r
-    \code\r
-    (attrib.flags & and_flags) == (eq_flags & and_flags)\r
-    \endcode\r
-\r
-    The number of attributes that match are returned in \a pcount.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_attrib_get_count(khm_int32 and_flags,\r
-                      khm_int32 eq_flags,\r
-                      khm_size * pcount);\r
-\r
-/*! \brief List attribute identifiers\r
-\r
-    Lists the identifiers of the attributes that match the given\r
-    criteria.  The criteria is specified against the flags of the\r
-    attribute.  An attribute is a match if the following condition is\r
-    satisfied:\r
-\r
-    \code\r
-    (attrib.flags & and_flags) == (eq_flags & and_flags)\r
-    \endcode\r
-\r
-    The list of attributes found are copied to the \a khm_int32 array\r
-    specified in \a plist.  The number of elements available in the\r
-    buffer \a plist is specified in \a pcsize.  On exit, \a pcsize\r
-    will hold the actual number of attribute identifiers copied to the\r
-    array.\r
-\r
-    \param[in] and_flags See above\r
-    \param[in] eq_flags See above\r
-    \param[in] plist A khm_int32 array\r
-    \param[in,out] pcsize On entry, holds the number of elements\r
-        available in the array pointed to by \a plist.  On exit, holds\r
-        the number of elements copied to the array.\r
-\r
-    \retval KHM_ERROR_SUCCESS The list of attribute identifiers have\r
-        been copied.\r
-    \retval KHM_ERROR_TOO_LONG The list was too long to fit in the\r
-        supplied buffer.  As many elements as possible have been\r
-        copied to the \a plist array and the required number of\r
-        elements has been written to \a pcsize.\r
-\r
-    \note The \a pcsize parameter specifies the number of khm_int32\r
-        elements in the array and not the number of bytes in the\r
-        array.  This is different from the usual size parameters used\r
-        in the NetIDMgr API.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_attrib_get_ids(khm_int32 and_flags,\r
-                    khm_int32 eq_flags,\r
-                    khm_int32 * plist,\r
-                    khm_size * pcsize);\r
-\r
-/*! \defgroup kcdb_credattr_flags Attribute flags */\r
-/*@{*/\r
-/*! \brief The attribute is required */\r
-#define KCDB_ATTR_FLAG_REQUIRED 0x00000008\r
-\r
-/*! \brief The attribute is computed.\r
-\r
-    If this flag is set, the \a compute_cb, \a compute_min_cbsize and\r
-    \a compute_max_cbsize members of the ::kcdb_attrib attribute\r
-    descriptor must be assigned valid values.\r
-*/\r
-#define KCDB_ATTR_FLAG_COMPUTED 0x00000010\r
-\r
-/*! \brief System attribute.\r
-\r
-    This cannot be specified for a custom attribute.  Implies that the\r
-    value of the attribute is given by the credentials database\r
-    itself.\r
-*/\r
-#define KCDB_ATTR_FLAG_SYSTEM   0x00000020\r
-\r
-/*! \brief Hidden\r
-\r
-    The attribute is not meant to be displayed to the user.  Setting\r
-    this flag prevents this attribute from being listed in the list of\r
-    available data fields in the UI.\r
-*/\r
-#define KCDB_ATTR_FLAG_HIDDEN   0x00000040\r
-\r
-/*! \brief Property\r
-\r
-    The attribute is a property.  The main difference between regular\r
-    attributes and properties are that properties are not allocated\r
-    off the credentials record.  Hence, a property can not be used as\r
-    a credentials field.  Other objects such as identities can hold\r
-    property sets.  A property set can hold both regular attributes as\r
-    well as properties.\r
-*/\r
-#define KCDB_ATTR_FLAG_PROPERTY 0x00000080\r
-\r
-/*! \brief Volatile\r
-\r
-    A volatile property is one whose value changes often, such as\r
-    ::KCDB_ATTR_TIMELEFT.  Some controls will make use of additional\r
-    logic to deal with such values, or not display them at all.\r
- */\r
-#define KCDB_ATTR_FLAG_VOLATILE 0x00000100\r
-\r
-/*! \brief Alternate view\r
-\r
-    The attribute is actually an alternate representation of another\r
-    attribute.  The Canonical attribute name is specified in \a\r
-    alt_id.\r
-\r
-    Sometimes a certain attribute may need to be represented in\r
-    different ways.  You can register multiple attributes for each\r
-    view.  However, you should also provide a canonical attribute for\r
-    whenever the canonical set of attributes of the credential is\r
-    required.\r
- */\r
-#define KCDB_ATTR_FLAG_ALTVIEW  0x00000200\r
-\r
-/*! \brief Transient attribute\r
-\r
-    A transient attribute is one whose absence is meaningful.  When\r
-    updating one record using another, if a transient attribute is\r
-    absent in the source but present in the destination, then the\r
-    attribute is removed from the destination.\r
-*/\r
-#define KCDB_ATTR_FLAG_TRANSIENT 0x00000400\r
-\r
-/*@}*/\r
-\r
-/*! \defgroup kcdb_credattr_idnames Standard attribute IDs and names */\r
-/*@{*/\r
-\r
-/*! \name Attribute related constants */\r
-/*@{*/\r
-/*! \brief Maximum valid attribute ID */\r
-#define KCDB_ATTR_MAX_ID        255\r
-\r
-/*! \brief Minimum valid property ID */\r
-#define KCDB_ATTR_MIN_PROP_ID   4096\r
-\r
-/*! \brief Maximum number of properties */\r
-#define KCDB_ATTR_MAX_PROPS     128\r
-\r
-/*! \brief Maximum valid property ID */\r
-#define KCDB_ATTR_MAX_PROP_ID (KCDB_ATTR_MIN_PROP_ID + KCDB_ATTR_MAX_PROPS - 1)\r
-\r
-/*! \brief Invalid ID */\r
-#define KCDB_ATTR_INVALID   (-1)\r
-\r
-/*! \brief First custom attribute ID */\r
-#define KCDB_ATTRID_USER        20\r
-\r
-/*@}*/\r
-\r
-/*!\name Attribute identifiers  */\r
-/*@{*/\r
-/*! \brief Name of the credential\r
-\r
-    - \b Type: STRING\r
-    - \b Flags: REQUIRED, COMPUTED, SYSTEM\r
- */\r
-#define KCDB_ATTR_NAME          0\r
-\r
-/*! \brief The identity handle for the credential\r
-\r
-    - \b Type: INT64\r
-    - \b Flags: REQUIRED, COMPUTED, SYSTEM, HIDDEN\r
-\r
-    \note The handle returned in by specifying this attribute to\r
-        kcdb_cred_get_attr() or kcdb_cred_get_attrib() is not held.\r
-        While the identity is implicitly held for the duration that\r
-        the credential is held, it is not recommended to obtain a\r
-        handle to the identity using this method.  Use\r
-        kcdb_cred_get_identity() instead.\r
-*/\r
-#define KCDB_ATTR_ID            1\r
-\r
-/*! \brief The name of the identity \r
-\r
-    - \b Type: STRING\r
-    - \b Flags: REQUIRED, COMPUTED, SYSTEM\r
- */\r
-#define KCDB_ATTR_ID_NAME       2\r
-\r
-/*! \brief The type of the credential\r
-\r
-    - \b Type: INT32\r
-    - \b Flags: REQUIRED, COMPUTED, SYSTEM, HIDDEN\r
-*/\r
-#define KCDB_ATTR_TYPE          3\r
-\r
-/*! \brief Type name for the credential \r
-\r
-    - \b Type: STRING\r
-    - \b Flags: REQUIRED, COMPUTED, SYSTEM\r
-*/\r
-#define KCDB_ATTR_TYPE_NAME     4\r
-\r
-/*! \brief Name of the parent credential \r
-\r
-    - \b Type: STRING\r
-    - \b Flags: SYSTEM\r
-*/\r
-#define KCDB_ATTR_PARENT_NAME   5\r
-\r
-/*! \brief Issed on \r
-\r
-    - \b Type: DATE\r
-    - \b Flags: SYSTEM\r
-*/\r
-#define KCDB_ATTR_ISSUE         6\r
-\r
-/*! \brief Expires on \r
-\r
-    - \b Type: DATE\r
-    - \b Flags: SYSTEM\r
-*/\r
-#define KCDB_ATTR_EXPIRE        7\r
-\r
-/*! \brief Renewable period expires on \r
-\r
-    - \b Type: DATE\r
-    - \b Flags: SYSTEM\r
-*/\r
-#define KCDB_ATTR_RENEW_EXPIRE  8\r
-\r
-/*! \brief Time left till expiration \r
-\r
-    - \b Type: INTERVAL\r
-    - \b Flags: SYSTEM, COMPUTED, VOLATILE\r
-*/\r
-#define KCDB_ATTR_TIMELEFT      9\r
-\r
-#define KCDB_ATTR_RENEW_TIMELEFT 10\r
-\r
-/*! \brief Location of the credential\r
-\r
-    - \b Type: STRING\r
-    - \b Flags: SYSTEM\r
-*/\r
-#define KCDB_ATTR_LOCATION      11\r
-\r
-/*! \brief Lifetime of the credential \r
-\r
-    - \b Type: INTERVAL\r
-    - \b Flags: SYSTEM\r
-*/\r
-#define KCDB_ATTR_LIFETIME      12\r
-\r
-#define KCDB_ATTR_RENEW_LIFETIME 13\r
-\r
-/*! \brief Flags for the credential\r
-\r
-    - \b Type: INT32\r
-    - \b Flags: REQUIRED, COMPUTED, SYSTEM, HIDDEN\r
- */\r
-#define KCDB_ATTR_FLAGS         14\r
-\r
-/*@}*/\r
-\r
-/*!\name Attribute names */\r
-/*@{ */\r
-\r
-#define KCDB_ATTRNAME_NAME          L"Name"\r
-#define KCDB_ATTRNAME_ID            L"Identity"\r
-#define KCDB_ATTRNAME_ID_NAME       L"IdentityName"\r
-#define KCDB_ATTRNAME_TYPE          L"TypeId"\r
-#define KCDB_ATTRNAME_TYPE_NAME     L"TypeName"\r
-#define KCDB_ATTRNAME_FLAGS         L"Flags"\r
-\r
-#define KCDB_ATTRNAME_PARENT_NAME   L"Parent"\r
-#define KCDB_ATTRNAME_ISSUE         L"Issued"\r
-#define KCDB_ATTRNAME_EXPIRE        L"Expires"\r
-#define KCDB_ATTRNAME_RENEW_EXPIRE  L"RenewExpires"\r
-#define KCDB_ATTRNAME_TIMELEFT      L"TimeLeft"\r
-#define KCDB_ATTRNAME_RENEW_TIMELEFT L"RenewTimeLeft"\r
-#define KCDB_ATTRNAME_LOCATION      L"Location"\r
-#define KCDB_ATTRNAME_LIFETIME      L"Lifetime"\r
-#define KCDB_ATTRNAME_RENEW_LIFETIME L"RenewLifetime"\r
-\r
-/*@}*/\r
-\r
-/*@}*/\r
-\r
-/*@}*/\r
-\r
-/*****************************************************************************/\r
-\r
-/*! \defgroup kcdb_credtype Credential types */\r
-/*@{*/\r
-\r
-/*! \brief Credential type descriptor */\r
-typedef struct tag_kcdb_credtype {\r
-    wchar_t * name;     /*!< name (less than KCDB_MAXCB_NAME bytes) */\r
-    khm_int32 id;\r
-    wchar_t * short_desc;       /*!< short localized description (less\r
-                                  than KCDB_MAXCB_SHORT_DESC bytes) */\r
-    wchar_t * long_desc;        /*!< long localized descriptionn (less\r
-                                  than KCDB_MAXCB_LONG_DESC bytes) */\r
-    khm_handle sub;             /*!< Subscription for credentials type\r
-                                  hander.  This should be a valid\r
-                                  subscription constructed through a\r
-                                  call to kmq_create_subscription()\r
-                                  and must handle KMSG_CRED messages\r
-                                  that are marked as being sent to\r
-                                  type specific subscriptions.\r
-\r
-                                  The subscription will be\r
-                                  automatically deleted with a call to\r
-                                  kmq_delete_subscription() when the\r
-                                  credentials type is unregistered.*/\r
-\r
-    kcdb_cred_comp_func is_equal; /*!< Used as an additional clause\r
-                                  when comparing two credentials for\r
-                                  equality.  The function this is\r
-                                  actually a comparison function, it\r
-                                  should return zero if the two\r
-                                  credentials are equal and non-zero\r
-                                  if they are not.  The addtional \a\r
-                                  rock parameter is always zero.\r
-\r
-                                  It can be assumed that the identity,\r
-                                  name and credentials type have\r
-                                  already been found to be equal among\r
-                                  the credentials and the credential\r
-                                  type is the type that is being\r
-                                  registered.*/\r
-\r
-#ifdef _WIN32\r
-    HICON icon;\r
-#endif\r
-} kcdb_credtype;\r
-\r
-/*! \brief Maximum value of a credential type identifier\r
-\r
-    Credential type identifiers are assigned serially unless the\r
-    process registering the credential type sets a specific identity.\r
-    The maximum identifier number places a hard limit to the number of\r
-    credential types that can be registered at one time, which is\r
-    KCDB_CREDTYPE_MAX_ID + 1.\r
- */\r
-#define KCDB_CREDTYPE_MAX_ID 31\r
-\r
-/*! \brief Specify all credential types\r
-\r
-    This value is used by functions which filter credentials based on\r
-    credential types.  Specifying this value tells the filter to\r
-    accept all credential types.\r
- */\r
-#define KCDB_CREDTYPE_ALL (-1)\r
-\r
-/*! \brief Automatically determine a credential type identifier\r
-\r
-    Used with kcdb_credtype_register() to specify that the credential\r
-    type identifier should be automatically determined to avoid\r
-    collisions.\r
- */\r
-#define KCDB_CREDTYPE_AUTO (-2)\r
-\r
-/*! \brief An invalid credential type\r
-\r
-    Even though any non positive credential type ID is invalid\r
-    anywhere where a specific credential type ID is required, this\r
-    value is provided for explicit indication that the credential type\r
-    is invalid.  Also it makes code more readable to have a constant\r
-    that shouts out INVALID.\r
-\r
-*/\r
-#define KCDB_CREDTYPE_INVALID (-3)\r
-\r
-/*! \brief Macro predicate for testing whether a credtype is valid\r
-\r
-    Returns TRUE if the given credtype is valid.  This is a safe\r
-    macro.\r
-*/\r
-#define KCDB_CREDTYPE_IS_VALID(t) ((t) >= 0)\r
-\r
-/*! \brief Register a credentials type.\r
-\r
-    The information given in the \a type parameter is used to register\r
-    a new credential type.  Note that the \a name member of the \a\r
-    type should be unique among all credential types.\r
-\r
-    You can specify ::KCDB_CREDTYPE_AUTO as the \a id member of \a\r
-    type to let kcdb_credtype_register() determine a suitable\r
-    credential type identifier.  You can subsequently call\r
-    kcdb_credtype_get_id() to retrieve the generated id or pass a\r
-    valid pointer to a khm_int32 type variable as \a new_id.\r
-\r
-    \param[in] type Credential type descriptor\r
-\r
-    \param[out] new_id The credential type identifier that this type\r
-        was registered as.\r
-\r
-    \retval KHM_ERROR_SUCCESS The credential type was successfully registered.\r
-\r
-    \retval KHM_ERROR_INVALID_PARAM One or more of the parameters were invalid\r
-\r
-    \retval KHM_ERROR_TOO_LONG One or more of the string fields in \a\r
-        type exceeded the character limit for that field.\r
-\r
-    \retval KHM_ERROR_NO_RESOURCES When autogenerating credential type\r
-        identifiers, this value indicates that the maximum number of\r
-        credential types have been registered.  No more registrations\r
-        can be accepted unless some credentials type is unregisred.\r
-\r
-    \retval KHM_ERROR_DUPLICATE The \a name or \a id that was\r
-        specified is already in use.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credtype_register(const kcdb_credtype * type, \r
-                       khm_int32 * new_id);\r
-\r
-/*! \brief Return a held reference to a \a kcdb_credtype object describing the credential type.\r
-\r
-    The reference points to a static internal object of type \a\r
-    kcdb_credtype.  Use the kcdb_credtype_release_info() function to\r
-    release the reference.\r
-\r
-    Also, the structure passed in as the \a type argument to\r
-    kcdb_credtype_register() is not valid as a credential type\r
-    descriptor.  Use kcdb_credtype_get_info() to obtain the actual\r
-    credential type descriptor.\r
-\r
-    \param[in] id Credentials type identifier.\r
-\r
-    \param[out] type Receives the credentials descriptor handle.  If\r
-        \a type is NULL, then no handle is returned.  However, the\r
-        function will still return \a KHM_ERROR_SUCCESS if the \a id\r
-        parameter passed in is a valid credentials type identifier.\r
-\r
-    \see kcdb_credtype_release_info()\r
-    \see kcdb_credtype_register()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credtype_get_info(khm_int32 id, \r
-                       kcdb_credtype ** type);\r
-\r
-/*! \brief Release a reference to a \a kcdb_credtype object\r
-\r
-    Undoes the hold obtained on a \a kcdb_credtype object from a\r
-    previous call to kcdb_credtype_get_info().\r
-\r
-    \see kcdb_credtype_get_info()\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credtype_release_info(kcdb_credtype * type);\r
-\r
-/*! \brief Unregister a credentials type\r
-\r
-    Undoes the registration performed by kcdb_credtype_register().\r
-\r
-    This should only be done when the credentials provider is being\r
-    unloaded.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credtype_unregister(khm_int32 id);\r
-\r
-/*! \brief Retrieve the name of a credentials type\r
-\r
-    Given a credentials type identifier, retrieves the name.  The name\r
-    is not localized and serves as a persistent identifier of the\r
-    credentials type.\r
-\r
-    \param[out] buf The buffer to receive the name.  Could be \a NULL\r
-        if only the length of the buffer is required.\r
-\r
-    \param[in,out] cbbuf On entry, specifies the size of the buffer\r
-        pointed to by \a buf if \a buf is not NULL.  On exit, contains\r
-        the number of bytes copied to \a buf or the required size of\r
-        the buffer.\r
-\r
-    \retval KHM_ERROR_SUCCESS The call succeeded.\r
-\r
-    \retval KHM_ERROR_TOO_LONG Either \a buf was NULL or the supplied\r
-        buffer was not large enough.  The required size is in \a cbbuf.\r
-\r
-    \retval KHM_ERROR_INVALID_PARAM Invalid parameter.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credtype_get_name(khm_int32 id,\r
-                       wchar_t * buf,\r
-                       khm_size * cbbuf);\r
-\r
-/*! \brief Retrieve the type specific subscription for a type\r
-\r
-    Given a credentials type, this function returns the credentials\r
-    type specific subcription.  It may return NULL if the subscription\r
-    is not available.\r
- */\r
-KHMEXP khm_handle KHMAPI \r
-kcdb_credtype_get_sub(khm_int32 id);\r
-\r
-/*! \brief Get the description of a credentials type\r
-\r
-   Unlike the name of a credential type, the description is localized.\r
-\r
-   \param[in] id Credentials type identifier\r
-\r
-   \param[out] buf Receives the description.  Can bet set to NULL if\r
-       only the size of the buffer is required.\r
-\r
-   \param[in,out] cbbuf On entry, specifies the size of the buffer\r
-       pointed to by \a buf.  On exit, specifies the required size of\r
-       the buffer or the number of bytes copied, depending on whether\r
-       the call succeeded or not.\r
-\r
-   \param[in] flags Specify ::KCDB_TS_SHORT if the short version of\r
-       the description is desired if there is more than one.\r
-\r
-   \retval KHM_ERROR_SUCCESS The call succeeded\r
-   \retval KHM_ERROR_TOO_LONG Either \a buf was NULL or the supplied buffer was insufficient.  The required size is specified in \a cbbuf.\r
-   \retval KHM_ERROR_INVALID_PARAM One or more parameters were invalid.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credtype_describe(khm_int32 id,\r
-                       wchar_t * buf,\r
-                       khm_size * cbbuf,\r
-                       khm_int32 flags);\r
-\r
-/*! \brief Look up the identifier of a credentials type by name\r
-\r
-    Given a name, looks up the identifier.\r
-\r
-    \param[in] name Name of the credentials type\r
-    \param[out] id Receives the identifier if the call succeeds\r
-\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_credtype_get_id(const wchar_t * name, \r
-                     khm_int32 * id);\r
-\r
-/*@}*/\r
-\r
-/*********************************************************************/\r
-\r
-/*! \defgroup kcdb_buf Generic access to buffer \r
-\r
-    Currently, credentials and identities both hold record data types.\r
-    This set of API's allow an application to access fields in the\r
-    records using a single interface.  Note that credentials only\r
-    accept regular attributes while identities can hold both\r
-    attributes and properties.\r
-\r
-    Handles to credentials and identities are implicitly also handles\r
-    to records.  Thus they can be directly used as such.\r
-*/\r
-/*@{*/\r
-\r
-/*! \brief Get an attribute from a record by attribute id.\r
-\r
-    \param[in] buffer The buffer that is to receive the attribute\r
-        value.  Set this to NULL if only the required buffer size is\r
-        to be returned.\r
-\r
-    \param[in,out] cbbuf The number of bytes available in \a buffer.\r
-        If \a buffer is not sufficient, returns KHM_ERROR_TOO_LONG and\r
-        sets this to the required buffer size.\r
-\r
-    \param[out] attr_type Receives the data type of the attribute.\r
-        Set this to NULL if the type is not required.\r
-\r
-    \note Set both \a buffer and \a cbbuf to NULL if only the\r
-        existence of the attribute is to be checked.  If the attribute\r
-        exists in this record then the function will return\r
-        KHM_ERROR_SUCCESS, otherwise it returns KHM_ERROR_NOT_FOUND.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_buf_get_attr(khm_handle  record, \r
-                  khm_int32   attr_id, \r
-                  khm_int32 * attr_type, \r
-                  void *      buffer, \r
-                  khm_size *  pcb_buf);\r
-\r
-/*! \brief Get an attribute from a record by name.\r
-\r
-    \param[in] buffer The buffer that is to receive the attribute\r
-        value.  Set this to NULL if only the required buffer size is\r
-        to be returned.\r
-\r
-    \param[in,out] cbbuf The number of bytes available in \a buffer.\r
-        If \a buffer is not sufficient, returns KHM_ERROR_TOO_LONG and\r
-        sets this to the required buffer size.\r
-\r
-    \note Set both \a buffer and \a cbbuf to NULL if only the\r
-        existence of the attribute is to be checked.  If the attribute\r
-        exists in this record then the function will return\r
-        KHM_ERROR_SUCCESS, otherwise it returns KHM_ERROR_NOT_FOUND.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_buf_get_attrib(khm_handle  record,\r
-                    const wchar_t *   attr_name,\r
-                    khm_int32 * attr_type,\r
-                    void *      buffer,\r
-                    khm_size *  pcb_buf);\r
-\r
-/*! \brief Get the string representation of a record attribute.\r
-\r
-    A shortcut function which generates the string representation of a\r
-    record attribute directly.\r
-\r
-    \param[in] record A handle to a record\r
-\r
-    \param[in] attr_id The attribute to retrieve\r
-\r
-    \param[out] buffer A pointer to a string buffer which receives the\r
-        string form of the attribute.  Set this to NULL if you only\r
-        want to determine the size of the required buffer.\r
-\r
-    \param[in,out] pcbbuf A pointer to a #khm_int32 that, on entry,\r
-        holds the size of the buffer pointed to by \a buffer, and on\r
-        exit, receives the actual number of bytes that were copied.\r
-\r
-    \param[in] flags Flags for the string conversion. Can be set to\r
-        one of KCDB_TS_LONG or KCDB_TS_SHORT.  The default is\r
-        KCDB_TS_LONG.\r
-\r
-    \retval KHM_ERROR_SUCCESS Success\r
-    \retval KHM_ERROR_NOT_FOUND The given attribute was either invalid\r
-        or was not defined for this record\r
-    \retval KHM_ERROR_INVALID_PARAM One or more parameters were invalid\r
-    \retval KHM_ERROR_TOO_LONG Either \a buffer was NULL or the\r
-        supplied buffer was insufficient\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_buf_get_attr_string(khm_handle  record,\r
-                         khm_int32   attr_id,\r
-                         wchar_t *   buffer,\r
-                         khm_size *  pcbbuf,\r
-                         khm_int32  flags);\r
-\r
-/*! \brief Get the string representation of a record attribute by name.\r
-\r
-    A shortcut function which generates the string representation of a\r
-    record attribute directly.\r
-\r
-    \param[in] record A handle to a record\r
-\r
-    \param[in] attrib The name of the attribute to retrieve\r
-\r
-    \param[out] buffer A pointer to a string buffer which receives the\r
-        string form of the attribute.  Set this to NULL if you only\r
-        want to determine the size of the required buffer.\r
-\r
-    \param[in,out] pcbbuf A pointer to a #khm_int32 that, on entry,\r
-        holds the size of the buffer pointed to by \a buffer, and on\r
-        exit, receives the actual number of bytes that were copied.\r
-\r
-    \param[in] flags Flags for the string conversion. Can be set to\r
-        one of KCDB_TS_LONG or KCDB_TS_SHORT.  The default is\r
-        KCDB_TS_LONG.\r
-\r
-    \see kcdb_cred_get_attr_string()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_buf_get_attrib_string(khm_handle  record,\r
-                           const wchar_t *   attr_name,\r
-                           wchar_t *   buffer,\r
-                           khm_size *  pcbbuf,\r
-                           khm_int32   flags);\r
-\r
-/*! \brief Set an attribute in a record by attribute id\r
-\r
-    \param[in] cbbuf Number of bytes of data in \a buffer.  The\r
-        individual data type handlers may copy in less than this many\r
-        bytes in to the record.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_buf_set_attr(khm_handle  record,\r
-                  khm_int32   attr_id,\r
-                  void *      buffer,\r
-                  khm_size    cbbuf);\r
-\r
-/*! \brief Set an attribute in a record by name\r
-\r
-    \param[in] cbbuf Number of bytes of data in \a buffer.  The\r
-        individual data type handlers may copy in less than this many\r
-        bytes in to the record.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_buf_set_attrib(khm_handle  record,\r
-                    const wchar_t *   attr_name,\r
-                    void *      buffer,\r
-                    khm_size    cbbuf);\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_buf_hold(khm_handle  record);\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kcdb_buf_release(khm_handle record);\r
-\r
-/*@}*/\r
-\r
-/********************************************************************/\r
-\r
-/* Notification operation constants */\r
-\r
-#define KCDB_OP_INSERT      1\r
-#define KCDB_OP_DELETE      2\r
-#define KCDB_OP_MODIFY      3\r
-#define KCDB_OP_ACTIVATE    4\r
-#define KCDB_OP_DEACTIVATE  5\r
-#define KCDB_OP_HIDE        6\r
-#define KCDB_OP_UNHIDE      7\r
-#define KCDB_OP_SETSEARCH   8\r
-#define KCDB_OP_UNSETSEARCH 9\r
-#define KCDB_OP_NEW_DEFAULT 10\r
-#define KCDB_OP_DELCONFIG   11\r
-\r
-/*@}*/\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._@-/"
+
+/*!
+\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 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 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 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      0x00000800L
+
+/*! \brief Unknown state
+
+    The validity of the identity cannot be determined.  This usually
+    means that an authority could not be contacted.  This flag is to
+    be treated as transient.  If ::KCDB_IDENT_FLAG_INVALID or
+    ::KCDB_IDENT_FLAG_VALID is set for the identity, this flag is to
+    be ignored.
+ */
+#define KCDB_IDENT_FLAG_UNKNOWN     0x00001000L
+
+/*! \brief Read/write flags mask.
+
+    A bitmask that correspond to all the read/write flags in the mask.
+*/
+#define KCDB_IDENT_FLAGMASK_RDWR    0x00001fffL
+
+/*@}*/
+
+/*! \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 \a flags parameter or the \a mask parameter.  The flags set in
+    the \a mask parameter of the identity will be set to the
+    corresponding values in the \a flags parameter.
+
+    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.  Setting either of
+    ::KCDB_IDENT_FLAG_INVALID or ::KCDB_IDENT_FLAG_VALID will
+    automatically reset ::KCDB_IDENT_FLAG_UNKNOWN.
+
+    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 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,
+                        khm_int32 mask);
+
+/*! \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.
+ */
+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. 
+
+    If the configuration space for the identity does not exist and the
+    flags parameter does not specify ::KHM_FLAG_CREATE, then the
+    function will return a failure code as specified in
+    ::khc_open_space().  Depending on whether or not a configuration
+    space was found, the ::KCDB_IDENT_FLAG_CONFIG flag will be set or
+    reset 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 Returns TRUE if the two identities are equal
+
+    Also returns TRUE if both identities are NULL.
+ */
+KHMEXP khm_boolean KHMAPI
+kcdb_identity_is_equal(khm_handle identity1,
+                       khm_handle identity2);
+
+/*! \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,
+                         const 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,
+                         const 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_PARAM 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,
+                                const 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_PARAM 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()
+    \see ::kcdb_credtype
+*/
+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_CREDTYPE_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_PARAM 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 External flags
+
+    These are flags that are provided by the credentials providers.
+    The other flags are internal to KCDB and should not be modified.
+ */
+#define KCDB_CRED_FLAGMASK_EXT     (KCDB_CRED_FLAG_INITIAL | KCDB_CRED_FLAG_EXPIRED | KCDB_CRED_FLAG_INVALID | KCDB_CRED_FLAG_RENEWABLE)
+
+/*! \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(const 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.  For some data types where the
+        size of the buffer is fixed or can be determined from its
+        contents, you can specify ::KCDB_CBSIZE_AUTO for this
+        parameter.
+*/
+KHMEXP khm_int32 KHMAPI 
+kcdb_cred_set_attrib(khm_handle cred, 
+                     const 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, 
+                     const 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_PARAM 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, 
+                            const 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, 
+                       const 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_PARAM 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_PARAM 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(const 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.
+
+    \see kcdb_type_release_info()
+*/
+KHMEXP khm_int32 KHMAPI 
+kcdb_type_get_info(khm_int32 id, kcdb_type ** info);
+
+/*! \brief Release a reference to a type info structure
+
+    Releases the reference to the type information obtained with a
+    prior call to kcdb_type_get_info().
+ */
+KHMEXP khm_int32 KHMAPI 
+kcdb_type_release_info(kcdb_type * info);
+
+/*! \brief Get the name of a type
+
+    Retrieves the non-localized name of the specified type.
+ */
+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(const 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 to a 64 bit int
+*/
+KHMEXP khm_int64 KHMAPI FtToInt(LPFILETIME pft);
+
+/*! \brief Convert a 64 bit int to a FILETIME
+*/
+KHMEXP FILETIME KHMAPI IntToFt(khm_int64 i);
+
+/*! \brief Calculate the difference between two FILETIMEs
+
+    Returns the value of ft1 - ft2
+ */
+KHMEXP FILETIME KHMAPI FtSub(LPFILETIME ft1, LPFILETIME ft2);
+
+/*! \brief Calculate the sum of two FILETIMEs
+
+    Return the value of ft1 + ft2
+ */
+KHMEXP FILETIME KHMAPI FtAdd(LPFILETIME ft1, LPFILETIME ft2);
+
+/*! \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_PARAM 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
+
+/*! \brief Void
+
+    No data.  This is not an actual data type.
+ */
+#define KCDB_TYPE_VOID      0
+
+/*! \brief String
+
+    NULL terminated Unicode string.  The byte count for a string
+    attribute always includes the terminating NULL.
+ */
+#define KCDB_TYPE_STRING    1
+
+/*! \brief Data
+
+    A date/time represented in FILETIME format.
+ */
+#define KCDB_TYPE_DATE      2
+
+/*! \brief Interval
+
+    An interval of time represented as the difference between two
+    FILETIME values.
+ */
+#define KCDB_TYPE_INTERVAL  3
+
+/*! \brief 32-bit integer
+
+    A 32-bit signed integer.
+ */
+#define KCDB_TYPE_INT32     4
+
+/*! \brief 64-bit integer
+
+    A 64-bit integer.
+ */
+#define KCDB_TYPE_INT64     5
+
+/*! \brief Raw data
+
+    A raw data buffer.
+ */
+#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(const 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(const 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
+
+/*! \brief Transient attribute
+
+    A transient attribute is one whose absence is meaningful.  When
+    updating one record using another, if a transient attribute is
+    absent in the source but present in the destination, then the
+    attribute is removed from the destination.
+*/
+#define KCDB_ATTR_FLAG_TRANSIENT 0x00000400
+
+/*@}*/
+
+/*! \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"Issued"
+#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.*/
+
+    kcdb_cred_comp_func is_equal; /*!< Used as an additional clause
+                                  when comparing two credentials for
+                                  equality.  The function this is
+                                  actually a comparison function, it
+                                  should return zero if the two
+                                  credentials are equal and non-zero
+                                  if they are not.  The addtional \a
+                                  rock parameter is always zero.
+
+                                  It can be assumed that the identity,
+                                  name and credentials type have
+                                  already been found to be equal among
+                                  the credentials and the credential
+                                  type is the type that is being
+                                  registered.*/
+
+#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_PARAM 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(const 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_PARAM 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_PARAM 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(const 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,
+                    const 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_PARAM 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,
+                           const 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,
+                    const 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
+#define KCDB_OP_DELCONFIG   11
+
+/*@}*/
+
+#endif
index f7bf4e7bd0d22fd320869307a35ebf0395a0e7a3..2b80d1832d00f3ca40cb71c85d66345682389e8d 100644 (file)
@@ -1,61 +1,61 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KCREDDBINTERNAL_H__\r
-#define __KHIMAIRA_KCREDDBINTERNAL_H__\r
-\r
-#include<windows.h>\r
-#include<kcreddb.h>\r
-#include<kmq.h>\r
-#include<khlist.h>\r
-#include<utils.h>\r
-#include<kherror.h>\r
-#include<khmsgtypes.h>\r
-#include<kconfig.h>\r
-#include<strsafe.h>\r
-\r
-#include<langres.h>\r
-\r
-#include "buf.h"\r
-#include "identity.h"\r
-#include "attrib.h"\r
-#include "type.h"\r
-#include "credential.h"\r
-#include "credset.h"\r
-#include "credtype.h"\r
-\r
-/* globals */\r
-\r
-extern HINSTANCE hinst_kcreddb;\r
-\r
-kconf_schema schema_kcdbconfig[];\r
-\r
-void kcdb_init(void);\r
-void kcdb_exit(void);\r
-khm_handle kcdb_get_config(void);\r
-\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
index 8f8a01b0645bf8725fb325bd14afdc81e9b32407..7702368ce09a8653a98d1138c552125e5484861e 100644 (file)
@@ -1,40 +1,40 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kcreddbinternal.h>\r
-\r
-HINSTANCE hinst_kcreddb;\r
-\r
-void\r
-kcdb_process_attach(HINSTANCE hinstDLL) {\r
-    hinst_kcreddb = hinstDLL;\r
-    kcdb_init();\r
-}\r
-\r
-void\r
-kcdb_process_detach(void) {\r
-    kcdb_exit();\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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();
+}
index 417b214e0a4658ea7c8198f22562e3efcedf04a2..1c3258b3d9ef793b8ca60466da79e200e4c0006a 100644 (file)
@@ -1,48 +1,48 @@
-//{{NO_DEPENDENCIES}}\r
-// Microsoft Visual C++ generated include file.\r
-// Used by D:\work\pismere\athena\auth\krb5\src\windows\identity\kcreddb\lang\en_us\kcredres.rc\r
-//\r
-#define IDS_CREDDB                      101\r
-#define IDS_NAME                        102\r
-#define IDS_IDENTITY                    103\r
-#define IDS_ISSUED                      104\r
-#define IDS_EXPIRES                     105\r
-#define IDS_TIMELEFT                    106\r
-#define IDS_LOCATION                    107\r
-#define IDS_PARENT                      108\r
-#define IDS_TYPE                        109\r
-#define IDS_IVL_EXPIRED                 110\r
-#define IDS_IVL_D_H                     111\r
-#define IDS_IVL_H_M                     112\r
-#define IDS_IVL_M_S                     113\r
-#define IDS_IVL_S                       114\r
-#define IDS_IVL_UNKNOWN                 115\r
-#define IDS_LIFETIME                    116\r
-#define IDS_IVL_1D                      117\r
-#define IDS_IVL_1H                      118\r
-#define IDS_IVL_1M                      119\r
-#define IDS_IVL_1S                      120\r
-#define IDS_IVL_D                       121\r
-#define IDS_IVL_H                       122\r
-#define IDS_IVL_M                       123\r
-#define IDS_IVL_S_SPEC                  124\r
-#define IDS_IVL_M_SPEC                  125\r
-#define IDS_IVL_H_SPEC                  126\r
-#define IDS_IVL_D_SPEC                  127\r
-#define IDS_IVl_W_SPEC                  128\r
-#define IDS_IVL_W_SPEC                  128\r
-#define IDS_FLAGS                       129\r
-#define IDS_RENEW_TIMELEFT              130\r
-#define IDS_RENEW_EXPIRES               131\r
-#define IDS_RENEW_LIFETIME              132\r
-\r
-// Next default values for new objects\r
-// \r
-#ifdef APSTUDIO_INVOKED\r
-#ifndef APSTUDIO_READONLY_SYMBOLS\r
-#define _APS_NEXT_RESOURCE_VALUE        102\r
-#define _APS_NEXT_COMMAND_VALUE         40001\r
-#define _APS_NEXT_CONTROL_VALUE         1001\r
-#define _APS_NEXT_SYMED_VALUE           101\r
-#endif\r
-#endif\r
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by D:\work\pismere\athena\auth\krb5\src\windows\identity\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_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
index dfb47e0d4348a4986f236cc324e07adc8728bc02..bc587b278154c31b95b308ca08c132b771057fb2 100644 (file)
@@ -1,27 +1,27 @@
-//{{NO_DEPENDENCIES}}\r
-// Microsoft Visual C++ generated include file.\r
-// Used by kcreddb.rc\r
-//\r
-#define IDS_PROJNAME                    100\r
-#define IDR_WMDMLOGGER                  101\r
-#define IDS_LOG_SEV_INFO                201\r
-#define IDS_LOG_SEV_WARN                202\r
-#define IDS_LOG_SEV_ERROR               203\r
-#define IDS_LOG_DATETIME                204\r
-#define IDS_LOG_SRCNAME                 205\r
-#define IDS_DEF_LOGFILE                 301\r
-#define IDS_DEF_MAXSIZE                 302\r
-#define IDS_DEF_SHRINKTOSIZE            303\r
-#define IDS_DEF_LOGENABLED              304\r
-#define IDS_MUTEX_TIMEOUT               401\r
-\r
-// Next default values for new objects\r
-// \r
-#ifdef APSTUDIO_INVOKED\r
-#ifndef APSTUDIO_READONLY_SYMBOLS\r
-#define _APS_NEXT_RESOURCE_VALUE        201\r
-#define _APS_NEXT_COMMAND_VALUE         32768\r
-#define _APS_NEXT_CONTROL_VALUE         201\r
-#define _APS_NEXT_SYMED_VALUE           101\r
-#endif\r
-#endif\r
+//{{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
index 48630b5fd5ca3195326c3e22ad3a9651515f8fc0..e4fd2df2c49948ba246289fa1fd6c665a0fed720 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kcreddbinternal.h>\r
-#include<limits.h>\r
-\r
-CRITICAL_SECTION cs_type;\r
-hashtable * kcdb_type_namemap;\r
-kcdb_type_i ** kcdb_type_tbl;\r
-kcdb_type_i * kcdb_types = NULL;\r
-\r
-/* Void */\r
-\r
-#define GENERIC_VOID_STR L"(Void)"\r
-\r
-khm_int32 KHMAPI kcdb_type_void_toString(\r
-    const void * d, \r
-    khm_size cbd, \r
-    wchar_t * buffer, \r
-    khm_size * cb_buf, \r
-    khm_int32 flags)\r
-{\r
-    size_t cbsize;\r
-\r
-    if(!cb_buf)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cbsize = sizeof(GENERIC_VOID_STR);\r
-\r
-    if(!buffer || *cb_buf < cbsize) {\r
-        *cb_buf = cbsize;\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-\r
-    StringCbCopy(buffer, *cb_buf, GENERIC_VOID_STR);\r
-\r
-    *cb_buf = cbsize;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-khm_boolean KHMAPI kcdb_type_void_isValid(\r
-    const void * d,\r
-    khm_size cbd)\r
-{\r
-    /* void is always valid, even if d is NULL */\r
-    return TRUE;\r
-}\r
-\r
-khm_int32 KHMAPI kcdb_type_void_comp(\r
-    const void * d1,\r
-    khm_size cbd1,\r
-    const void * d2,\r
-    khm_size cbd2)\r
-{\r
-    /* voids can not be compared */\r
-    return 0;\r
-}\r
-\r
-khm_int32 KHMAPI kcdb_type_void_dup(\r
-    const void * d_src,\r
-    khm_size cbd_src,\r
-    void * d_dst,\r
-    khm_size * cbd_dst)\r
-{\r
-    if(!cbd_dst)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    *cbd_dst = 0;\r
-\r
-    /* copying a void doesn't do much */\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-\r
-/* String */\r
-khm_int32 KHMAPI kcdb_type_string_toString(\r
-    const void * d, \r
-    khm_size cbd, \r
-    wchar_t * buffer, \r
-    khm_size * cb_buf, \r
-    khm_int32 flags)\r
-{\r
-    size_t cbsize;\r
-    wchar_t * sd;\r
-\r
-    if(!cb_buf)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    sd = (wchar_t *) d;\r
-\r
-    if(FAILED(StringCbLength(sd, KCDB_TYPE_MAXCB, &cbsize)))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cbsize += sizeof(wchar_t);\r
-\r
-    if(!buffer || *cb_buf < cbsize) {\r
-        *cb_buf = cbsize;\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-\r
-    StringCbCopy(buffer, *cb_buf, sd);\r
-\r
-    *cb_buf = cbsize;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-khm_boolean KHMAPI kcdb_type_string_isValid(\r
-    const void * d,\r
-    khm_size cbd)\r
-{\r
-    size_t cbsize;\r
-\r
-    if(cbd == KCDB_CBSIZE_AUTO)\r
-        cbd = KCDB_TYPE_MAXCB;\r
-\r
-    if(FAILED(StringCbLength((wchar_t *) d, cbd, &cbsize)))\r
-        return FALSE;\r
-    else\r
-        return TRUE;\r
-}\r
-\r
-khm_int32 KHMAPI kcdb_type_string_comp(\r
-    const void * d1,\r
-    khm_size cbd1,\r
-    const void * d2,\r
-    khm_size cbd2)\r
-{\r
-    return wcscmp((const wchar_t *) d1, (const wchar_t *) d2);\r
-}\r
-\r
-khm_int32 KHMAPI kcdb_type_string_dup(\r
-    const void * d_src,\r
-    khm_size cbd_src,\r
-    void * d_dst,\r
-    khm_size * cbd_dst)\r
-{\r
-    size_t cbsize;\r
-\r
-    if(!cbd_dst)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(cbd_src == KCDB_CBSIZE_AUTO) {\r
-        cbd_src = KCDB_TYPE_MAXCB;\r
-    }\r
-\r
-    if(FAILED(StringCbLength((const wchar_t *) d_src, cbd_src, &cbsize))) {\r
-        return KHM_ERROR_UNKNOWN;\r
-    }\r
-\r
-    cbsize += sizeof(wchar_t);\r
-\r
-    if(!d_dst || *cbd_dst < cbsize) {\r
-        *cbd_dst = cbsize;\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-\r
-    StringCbCopy((wchar_t *) d_dst, *cbd_dst, (const wchar_t *) d_src);\r
-    *cbd_dst = cbsize;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* Date and time */\r
-\r
-\r
-khm_int32 KHMAPI kcdb_type_date_toString(\r
-    const void * d, \r
-    khm_size cbd, \r
-    wchar_t * buffer, \r
-    khm_size * cb_buf, \r
-    khm_int32 flags)\r
-{\r
-    size_t cbsize;\r
-    size_t cchsize;\r
-    wchar_t * bufend;\r
-    SYSTEMTIME st_now;\r
-    SYSTEMTIME st_d;\r
-    SYSTEMTIME st_dl;\r
-    FILETIME *ft;\r
-    int today = 0;\r
-\r
-    if(!cb_buf)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    ft = (FILETIME *) d;\r
-\r
-    GetLocalTime(&st_now);\r
-    FileTimeToSystemTime(ft, &st_d);\r
-    SystemTimeToTzSpecificLocalTime(NULL, &st_d, &st_dl);\r
-    if (st_now.wYear == st_dl.wYear &&\r
-        st_now.wMonth == st_dl.wMonth &&\r
-        st_now.wDay == st_dl.wDay)\r
-        today = 1;\r
-\r
-    if(today && (flags & KCDB_TS_SHORT)) {\r
-        cbsize = 0;\r
-    } else {\r
-        cbsize = GetDateFormat(\r
-            LOCALE_USER_DEFAULT,\r
-            DATE_SHORTDATE,\r
-            &st_dl,\r
-            NULL,\r
-            NULL,\r
-            0) * sizeof(wchar_t);\r
-    }\r
-\r
-    cbsize += GetTimeFormat(\r
-        LOCALE_USER_DEFAULT,\r
-        0,\r
-        &st_dl,\r
-        NULL,\r
-        NULL,\r
-        0) * sizeof(wchar_t);\r
-\r
-    if(!buffer || *cb_buf < cbsize) {\r
-        *cb_buf = cbsize;\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-\r
-    cchsize = cbsize / sizeof(wchar_t);\r
-\r
-    if(!today || !(flags & KCDB_TS_SHORT)) {\r
-        size_t cch_buf_len;\r
-\r
-        GetDateFormat(\r
-            LOCALE_USER_DEFAULT,\r
-            DATE_SHORTDATE,\r
-            &st_dl,\r
-            NULL,\r
-            buffer,\r
-            (int) cchsize);\r
-\r
-        StringCchCat(buffer, cchsize, L" ");\r
-\r
-        StringCchLength(buffer, cchsize, &cch_buf_len);\r
-\r
-        bufend = buffer + cch_buf_len;\r
-        cchsize -= cch_buf_len;\r
-    } else {\r
-        bufend = buffer;\r
-    }\r
-\r
-    GetTimeFormat(\r
-        LOCALE_USER_DEFAULT,\r
-        0,\r
-        &st_dl,\r
-        NULL,\r
-        bufend,\r
-        (int) cchsize);\r
-\r
-    *cb_buf = cbsize;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-khm_boolean KHMAPI kcdb_type_date_isValid(\r
-    const void * d,\r
-    khm_size cbd)\r
-{\r
-    return (d && (cbd == KCDB_CBSIZE_AUTO || cbd == sizeof(FILETIME)));\r
-}\r
-\r
-khm_int32 KHMAPI kcdb_type_date_comp(\r
-    const void * d1,\r
-    khm_size cbd1,\r
-    const void * d2,\r
-    khm_size cbd2)\r
-{\r
-    return (khm_int32) CompareFileTime((CONST FILETIME *) d1, (CONST FILETIME *) d2);\r
-}\r
-\r
-khm_int32 KHMAPI kcdb_type_date_dup(\r
-    const void * d_src,\r
-    khm_size cbd_src,\r
-    void * d_dst,\r
-    khm_size * cbd_dst)\r
-{\r
-    if(d_dst && *cbd_dst >= sizeof(FILETIME)) {\r
-        *cbd_dst = sizeof(FILETIME);\r
-        *((FILETIME *) d_dst) = *((FILETIME *) d_src);\r
-        return KHM_ERROR_SUCCESS;\r
-    } else {\r
-        *cbd_dst = sizeof(FILETIME);\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-}\r
-\r
-/* Interval */\r
-\r
-/* returns the number of milliseconds that must elapse away from the\r
-   interval specified in pft for the representation of pft to change\r
-   from whatever it is right now */\r
-KHMEXP long KHMAPI \r
-FtIntervalMsToRepChange(LPFILETIME pft)\r
-{\r
-    __int64 ms,s,m,h,d;\r
-    __int64 ift;\r
-    long l;\r
-\r
-    ift = FtToInt(pft);\r
-    ms = ift / 10000i64;\r
-    \r
-    if(ms < 0 || ift == _I64_MAX)\r
-        return -1;\r
-\r
-    s = ms / 1000i64;\r
-    m = s / 60;\r
-    h = s / 3600;\r
-    d = s / (3600*24);\r
-\r
-    if (d > 0) {\r
-        /* rep change at next hour change */\r
-        l = (long) (ms % (3600*1000i64));\r
-    } else if (h > 0) {\r
-        /* rep change at next minute change */\r
-        l = (long) (ms % (60*1000i64));\r
-    } else if (m > 5) {\r
-        /* rep change at next minute change */\r
-        l = (long) (ms % (60*1000i64));\r
-    } else {\r
-        /* rep change at next second change */\r
-        l = (long) (ms % 1000);\r
-    }\r
-\r
-    return l;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-FtIntervalToString(LPFILETIME data, wchar_t * buffer, khm_size * cb_buf)\r
-{\r
-    size_t cbsize;\r
-    __int64 s,m,h,d;\r
-    __int64 ift;\r
-    wchar_t ibuf[256];\r
-    wchar_t fbuf[256];\r
-    wchar_t * t;\r
-\r
-    if(!cb_buf)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    ift = FtToInt(data);\r
-    s = ift / 10000000i64;\r
-\r
-    m = s / 60;\r
-    h = s / 3600;\r
-    d = s / (3600*24);\r
-\r
-    if(ift == _I64_MAX) {\r
-#ifdef INDICATE_UNKNOWN_EXPIRY_TIMES\r
-        LoadString(hinst_kcreddb, IDS_IVL_UNKNOWN, ibuf, sizeof(ibuf)/sizeof(wchar_t));\r
-#else\r
-        StringCbCopy(ibuf, sizeof(ibuf), L"");\r
-#endif\r
-    } else if(s < 0) {\r
-        LoadString(hinst_kcreddb, IDS_IVL_EXPIRED, ibuf, sizeof(ibuf)/sizeof(wchar_t));\r
-    } else if(d > 0) {\r
-        h = (s - (d * 3600 * 24)) / 3600;\r
-        if(d == 1) {\r
-            LoadString(hinst_kcreddb, IDS_IVL_1D, ibuf, ARRAYLENGTH(ibuf));\r
-        } else {\r
-            LoadString(hinst_kcreddb, IDS_IVL_D, fbuf, ARRAYLENGTH(fbuf));\r
-            StringCbPrintf(ibuf, sizeof(ibuf), fbuf, d);\r
-        }\r
-        if(h > 0) {\r
-            StringCbCat(ibuf, sizeof(ibuf), L" ");\r
-            t = ibuf + wcslen(ibuf);\r
-            if(h == 1)\r
-            {\r
-                LoadString(hinst_kcreddb, IDS_IVL_1H, t,\r
-                           (int) (ARRAYLENGTH(ibuf) - wcslen(ibuf)));\r
-            } else {\r
-                LoadString(hinst_kcreddb, IDS_IVL_H, fbuf,\r
-                           (int) ARRAYLENGTH(fbuf));\r
-                StringCbPrintf(t, sizeof(ibuf) - wcslen(ibuf)*sizeof(wchar_t), fbuf, h);\r
-            }\r
-        }\r
-    } else if(h > 0 || m > 5) {\r
-        m = (s - (h * 3600)) / 60;\r
-        if(h == 1) {\r
-            LoadString(hinst_kcreddb, IDS_IVL_1H, ibuf, ARRAYLENGTH(ibuf));\r
-        } else if (h > 1) {\r
-            LoadString(hinst_kcreddb, IDS_IVL_H, fbuf, ARRAYLENGTH(fbuf));\r
-            StringCbPrintf(ibuf, sizeof(ibuf), fbuf, h);\r
-        } else {\r
-            *ibuf = L'\0';\r
-        }\r
-\r
-        if(m > 0 || h == 0) {\r
-            if (h >= 1)\r
-                StringCbCat(ibuf, sizeof(ibuf), L" ");\r
-\r
-            t = ibuf + wcslen(ibuf);\r
-            if(m == 1)\r
-            {\r
-                LoadString(hinst_kcreddb, IDS_IVL_1M, t,\r
-                           (int) (ARRAYLENGTH(ibuf) - wcslen(ibuf)));\r
-            } else {\r
-                LoadString(hinst_kcreddb, IDS_IVL_M, fbuf,\r
-                           (int) ARRAYLENGTH(fbuf));\r
-                StringCbPrintf(t, sizeof(ibuf) - wcslen(ibuf)*sizeof(wchar_t), fbuf, m);\r
-            }\r
-        }\r
-    } else if(m > 0) {\r
-        s -= m * 60;\r
-        if(m == 1) {\r
-            LoadString(hinst_kcreddb, IDS_IVL_1M, ibuf, ARRAYLENGTH(ibuf));\r
-        } else {\r
-            LoadString(hinst_kcreddb, IDS_IVL_M, fbuf, ARRAYLENGTH(fbuf));\r
-            StringCbPrintf(ibuf, sizeof(ibuf), fbuf, m);\r
-        }\r
-        if(s > 0) {\r
-            StringCbCat(ibuf, sizeof(ibuf), L" ");\r
-            t = ibuf + wcslen(ibuf);\r
-            if(s == 1)\r
-            {\r
-                LoadString(hinst_kcreddb, IDS_IVL_1S, t,\r
-                           (int) (ARRAYLENGTH(ibuf) - wcslen(ibuf)));\r
-            } else {\r
-                LoadString(hinst_kcreddb, IDS_IVL_S, fbuf,\r
-                           (int) ARRAYLENGTH(fbuf));\r
-                StringCbPrintf(t, sizeof(ibuf) - wcslen(ibuf)*sizeof(wchar_t), fbuf, s);\r
-            }\r
-        }\r
-    } else {\r
-        if(s == 1) {\r
-            LoadString(hinst_kcreddb, IDS_IVL_1S, ibuf, ARRAYLENGTH(ibuf));\r
-        } else {\r
-            LoadString(hinst_kcreddb, IDS_IVL_S, fbuf, sizeof(fbuf)/sizeof(wchar_t));\r
-            StringCbPrintf(ibuf, sizeof(ibuf), fbuf, s);\r
-        }\r
-    }\r
-\r
-    StringCbLength(ibuf, sizeof(ibuf), &cbsize);\r
-    cbsize += sizeof(wchar_t);\r
-\r
-    if(!buffer || *cb_buf < cbsize) {\r
-        *cb_buf = cbsize;\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-\r
-    StringCbCopy(buffer, *cb_buf, ibuf);\r
-    *cb_buf = cbsize;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-khm_int32 KHMAPI \r
-kcdb_type_interval_toString(const void * data, \r
-                            khm_size cbd, \r
-                            wchar_t * buffer, \r
-                            khm_size * cb_buf, \r
-                            khm_int32 flags)\r
-{\r
-    return FtIntervalToString((LPFILETIME) data, buffer, cb_buf);\r
-}\r
-\r
-khm_boolean KHMAPI kcdb_type_interval_isValid(\r
-    const void * d,\r
-    khm_size cbd)\r
-{\r
-    return (d && (cbd == sizeof(FILETIME) || cbd == KCDB_CBSIZE_AUTO));\r
-}\r
-\r
-khm_int32 KHMAPI kcdb_type_interval_comp(\r
-    const void * d1,\r
-    khm_size cbd1,\r
-    const void * d2,\r
-    khm_size cbd2)\r
-{\r
-    __int64 i1, i2;\r
-\r
-    i1 = FtToInt((FILETIME *) d1);\r
-    i2 = FtToInt((FILETIME *) d2);\r
-\r
-    if(i1 < i2)\r
-        return -1;\r
-    else if(i1 > i2)\r
-        return 1;\r
-    else\r
-        return 0;\r
-}\r
-\r
-khm_int32 KHMAPI kcdb_type_interval_dup(\r
-    const void * d_src,\r
-    khm_size cbd_src,\r
-    void * d_dst,\r
-    khm_size * cbd_dst)\r
-{\r
-    if(d_dst && *cbd_dst >= sizeof(FILETIME)) {\r
-        *cbd_dst = sizeof(FILETIME);\r
-        *((FILETIME *) d_dst) = *((FILETIME *) d_src);\r
-        return KHM_ERROR_SUCCESS;\r
-    } else {\r
-        *cbd_dst = sizeof(FILETIME);\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-}\r
-\r
-/* Int32 */\r
-\r
-khm_int32 KHMAPI kcdb_type_int32_toString(\r
-    const void * d, \r
-    khm_size cbd, \r
-    wchar_t * buffer, \r
-    khm_size * cb_buf, \r
-    khm_int32 flags)\r
-{\r
-    size_t cbsize;\r
-    wchar_t ibuf[12];\r
-\r
-    if(!cb_buf)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    StringCbPrintf(ibuf, sizeof(ibuf), L"%d", *((khm_int32 *) d));\r
-    StringCbLength(ibuf, sizeof(ibuf), &cbsize);\r
-    cbsize += sizeof(wchar_t);\r
-\r
-    if(!buffer || *cb_buf < cbsize) {\r
-        *cb_buf = cbsize;\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-\r
-    StringCbCopy((wchar_t *) buffer, *cb_buf, ibuf);\r
-    *cb_buf = cbsize;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-khm_boolean KHMAPI kcdb_type_int32_isValid(\r
-    const void * d,\r
-    khm_size cbd)\r
-{\r
-    return (d && (cbd == KCDB_CBSIZE_AUTO || cbd == sizeof(khm_int32)));\r
-}\r
-\r
-khm_int32 KHMAPI kcdb_type_int32_comp(\r
-    const void * d1,\r
-    khm_size cbd1,\r
-    const void * d2,\r
-    khm_size cbd2)\r
-{\r
-    return *((khm_int32 *) d1) - *((khm_int32 *) d2);\r
-}\r
-\r
-khm_int32 KHMAPI kcdb_type_int32_dup(\r
-    const void * d_src,\r
-    khm_size cbd_src,\r
-    void * d_dst,\r
-    khm_size * cbd_dst)\r
-{\r
-    if(d_dst && (*cbd_dst >= sizeof(khm_int32))) {\r
-        *cbd_dst = sizeof(khm_int32);\r
-        *((khm_int32 *) d_dst) = *((khm_int32 *) d_src);\r
-        return KHM_ERROR_SUCCESS;\r
-    } else {\r
-        *cbd_dst = sizeof(khm_int32);\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-}\r
-\r
-/* Int64 */\r
-\r
-khm_int32 KHMAPI kcdb_type_int64_toString(\r
-    const void * d, \r
-    khm_size cbd, \r
-    wchar_t * buffer, \r
-    khm_size * cb_buf, \r
-    khm_int32 flags)\r
-{\r
-    size_t cbsize;\r
-    wchar_t ibuf[22];\r
-\r
-    if(!cb_buf)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    StringCbPrintf(ibuf, sizeof(ibuf), L"%I64d", *((__int64 *) d));\r
-    StringCbLength(ibuf, sizeof(ibuf), &cbsize);\r
-    cbsize += sizeof(wchar_t);\r
-\r
-    if(!buffer || *cb_buf < cbsize) {\r
-        *cb_buf = cbsize;\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-\r
-    StringCbCopy((wchar_t *) buffer, *cb_buf, ibuf);\r
-    *cb_buf = cbsize;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-khm_boolean KHMAPI kcdb_type_int64_isValid(\r
-    const void * d,\r
-    khm_size cbd)\r
-{\r
-    return (d && (cbd == KCDB_CBSIZE_AUTO || cbd == sizeof(__int64)));\r
-}\r
-\r
-khm_int32 KHMAPI kcdb_type_int64_comp(\r
-    const void * d1,\r
-    khm_size cbd1,\r
-    const void * d2,\r
-    khm_size cbd2)\r
-{\r
-    __int64 r = *((__int64 *) d1) - *((__int64 *) d2);\r
-    return (r==0i64)?0:((r>0i64)?1:-1);\r
-}\r
-\r
-khm_int32 KHMAPI kcdb_type_int64_dup(\r
-    const void * d_src,\r
-    khm_size cbd_src,\r
-    void * d_dst,\r
-    khm_size * cbd_dst)\r
-{\r
-    if(d_dst && (*cbd_dst >= sizeof(__int64))) {\r
-        *cbd_dst = sizeof(__int64);\r
-        *((__int64 *) d_dst) = *((__int64 *) d_src);\r
-        return KHM_ERROR_SUCCESS;\r
-    } else {\r
-        *cbd_dst = sizeof(__int64);\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-}\r
-\r
-/* Data */\r
-#define GENERIC_DATA_STR L"(Data)"\r
-\r
-khm_int32 KHMAPI kcdb_type_data_toString(\r
-    const void * d, \r
-    khm_size cbd, \r
-    wchar_t * buffer, \r
-    khm_size * cb_buf, \r
-    khm_int32 flags)\r
-{\r
-    size_t cbsize;\r
-\r
-    if(!cb_buf)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cbsize = sizeof(GENERIC_DATA_STR);\r
-\r
-    if(!buffer || *cb_buf < cbsize) {\r
-        *cb_buf = cbsize;\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-\r
-    StringCbCopy(buffer, *cb_buf, GENERIC_DATA_STR);\r
-\r
-    *cb_buf = cbsize;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-khm_boolean KHMAPI kcdb_type_data_isValid(\r
-    const void * d,\r
-    khm_size cbd)\r
-{\r
-    /* data is always valid */\r
-    if (cbd != 0 && d == NULL)\r
-        return FALSE;\r
-    else\r
-        return TRUE;\r
-}\r
-\r
-khm_int32 KHMAPI kcdb_type_data_comp(\r
-    const void * d1,\r
-    khm_size cbd1,\r
-    const void * d2,\r
-    khm_size cbd2)\r
-{\r
-    khm_size pref;\r
-    khm_int32 rv = 0;\r
-\r
-    pref = min(cbd1, cbd2);\r
-\r
-    if (pref == 0)\r
-        return (cbd1 < cbd2)? -1 : ((cbd1 > cbd2)? 1 : 0);\r
-\r
-    rv = memcmp(d1, d2, pref);\r
-\r
-    if (rv == 0) {\r
-        return (cbd1 < cbd2)? -1 : ((cbd1 > cbd2)? 1 : 0);\r
-    } else {\r
-        return rv;\r
-    }\r
-}\r
-\r
-khm_int32 KHMAPI kcdb_type_data_dup(\r
-    const void * d_src,\r
-    khm_size cbd_src,\r
-    void * d_dst,\r
-    khm_size * cbd_dst)\r
-{\r
-    if(!cbd_dst || cbd_src == KCDB_CBSIZE_AUTO)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(!d_dst || *cbd_dst < cbd_src) {\r
-        *cbd_dst = cbd_src;\r
-        return KHM_ERROR_TOO_LONG;\r
-    } else {\r
-        *cbd_dst = cbd_src;\r
-        memcpy(d_dst, d_src, cbd_src);\r
-        return KHM_ERROR_SUCCESS;\r
-    }\r
-}\r
-\r
-\r
-void kcdb_type_msg_completion(kmq_message * m) \r
-{\r
-    kcdb_type_release((kcdb_type_i *) m->vparam);\r
-}\r
-\r
-void kcdb_type_post_message(khm_int32 op, kcdb_type_i * t)\r
-{\r
-    kcdb_type_hold(t);\r
-    kmq_post_message(KMSG_KCDB, KMSG_KCDB_TYPE, op, (void *) t);\r
-}\r
-\r
-void kcdb_type_init(void)\r
-{\r
-    kcdb_type type;\r
-\r
-    InitializeCriticalSection(&cs_type);\r
-    kcdb_type_namemap = hash_new_hashtable(\r
-        KCDB_TYPE_HASH_SIZE,\r
-        hash_string,\r
-        hash_string_comp,\r
-        kcdb_type_add_ref,\r
-        kcdb_type_del_ref);\r
-    kcdb_type_tbl = PMALLOC(sizeof(kcdb_type_i *) * (KCDB_TYPE_MAX_ID + 1));\r
-    ZeroMemory(kcdb_type_tbl, sizeof(kcdb_type_i *) * (KCDB_TYPE_MAX_ID + 1));\r
-    kcdb_types = NULL;\r
-\r
-    /*TODO: register standard data types */\r
-\r
-    ZeroMemory(&type, sizeof(type));\r
-    type.comp = kcdb_type_void_comp;\r
-    type.dup = kcdb_type_void_dup;\r
-    type.isValid = kcdb_type_void_isValid;\r
-    type.toString = kcdb_type_void_toString;\r
-    type.name = KCDB_TYPENAME_VOID;\r
-    type.id = KCDB_TYPE_VOID;\r
-\r
-    kcdb_type_register(&type, NULL);\r
-\r
-    ZeroMemory(&type, sizeof(type));\r
-    type.comp = kcdb_type_string_comp;\r
-    type.dup = kcdb_type_string_dup;\r
-    type.isValid = kcdb_type_string_isValid;\r
-    type.toString = kcdb_type_string_toString;\r
-    type.name = KCDB_TYPENAME_STRING;\r
-    type.id = KCDB_TYPE_STRING;\r
-    type.flags = KCDB_TYPE_FLAG_CB_AUTO;\r
-\r
-    kcdb_type_register(&type, NULL);\r
-\r
-    ZeroMemory(&type, sizeof(type));\r
-    type.comp = kcdb_type_date_comp;\r
-    type.dup = kcdb_type_date_dup;\r
-    type.isValid = kcdb_type_date_isValid;\r
-    type.toString = kcdb_type_date_toString;\r
-    type.name = KCDB_TYPENAME_DATE;\r
-    type.id = KCDB_TYPE_DATE;\r
-    type.cb_max = sizeof(FILETIME);\r
-    type.cb_min = sizeof(FILETIME);\r
-    type.flags = KCDB_TYPE_FLAG_CB_FIXED;\r
-\r
-    kcdb_type_register(&type, NULL);\r
-\r
-    ZeroMemory(&type, sizeof(type));\r
-    type.comp = kcdb_type_interval_comp;\r
-    type.dup = kcdb_type_interval_dup;\r
-    type.isValid = kcdb_type_interval_isValid;\r
-    type.toString = kcdb_type_interval_toString;\r
-    type.name = KCDB_TYPENAME_INTERVAL;\r
-    type.id = KCDB_TYPE_INTERVAL;\r
-    type.cb_max = sizeof(FILETIME);\r
-    type.cb_min = sizeof(FILETIME);\r
-    type.flags = KCDB_TYPE_FLAG_CB_FIXED;\r
-\r
-    kcdb_type_register(&type, NULL);\r
-\r
-    ZeroMemory(&type, sizeof(type));\r
-    type.comp = kcdb_type_int32_comp;\r
-    type.dup = kcdb_type_int32_dup;\r
-    type.isValid = kcdb_type_int32_isValid;\r
-    type.toString = kcdb_type_int32_toString;\r
-    type.name = KCDB_TYPENAME_INT32;\r
-    type.id = KCDB_TYPE_INT32;\r
-    type.cb_max = sizeof(khm_int32);\r
-    type.cb_min = sizeof(khm_int32);\r
-    type.flags = KCDB_TYPE_FLAG_CB_FIXED;\r
-\r
-    kcdb_type_register(&type, NULL);\r
-\r
-    ZeroMemory(&type, sizeof(type));\r
-    type.comp = kcdb_type_int64_comp;\r
-    type.dup = kcdb_type_int64_dup;\r
-    type.isValid = kcdb_type_int64_isValid;\r
-    type.toString = kcdb_type_int64_toString;\r
-    type.name = KCDB_TYPENAME_INT64;\r
-    type.id = KCDB_TYPE_INT64;\r
-    type.cb_max = sizeof(__int64);\r
-    type.cb_min = sizeof(__int64);\r
-    type.flags = KCDB_TYPE_FLAG_CB_FIXED;\r
-\r
-    kcdb_type_register(&type, NULL);\r
-\r
-    ZeroMemory(&type, sizeof(type));\r
-    type.comp = kcdb_type_data_comp;\r
-    type.dup = kcdb_type_data_dup;\r
-    type.isValid = kcdb_type_data_isValid;\r
-    type.toString = kcdb_type_data_toString;\r
-    type.name = KCDB_TYPENAME_DATA;\r
-    type.id = KCDB_TYPE_DATA;\r
-\r
-    kcdb_type_register(&type, NULL);\r
-}\r
-\r
-void kcdb_type_add_ref(const void *key, void *vt)\r
-{\r
-    kcdb_type_hold((kcdb_type_i *) vt);\r
-}\r
-\r
-void kcdb_type_del_ref(const void *key, void *vt)\r
-{\r
-    kcdb_type_release((kcdb_type_i *) vt);\r
-}\r
-\r
-khm_int32 kcdb_type_hold(kcdb_type_i * t)\r
-{\r
-    if(!t)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_type);\r
-    t->refcount++;\r
-    LeaveCriticalSection(&cs_type);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-khm_int32 kcdb_type_release(kcdb_type_i * t)\r
-{\r
-    if(!t)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_type);\r
-    t->refcount--;\r
-    kcdb_type_check_and_delete(t->type.id);\r
-    LeaveCriticalSection(&cs_type);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-void kcdb_type_exit(void)\r
-{\r
-    EnterCriticalSection(&cs_type);\r
-    PFREE(kcdb_type_tbl);\r
-    /*TODO: free up the individual types */\r
-    LeaveCriticalSection(&cs_type);\r
-    DeleteCriticalSection(&cs_type);\r
-}\r
-\r
-void kcdb_type_check_and_delete(khm_int32 id)\r
-{\r
-    kcdb_type_i * t;\r
-\r
-    if(id < 0 || id > KCDB_TYPE_MAX_ID)\r
-        return;\r
-\r
-    EnterCriticalSection(&cs_type);\r
-    t = kcdb_type_tbl[id];\r
-    if(t && !t->refcount) {\r
-        kcdb_type_tbl[id] = NULL;\r
-        LDELETE(&kcdb_types, t);\r
-        /* must already be out of the hash-table, otherwise refcount should not\r
-            be zero */\r
-        PFREE(t->type.name);\r
-        PFREE(t);\r
-    }\r
-    LeaveCriticalSection(&cs_type);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_type_get_id(const wchar_t *name, khm_int32 * id)\r
-{\r
-    kcdb_type_i * t;\r
-    size_t cbsize;\r
-\r
-    if(FAILED(StringCbLength(name, KCDB_MAXCB_NAME, &cbsize))) {\r
-        /* also fails of name is NULL */\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    EnterCriticalSection(&cs_type);\r
-    t = hash_lookup(kcdb_type_namemap, (void*) name);\r
-    LeaveCriticalSection(&cs_type);\r
-\r
-    if(!t) {\r
-        *id = KCDB_TYPE_INVALID;\r
-        return KHM_ERROR_NOT_FOUND;\r
-    } else {\r
-        *id = t->type.id;\r
-        return KHM_ERROR_SUCCESS;\r
-    }\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_type_get_info(khm_int32 id, kcdb_type ** info)\r
-{\r
-    kcdb_type_i * t;\r
-\r
-    if(id < 0 || id > KCDB_TYPE_MAX_ID)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_type);\r
-    t = kcdb_type_tbl[id];\r
-\r
-    if (t)\r
-        kcdb_type_hold(t);\r
-    LeaveCriticalSection(&cs_type);\r
-\r
-    if(info)\r
-        *info = (kcdb_type *) t;\r
-    else if (t)\r
-        kcdb_type_release(t);\r
-\r
-    return (t)? KHM_ERROR_SUCCESS : KHM_ERROR_NOT_FOUND;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_type_release_info(kcdb_type * info)\r
-{\r
-    return kcdb_type_release((kcdb_type_i *) info);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_type_get_name(khm_int32 id, wchar_t * buffer, khm_size * cbbuf)\r
-{\r
-    size_t cbsize;\r
-    kcdb_type_i * t;\r
-\r
-    if(id < 0 || id > KCDB_TYPE_MAX_ID || !cbbuf)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    t = kcdb_type_tbl[id];\r
-\r
-    if(!t)\r
-        return KHM_ERROR_NOT_FOUND;\r
-\r
-    if(FAILED(StringCbLength(t->type.name, KCDB_MAXCB_NAME, &cbsize)))\r
-        return KHM_ERROR_UNKNOWN;\r
-\r
-    cbsize += sizeof(wchar_t);\r
-\r
-    if(!buffer || *cbbuf < cbsize) {\r
-        *cbbuf = cbsize;\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-\r
-    StringCbCopy(buffer, *cbbuf, t->type.name);\r
-    *cbbuf = cbsize;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_type_register(const kcdb_type * type, khm_int32 * new_id)\r
-{\r
-    kcdb_type_i *t;\r
-    size_t cbsize;\r
-    khm_int32 type_id;\r
-\r
-    if(!type || \r
-        !type->comp || \r
-        !type->dup || \r
-        !type->isValid || \r
-        !type->toString || \r
-        !type->name)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if((type->flags & KCDB_TYPE_FLAG_CB_MIN) &&\r
-        (type->cb_min < 0 || type->cb_min > KCDB_TYPE_MAXCB))\r
-    {\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    if((type->flags & KCDB_TYPE_FLAG_CB_MAX) &&\r
-        (type->cb_max < 0 || type->cb_max > KCDB_TYPE_MAXCB))\r
-    {\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    if((type->flags & KCDB_TYPE_FLAG_CB_MIN) &&\r
-        (type->flags & KCDB_TYPE_FLAG_CB_MAX) &&\r
-        (type->cb_max < type->cb_min))\r
-    {\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    if(FAILED(StringCbLength(type->name, KCDB_MAXCB_NAME, &cbsize)))\r
-        return KHM_ERROR_TOO_LONG;\r
-\r
-    cbsize += sizeof(wchar_t);\r
-\r
-    EnterCriticalSection(&cs_type);\r
-    if(type->id == KCDB_TYPE_INVALID) {\r
-        kcdb_type_get_next_free(&type_id);\r
-    } else if(type->id < 0 || type->id > KCDB_TYPE_MAX_ID) {\r
-        LeaveCriticalSection(&cs_type);\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    } else if(kcdb_type_tbl[type->id]) {\r
-        LeaveCriticalSection(&cs_type);\r
-        return KHM_ERROR_DUPLICATE;\r
-    } else {\r
-        type_id = type->id;\r
-    }\r
-\r
-    if(type_id == KCDB_TYPE_INVALID) {\r
-        LeaveCriticalSection(&cs_type);\r
-        return KHM_ERROR_NO_RESOURCES;\r
-    }\r
-\r
-    t = PMALLOC(sizeof(kcdb_type_i));\r
-    ZeroMemory(t, sizeof(kcdb_type_i));\r
-\r
-    t->type.name = PMALLOC(cbsize);\r
-    StringCbCopy(t->type.name, cbsize, type->name);\r
-\r
-    t->type.comp = type->comp;\r
-    t->type.dup = type->dup;\r
-    t->type.flags = type->flags;\r
-    t->type.id = type_id;\r
-    t->type.isValid = type->isValid;\r
-    t->type.toString = type->toString;\r
-\r
-    LINIT(t);\r
-\r
-    kcdb_type_tbl[type_id] = t;\r
-    LPUSH(&kcdb_types, t);\r
-\r
-    hash_add(kcdb_type_namemap, (void *) t->type.name, (void *) t);\r
-\r
-    LeaveCriticalSection(&cs_type);\r
-\r
-    if(new_id)\r
-        *new_id = type_id;\r
-\r
-    kcdb_type_post_message(KCDB_OP_INSERT, t);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_type_unregister(khm_int32 id)\r
-{\r
-    kcdb_type_i * t;\r
-\r
-    if(id < 0 || id > KCDB_TYPE_MAX_ID)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_type);\r
-    t = kcdb_type_tbl[id];\r
-    if(t) {\r
-        kcdb_type_post_message(KCDB_OP_DELETE, t);\r
-        /* we are going to remove t from the hash table.  If no one is holding\r
-            a reference to it, then we can free it (actually, the del_ref code\r
-            will take care of that anyway).  If there is a hold, then it will\r
-            get freed when they release it. \r
-            \r
-            Actually, the post_message call above pretty much guarantees that\r
-            the type has a hold on it.*/\r
-        t->type.flags |= KCDB_TYPE_FLAG_DELETED;\r
-        hash_del(kcdb_type_namemap, t->type.name);\r
-    }\r
-    LeaveCriticalSection(&cs_type);\r
-\r
-    if(t)\r
-        return KHM_ERROR_SUCCESS;\r
-    else\r
-        return KHM_ERROR_NOT_FOUND;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kcdb_type_get_next_free(khm_int32 * id)\r
-{\r
-    int i;\r
-\r
-    if(!id)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    /* do a linear search because this function only gets called a few times */\r
-    EnterCriticalSection(&cs_type);\r
-    for(i=0; i <= KCDB_TYPE_MAX_ID; i++) {\r
-        if(!kcdb_type_tbl[i])\r
-            break;\r
-    }\r
-    LeaveCriticalSection(&cs_type);\r
-\r
-    if(i <= KCDB_TYPE_MAX_ID) {\r
-        *id = i;\r
-        return KHM_ERROR_SUCCESS;\r
-    } else {\r
-        *id = KCDB_TYPE_INVALID;\r
-        return KHM_ERROR_NO_RESOURCES;\r
-    }\r
-}\r
-\r
-/* Conversion functions */\r
-\r
-KHMEXP void KHMAPI TimetToFileTime( time_t t, LPFILETIME pft )\r
-{\r
-    LONGLONG ll;\r
-\r
-    if ( sizeof(time_t) == 4 )\r
-       ll = Int32x32To64(t, 10000000) + 116444736000000000i64;\r
-    else {\r
-       ll = t * 10000000i64 + 116444736000000000i64;\r
-    }\r
-    pft->dwLowDateTime = (DWORD) ll;\r
-    pft->dwHighDateTime = (DWORD) (ll >> 32);\r
-}\r
-\r
-KHMEXP void KHMAPI TimetToFileTimeInterval(time_t t, LPFILETIME pft)\r
-{\r
-    LONGLONG ll;\r
-    \r
-    if ( sizeof(time_t) == 4 )\r
-       ll = Int32x32To64(t, 10000000);\r
-    else {\r
-       ll = t * 10000000i64;\r
-    }\r
-    pft->dwLowDateTime = (DWORD) ll;\r
-    pft->dwHighDateTime = (DWORD) (ll >> 32);\r
-}\r
-\r
-KHMEXP long KHMAPI FtIntervalToSeconds(LPFILETIME pft)\r
-{\r
-    __int64 i = FtToInt(pft);\r
-    return (long) (i / 10000000i64);\r
-}\r
-\r
-KHMEXP long KHMAPI FtIntervalToMilliseconds(LPFILETIME pft)\r
-{\r
-    __int64 i = FtToInt(pft);\r
-    return (long) (i / 10000i64);\r
-}\r
-\r
-KHMEXP khm_int64 KHMAPI FtToInt(LPFILETIME pft) {\r
-    LARGE_INTEGER ll;\r
-    ll.LowPart = pft->dwLowDateTime;\r
-    ll.HighPart = pft->dwHighDateTime;\r
-    return ll.QuadPart;\r
-}\r
-\r
-KHMEXP FILETIME KHMAPI IntToFt(khm_int64 i) {\r
-    LARGE_INTEGER ll;\r
-    FILETIME ft;\r
-\r
-    ll.QuadPart = i;\r
-    ft.dwLowDateTime = ll.LowPart;\r
-    ft.dwHighDateTime = ll.HighPart;\r
-\r
-    return ft;\r
-}\r
-\r
-KHMEXP FILETIME KHMAPI FtSub(LPFILETIME ft1, LPFILETIME ft2) {\r
-    FILETIME d;\r
-    LARGE_INTEGER l1, l2;\r
-\r
-    l1.LowPart = ft1->dwLowDateTime;\r
-    l1.HighPart = ft1->dwHighDateTime;\r
-    l2.LowPart = ft2->dwLowDateTime;\r
-    l2.HighPart = ft2->dwHighDateTime;\r
-\r
-    l1.QuadPart -= l2.QuadPart;\r
-\r
-    d.dwLowDateTime = l1.LowPart;\r
-    d.dwHighDateTime = l1.HighPart;\r
-\r
-    return d;\r
-}\r
-\r
-KHMEXP FILETIME KHMAPI FtAdd(LPFILETIME ft1, LPFILETIME ft2) {\r
-    FILETIME d;\r
-    LARGE_INTEGER l1, l2;\r
-\r
-    l1.LowPart = ft1->dwLowDateTime;\r
-    l1.HighPart = ft1->dwHighDateTime;\r
-    l2.LowPart = ft2->dwLowDateTime;\r
-    l2.HighPart = ft2->dwHighDateTime;\r
-\r
-    l1.QuadPart += l2.QuadPart;\r
-\r
-    d.dwLowDateTime = l1.LowPart;\r
-    d.dwHighDateTime = l1.HighPart;\r
-\r
-    return d;\r
-}\r
-\r
-KHMEXP int KHMAPI AnsiStrToUnicode( wchar_t * wstr, size_t cbwstr, const char * astr)\r
-{\r
-    size_t nc;\r
-\r
-    if(cbwstr == 0)\r
-        return 0;\r
-\r
-    nc = strlen(astr);\r
-    if(nc == MultiByteToWideChar(\r
-        CP_ACP, \r
-        0, \r
-        astr, \r
-        (int) nc, \r
-        wstr, \r
-        (int)(cbwstr / sizeof(wchar_t) - 1))) {\r
-        wstr[nc] = L'\0';\r
-    } else {\r
-        wstr[0] = L'\0';\r
-        nc = 0;\r
-    }\r
-\r
-    return (int) nc;\r
-}\r
-\r
-KHMEXP int KHMAPI UnicodeStrToAnsi( char * dest, size_t cbdest, const wchar_t * src)\r
-{\r
-    size_t nc;\r
-\r
-    if(cbdest == 0)\r
-        return 0;\r
-\r
-    dest[0] = 0;\r
-\r
-    if(FAILED(StringCchLength(src, cbdest, &nc)) || nc*sizeof(char) >= cbdest)\r
-        // note that cbdest counts the terminating NULL, while nc doesn't\r
-        return 0;\r
-\r
-    nc = WideCharToMultiByte(\r
-        CP_ACP, \r
-        WC_NO_BEST_FIT_CHARS, \r
-        src, \r
-        (int) nc, \r
-        dest, \r
-        (int) cbdest, \r
-        NULL, \r
-        NULL);\r
-\r
-    dest[nc] = 0;\r
-\r
-    return (int) nc;\r
-}\r
-\r
-#define MAX_IVL_SPECLIST_LEN 256\r
-#define MAX_IVL_UNITS 5\r
-\r
-enum _ivl_indices {\r
-    IVL_SECONDS = 0,\r
-    IVL_MINUTES,\r
-    IVL_HOURS,\r
-    IVL_DAYS,\r
-    IVL_WEEKS\r
-};\r
-\r
-typedef struct ivspec_t {\r
-    wchar_t str[MAX_IVL_SPECLIST_LEN];\r
-    __int64 mul;\r
-} ivspec;\r
-\r
-static ivspec ivspecs[MAX_IVL_UNITS];\r
-static BOOL ivspecs_loaded = FALSE;\r
-\r
-int _iv_is_in_spec(wchar_t *s, int n, wchar_t * spec)\r
-{\r
-    /* spec strigns are comma separated */\r
-    wchar_t *b, *e;\r
-\r
-    b = spec;\r
-    while(*b) {\r
-        e = wcschr(b, L',');\r
-        if(!e)\r
-            e = b + wcslen(b);\r
-    \r
-        if((e - b) == n  && !_wcsnicmp(b, s, n)) {\r
-            return TRUE;\r
-        }\r
-\r
-        if(*e)\r
-            b = e+1;\r
-        else\r
-            break;\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI IntervalStringToFt(FILETIME * pft, wchar_t * str)\r
-{\r
-    size_t cb;\r
-    wchar_t * b;\r
-    __int64 t;\r
-\r
-    *pft = IntToFt(0);\r
-\r
-    /* ideally we should synchronize this, but it doesn't hurt if two\r
-       threads do this at the same time, because we only set the ivspecs_loaded\r
-       flag when we are done */\r
-    if(!ivspecs_loaded) {\r
-        LoadString(hinst_kcreddb, IDS_IVL_S_SPEC, ivspecs[IVL_SECONDS].str, MAX_IVL_SPECLIST_LEN);\r
-        ivspecs[IVL_SECONDS].mul = 10000000i64;\r
-        LoadString(hinst_kcreddb, IDS_IVL_M_SPEC, ivspecs[IVL_MINUTES].str, MAX_IVL_SPECLIST_LEN);\r
-        ivspecs[IVL_MINUTES].mul = ivspecs[IVL_SECONDS].mul * 60;\r
-        LoadString(hinst_kcreddb, IDS_IVL_H_SPEC, ivspecs[2].str, MAX_IVL_SPECLIST_LEN);\r
-        ivspecs[IVL_HOURS].mul = ivspecs[IVL_MINUTES].mul * 60;\r
-        LoadString(hinst_kcreddb, IDS_IVL_D_SPEC, ivspecs[3].str, MAX_IVL_SPECLIST_LEN);\r
-        ivspecs[IVL_DAYS].mul = ivspecs[IVL_HOURS].mul * 24;\r
-        LoadString(hinst_kcreddb, IDS_IVL_W_SPEC, ivspecs[4].str, MAX_IVL_SPECLIST_LEN);\r
-        ivspecs[IVL_WEEKS].mul = ivspecs[IVL_DAYS].mul * 7;\r
-\r
-        ivspecs_loaded = TRUE;\r
-    }\r
-\r
-    if(!str || FAILED(StringCbLength(str, MAX_IVL_SPECLIST_LEN, &cb)))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    b = str;\r
-    t = 0;\r
-    while(*b) {\r
-        __int64 f = 1;\r
-        wchar_t *e;\r
-        int i;\r
-\r
-        while(*b && iswspace(*b))\r
-            b++;\r
-\r
-        if(*b && iswdigit(*b)) {\r
-            f = _wtoi64(b);\r
-\r
-            while(*b && iswdigit(*b))\r
-                b++;\r
-        }\r
-\r
-        while(*b && iswspace(*b))\r
-            b++;\r
-\r
-        if(!*b) /* no unit specified */\r
-            return KHM_ERROR_INVALID_PARAM;\r
-\r
-        e = b;\r
-\r
-        while(*e && !iswspace(*e))\r
-            e++;\r
-\r
-        for(i=0; i < MAX_IVL_UNITS; i++) {\r
-            if(_iv_is_in_spec(b, (int)(e-b), ivspecs[i].str))\r
-                break;\r
-        }\r
-\r
-        if(i==MAX_IVL_UNITS)\r
-            return KHM_ERROR_INVALID_PARAM;\r
-\r
-        t += f * ivspecs[i].mul;\r
-\r
-        b = e;\r
-    }\r
-\r
-    *pft = IntToFt(t);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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_PARAM;
+
+    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_PARAM;
+
+    *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_PARAM;
+
+    sd = (wchar_t *) d;
+
+    if(FAILED(StringCbLength(sd, KCDB_TYPE_MAXCB, &cbsize)))
+        return KHM_ERROR_INVALID_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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 += GetTimeFormat(
+        LOCALE_USER_DEFAULT,
+        0,
+        &st_dl,
+        NULL,
+        NULL,
+        0) * 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;
+    __int64 ift;
+    long l;
+
+    ift = FtToInt(pft);
+    ms = ift / 10000i64;
+    
+    if(ms < 0 || ift == _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 if (m > 5) {
+        /* rep change at next minute change */
+        l = (long) (ms % (60*1000i64));
+    } else {
+        /* rep change at next second change */
+        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;
+    __int64 ift;
+    wchar_t ibuf[256];
+    wchar_t fbuf[256];
+    wchar_t * t;
+
+    if(!cb_buf)
+        return KHM_ERROR_INVALID_PARAM;
+
+    ift = FtToInt(data);
+    s = ift / 10000000i64;
+
+    m = s / 60;
+    h = s / 3600;
+    d = s / (3600*24);
+
+    if(ift == _I64_MAX) {
+#ifdef INDICATE_UNKNOWN_EXPIRY_TIMES
+        LoadString(hinst_kcreddb, IDS_IVL_UNKNOWN, ibuf, sizeof(ibuf)/sizeof(wchar_t));
+#else
+        StringCbCopy(ibuf, sizeof(ibuf), L"");
+#endif
+    } 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,
+                           (int) (ARRAYLENGTH(ibuf) - wcslen(ibuf)));
+            } else {
+                LoadString(hinst_kcreddb, IDS_IVL_H, fbuf,
+                           (int) ARRAYLENGTH(fbuf));
+                StringCbPrintf(t, sizeof(ibuf) - wcslen(ibuf)*sizeof(wchar_t), fbuf, h);
+            }
+        }
+    } else if(h > 0 || m > 5) {
+        m = (s - (h * 3600)) / 60;
+        if(h == 1) {
+            LoadString(hinst_kcreddb, IDS_IVL_1H, ibuf, ARRAYLENGTH(ibuf));
+        } else if (h > 1) {
+            LoadString(hinst_kcreddb, IDS_IVL_H, fbuf, ARRAYLENGTH(fbuf));
+            StringCbPrintf(ibuf, sizeof(ibuf), fbuf, h);
+        } else {
+            *ibuf = L'\0';
+        }
+
+        if(m > 0 || h == 0) {
+            if (h >= 1)
+                StringCbCat(ibuf, sizeof(ibuf), L" ");
+
+            t = ibuf + wcslen(ibuf);
+            if(m == 1)
+            {
+                LoadString(hinst_kcreddb, IDS_IVL_1M, t,
+                           (int) (ARRAYLENGTH(ibuf) - wcslen(ibuf)));
+            } else {
+                LoadString(hinst_kcreddb, IDS_IVL_M, fbuf,
+                           (int) 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,
+                           (int) (ARRAYLENGTH(ibuf) - wcslen(ibuf)));
+            } else {
+                LoadString(hinst_kcreddb, IDS_IVL_S, fbuf,
+                           (int) 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 = FtToInt((FILETIME *) d1);
+    i2 = FtToInt((FILETIME *) 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(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;
+    }
+}
+
+/* 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_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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 */
+    if (cbd != 0 && d == NULL)
+        return FALSE;
+    else
+        return TRUE;
+}
+
+khm_int32 KHMAPI kcdb_type_data_comp(
+    const void * d1,
+    khm_size cbd1,
+    const void * d2,
+    khm_size cbd2)
+{
+    khm_size pref;
+    khm_int32 rv = 0;
+
+    pref = min(cbd1, cbd2);
+
+    if (pref == 0)
+        return (cbd1 < cbd2)? -1 : ((cbd1 > cbd2)? 1 : 0);
+
+    rv = memcmp(d1, d2, pref);
+
+    if (rv == 0) {
+        return (cbd1 < cbd2)? -1 : ((cbd1 > cbd2)? 1 : 0);
+    } else {
+        return rv;
+    }
+}
+
+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 || cbd_src == KCDB_CBSIZE_AUTO)
+        return KHM_ERROR_INVALID_PARAM;
+
+    if(!d_dst || *cbd_dst < cbd_src) {
+        *cbd_dst = cbd_src;
+        return KHM_ERROR_TOO_LONG;
+    } else {
+        *cbd_dst = cbd_src;
+        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 = PMALLOC(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(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_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_PARAM;
+
+    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_PARAM;
+
+    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);
+    PFREE(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 */
+        PFREE(t->type.name);
+        PFREE(t);
+    }
+    LeaveCriticalSection(&cs_type);
+}
+
+KHMEXP khm_int32 KHMAPI kcdb_type_get_id(const 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_PARAM;
+    }
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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(const 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_PARAM;
+
+    if((type->flags & KCDB_TYPE_FLAG_CB_MIN) &&
+        (type->cb_min < 0 || type->cb_min > KCDB_TYPE_MAXCB))
+    {
+        return KHM_ERROR_INVALID_PARAM;
+    }
+
+    if((type->flags & KCDB_TYPE_FLAG_CB_MAX) &&
+        (type->cb_max < 0 || type->cb_max > KCDB_TYPE_MAXCB))
+    {
+        return KHM_ERROR_INVALID_PARAM;
+    }
+
+    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_PARAM;
+    }
+
+    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_PARAM;
+    } 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 = PMALLOC(sizeof(kcdb_type_i));
+    ZeroMemory(t, sizeof(kcdb_type_i));
+
+    t->type.name = PMALLOC(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_PARAM;
+
+    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_PARAM;
+
+    /* 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;
+
+    if ( sizeof(time_t) == 4 )
+       ll = Int32x32To64(t, 10000000) + 116444736000000000i64;
+    else {
+       ll = t * 10000000i64 + 116444736000000000i64;
+    }
+    pft->dwLowDateTime = (DWORD) ll;
+    pft->dwHighDateTime = (DWORD) (ll >> 32);
+}
+
+KHMEXP void KHMAPI TimetToFileTimeInterval(time_t t, LPFILETIME pft)
+{
+    LONGLONG ll;
+    
+    if ( sizeof(time_t) == 4 )
+       ll = Int32x32To64(t, 10000000);
+    else {
+       ll = t * 10000000i64;
+    }
+    pft->dwLowDateTime = (DWORD) ll;
+    pft->dwHighDateTime = (DWORD) (ll >> 32);
+}
+
+KHMEXP long KHMAPI FtIntervalToSeconds(LPFILETIME pft)
+{
+    __int64 i = FtToInt(pft);
+    return (long) (i / 10000000i64);
+}
+
+KHMEXP long KHMAPI FtIntervalToMilliseconds(LPFILETIME pft)
+{
+    __int64 i = FtToInt(pft);
+    return (long) (i / 10000i64);
+}
+
+KHMEXP khm_int64 KHMAPI FtToInt(LPFILETIME pft) {
+    LARGE_INTEGER ll;
+    ll.LowPart = pft->dwLowDateTime;
+    ll.HighPart = pft->dwHighDateTime;
+    return ll.QuadPart;
+}
+
+KHMEXP FILETIME KHMAPI IntToFt(khm_int64 i) {
+    LARGE_INTEGER ll;
+    FILETIME ft;
+
+    ll.QuadPart = i;
+    ft.dwLowDateTime = ll.LowPart;
+    ft.dwHighDateTime = ll.HighPart;
+
+    return ft;
+}
+
+KHMEXP FILETIME KHMAPI FtSub(LPFILETIME ft1, LPFILETIME ft2) {
+    FILETIME d;
+    LARGE_INTEGER l1, l2;
+
+    l1.LowPart = ft1->dwLowDateTime;
+    l1.HighPart = ft1->dwHighDateTime;
+    l2.LowPart = ft2->dwLowDateTime;
+    l2.HighPart = ft2->dwHighDateTime;
+
+    l1.QuadPart -= l2.QuadPart;
+
+    d.dwLowDateTime = l1.LowPart;
+    d.dwHighDateTime = l1.HighPart;
+
+    return d;
+}
+
+KHMEXP FILETIME KHMAPI FtAdd(LPFILETIME ft1, LPFILETIME ft2) {
+    FILETIME d;
+    LARGE_INTEGER l1, l2;
+
+    l1.LowPart = ft1->dwLowDateTime;
+    l1.HighPart = ft1->dwHighDateTime;
+    l2.LowPart = ft2->dwLowDateTime;
+    l2.HighPart = ft2->dwHighDateTime;
+
+    l1.QuadPart += l2.QuadPart;
+
+    d.dwLowDateTime = l1.LowPart;
+    d.dwHighDateTime = l1.HighPart;
+
+    return d;
+}
+
+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 t;
+
+    *pft = IntToFt(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_PARAM;
+
+    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_PARAM;
+
+        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_PARAM;
+
+        t += f * ivspecs[i].mul;
+
+        b = e;
+    }
+
+    *pft = IntToFt(t);
+
+    return KHM_ERROR_SUCCESS;
+}
index f7ef26ac446493c5eb49095075fb49fd15e51306..698e5f3863dcebd4c3d028527c89fb06976356c5 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KCDB_TYPE_H\r
-#define __KHIMAIRA_KCDB_TYPE_H\r
-\r
-/* Types */\r
-\r
-typedef struct kcdb_type_i_t {\r
-    kcdb_type type;\r
-\r
-    khm_int32 refcount;\r
-\r
-    struct kcdb_type_i_t * next;\r
-    struct kcdb_type_i_t * prev;\r
-} kcdb_type_i;\r
-\r
-#define KCDB_TYPE_HASH_SIZE 31\r
-\r
-#define KCDB_TYPE_FLAG_DELETED 8\r
-\r
-void kcdb_type_init(void);\r
-void kcdb_type_exit(void);\r
-void kcdb_type_add_ref(const void *key, void *vt);\r
-void kcdb_type_del_ref(const void *key, void *vt);\r
-void kcdb_type_msg_completion(kmq_message * m);\r
-khm_int32 kcdb_type_hold(kcdb_type_i * t);\r
-khm_int32 kcdb_type_release(kcdb_type_i * t);\r
-void kcdb_type_check_and_delete(khm_int32 id);\r
-void kcdb_type_post_message(khm_int32 op, kcdb_type_i * t);\r
-\r
-khm_int32 KHMAPI kcdb_type_void_toString(\r
-    const void * d, \r
-    khm_size cbd, \r
-    wchar_t * buffer, \r
-    khm_size * cb_buf, \r
-    khm_int32 flags);\r
-\r
-khm_boolean KHMAPI kcdb_type_void_isValid(\r
-    const void * d,\r
-    khm_size cbd);\r
-\r
-khm_int32 KHMAPI kcdb_type_void_comp(\r
-    const void * d1,\r
-    khm_size cbd1,\r
-    const void * d2,\r
-    khm_size cbd2);\r
-\r
-khm_int32 KHMAPI kcdb_type_void_dup(\r
-    const void * d_src,\r
-    khm_size cbd_src,\r
-    void * d_dst,\r
-    khm_size * cbd_dst);\r
-\r
-khm_int32 KHMAPI kcdb_type_string_toString(\r
-    const void * d, \r
-    khm_size cbd, \r
-    wchar_t * buffer, \r
-    khm_size * cb_buf, \r
-    khm_int32 flags);\r
-\r
-khm_boolean KHMAPI kcdb_type_string_isValid(\r
-    const void * d,\r
-    khm_size cbd);\r
-\r
-khm_int32 KHMAPI kcdb_type_string_comp(\r
-    const void * d1,\r
-    khm_size cbd1,\r
-    const void * d2,\r
-    khm_size cbd2);\r
-\r
-khm_int32 KHMAPI kcdb_type_string_dup(\r
-    const void * d_src,\r
-    khm_size cbd_src,\r
-    void * d_dst,\r
-    khm_size * cbd_dst);\r
-\r
-khm_int32 KHMAPI kcdb_type_date_toString(\r
-    const void * d, \r
-    khm_size cbd, \r
-    wchar_t * buffer, \r
-    khm_size * cb_buf, \r
-    khm_int32 flags);\r
-\r
-khm_boolean KHMAPI kcdb_type_date_isValid(\r
-    const void * d,\r
-    khm_size cbd);\r
-\r
-khm_int32 KHMAPI kcdb_type_date_comp(\r
-    const void * d1,\r
-    khm_size cbd1,\r
-    const void * d2,\r
-    khm_size cbd2);\r
-\r
-khm_int32 KHMAPI kcdb_type_date_dup(\r
-    const void * d_src,\r
-    khm_size cbd_src,\r
-    void * d_dst,\r
-    khm_size * cbd_dst);\r
-\r
-khm_int32 KHMAPI kcdb_type_interval_toString(\r
-    const void * d, \r
-    khm_size cbd, \r
-    wchar_t * buffer, \r
-    khm_size * cb_buf, \r
-    khm_int32 flags);\r
-\r
-khm_boolean KHMAPI kcdb_type_interval_isValid(\r
-    const void * d,\r
-    khm_size cbd);\r
-\r
-khm_int32 KHMAPI kcdb_type_interval_comp(\r
-    const void * d1,\r
-    khm_size cbd1,\r
-    const void * d2,\r
-    khm_size cbd2);\r
-\r
-khm_int32 KHMAPI kcdb_type_interval_dup(\r
-    const void * d_src,\r
-    khm_size cbd_src,\r
-    void * d_dst,\r
-    khm_size * cbd_dst);\r
-\r
-khm_int32 KHMAPI kcdb_type_int32_toString(\r
-    const void * d, \r
-    khm_size cbd, \r
-    wchar_t * buffer, \r
-    khm_size * cb_buf, \r
-    khm_int32 flags);\r
-\r
-khm_boolean KHMAPI kcdb_type_int32_isValid(\r
-    const void * d,\r
-    khm_size cbd);\r
-\r
-khm_int32 KHMAPI kcdb_type_int32_comp(\r
-    const void * d1,\r
-    khm_size cbd1,\r
-    const void * d2,\r
-    khm_size cbd2);\r
-\r
-khm_int32 KHMAPI kcdb_type_int32_dup(\r
-    const void * d_src,\r
-    khm_size cbd_src,\r
-    void * d_dst,\r
-    khm_size * cbd_dst);\r
-\r
-khm_int32 KHMAPI kcdb_type_int64_toString(\r
-    const void * d, \r
-    khm_size cbd, \r
-    wchar_t * buffer, \r
-    khm_size * cb_buf, \r
-    khm_int32 flags);\r
-\r
-khm_boolean KHMAPI kcdb_type_int64_isValid(\r
-    const void * d,\r
-    khm_size cbd);\r
-\r
-khm_int32 KHMAPI kcdb_type_int64_comp(\r
-    const void * d1,\r
-    khm_size cbd1,\r
-    const void * d2,\r
-    khm_size cbd2);\r
-\r
-khm_int32 KHMAPI kcdb_type_int64_dup(\r
-    const void * d_src,\r
-    khm_size cbd_src,\r
-    void * d_dst,\r
-    khm_size * cbd_dst);\r
-\r
-khm_int32 KHMAPI kcdb_type_data_toString(\r
-    const void * d, \r
-    khm_size cbd, \r
-    wchar_t * buffer, \r
-    khm_size * cb_buf, \r
-    khm_int32 flags);\r
-\r
-khm_boolean KHMAPI kcdb_type_data_isValid(\r
-    const void * d,\r
-    khm_size cbd);\r
-\r
-khm_int32 KHMAPI kcdb_type_data_comp(\r
-    const void * d1,\r
-    khm_size cbd1,\r
-    const void * d2,\r
-    khm_size cbd2);\r
-\r
-khm_int32 KHMAPI kcdb_type_data_dup(\r
-    const void * d_src,\r
-    khm_size cbd_src,\r
-    void * d_dst,\r
-    khm_size * cbd_dst);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
index feecbe06cef623f584ae7d1f0e7003fcf2d0d65a..d9fdf2d49b9ef3ce71176fc3a26e78bae03d3ac5 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kherrinternal.h>\r
-#include<assert.h>\r
-#include<stdarg.h>\r
-\r
-CRITICAL_SECTION cs_error;\r
-DWORD tls_error = 0;\r
-kherr_context * ctx_free_list = NULL;\r
-kherr_context * ctx_root_list = NULL;\r
-kherr_context * ctx_error_list = NULL;\r
-kherr_event * evt_free_list = NULL;\r
-\r
-kherr_handler_node * ctx_handlers = NULL;\r
-khm_size n_ctx_handlers;\r
-khm_size nc_ctx_handlers;\r
-\r
-kherr_serial ctx_serial = 0;\r
-\r
-#ifdef DEBUG\r
-#define DEBUG_CONTEXT\r
-\r
-KHMEXP void kherr_debug_printf(wchar_t * fmt, ...) {\r
-    va_list vl;\r
-    wchar_t buf[1024];\r
-\r
-    va_start(vl, fmt);\r
-    StringCbVPrintf(buf, sizeof(buf), fmt, vl);\r
-    OutputDebugString(buf);\r
-    va_end(vl);\r
-}\r
-#endif\r
-\r
-KHMEXP void KHMAPI kherr_add_ctx_handler(kherr_ctx_handler h,\r
-                                         khm_int32 filter,\r
-                                         kherr_serial serial) {\r
-\r
-    khm_size idx;\r
-\r
-    assert(h);\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    if( ctx_handlers == NULL) {\r
-        nc_ctx_handlers = CTX_ALLOC_INCR;\r
-        n_ctx_handlers = 0;\r
-        ctx_handlers = PMALLOC(sizeof(*ctx_handlers) * nc_ctx_handlers);\r
-        /* No need to initialize */\r
-    } else if (n_ctx_handlers == nc_ctx_handlers) {\r
-        khm_size new_nc;\r
-        kherr_handler_node * new_ctxs;\r
-\r
-        new_nc = nc_ctx_handlers + CTX_ALLOC_INCR;\r
-        new_ctxs = PMALLOC(sizeof(*new_ctxs) * new_nc);\r
-        memcpy(new_ctxs, ctx_handlers, n_ctx_handlers * sizeof(*new_ctxs));\r
-\r
-        PFREE(ctx_handlers);\r
-        ctx_handlers = new_ctxs;\r
-        nc_ctx_handlers = new_nc;\r
-    }\r
-\r
-    if (filter == 0)\r
-        filter = KHERR_CTX_BEGIN |\r
-            KHERR_CTX_DESCRIBE |\r
-            KHERR_CTX_END |\r
-            KHERR_CTX_ERROR;\r
-\r
-    /* Since commit events are the most frequent, we put those\r
-       handlers at the top of the list.  When dispatching a commit\r
-       event, we stop looking at the list when we find a filter that\r
-       doesn't filter for commit events. */\r
-    if (filter & KHERR_CTX_EVTCOMMIT) {\r
-       idx = 0;\r
-       memmove(&ctx_handlers[1], &ctx_handlers[0],\r
-               n_ctx_handlers * sizeof(ctx_handlers[0]));\r
-    } else {\r
-       idx = n_ctx_handlers;\r
-    }\r
-\r
-    ctx_handlers[idx].h = h;\r
-    ctx_handlers[idx].filter = filter;\r
-    ctx_handlers[idx].serial = serial;\r
-\r
-    n_ctx_handlers++;\r
-\r
-    LeaveCriticalSection(&cs_error);\r
-}\r
-\r
-KHMEXP void KHMAPI kherr_remove_ctx_handler(kherr_ctx_handler h,\r
-                                            kherr_serial serial) {\r
-    khm_size i;\r
-    EnterCriticalSection(&cs_error);\r
-\r
-    for (i=0 ; i < n_ctx_handlers; i++) {\r
-        if (ctx_handlers[i].h == h &&\r
-            ctx_handlers[i].serial == serial) {\r
-            break;\r
-        }\r
-    }\r
-\r
-    if ( i < n_ctx_handlers ) {\r
-        n_ctx_handlers --;\r
-        for (; i < n_ctx_handlers; i++) {\r
-            ctx_handlers[i] = ctx_handlers[i + 1];\r
-        }\r
-    }\r
-    \r
-    LeaveCriticalSection(&cs_error);\r
-}\r
-\r
-/* Called with cs_error held */\r
-void notify_ctx_event(enum kherr_ctx_event e, kherr_context * c) {\r
-    khm_size i;\r
-\r
-    kherr_ctx_handler h;\r
-\r
-    for (i=0; i<n_ctx_handlers; i++) {\r
-        if (ctx_handlers[i].h && (ctx_handlers[i].filter & e) &&\r
-            (ctx_handlers[i].serial == 0 ||\r
-             ctx_handlers[i].serial == c->serial)) {\r
-            if (IsBadCodePtr((FARPROC) ctx_handlers[i].h)) {\r
-                ctx_handlers[i].h = NULL;\r
-            } else {\r
-                h = ctx_handlers[i].h;\r
-                (*h)(e,c);\r
-\r
-                /* a context handler is allowed to remove itself\r
-                   during a callback.  It is, however, not allowed to\r
-                   remove anything else. */\r
-                if (h != ctx_handlers[i].h)\r
-                    i--;\r
-            }\r
-        } else if (e == KHERR_CTX_EVTCOMMIT &&\r
-                  !(ctx_handlers[i].filter & KHERR_CTX_EVTCOMMIT)) {\r
-           /* All handlers that filter for commit events are at the\r
-              top of the list.  If this handler wasn't filtering for\r
-              it, then there's no point in goint further down the\r
-              list. */\r
-           break;\r
-       }\r
-    }\r
-}\r
-\r
-void attach_this_thread(void) {\r
-    kherr_thread * t;\r
-\r
-    t = (kherr_thread *) TlsGetValue(tls_error);\r
-    if (t)\r
-        return;\r
-\r
-    t = PMALLOC(sizeof(kherr_thread) + \r
-                sizeof(kherr_context *) * THREAD_STACK_SIZE);\r
-    t->nc_ctx = THREAD_STACK_SIZE;\r
-    t->n_ctx = 0;\r
-    t->ctx = (kherr_context **) &t[1];\r
-\r
-    TlsSetValue(tls_error, t);\r
-}\r
-\r
-void detach_this_thread(void) {\r
-    kherr_thread * t;\r
-    khm_size i;\r
-\r
-    t = (kherr_thread *) TlsGetValue(tls_error);\r
-    if (t) {\r
-        for(i=0; i < t->n_ctx; i++) {\r
-            kherr_release_context(t->ctx[i]);\r
-        }\r
-        PFREE(t);\r
-        TlsSetValue(tls_error, 0);\r
-    }\r
-}\r
-\r
-kherr_context * peek_context(void) {\r
-    kherr_thread * t;\r
-\r
-    t = (kherr_thread *) TlsGetValue(tls_error);\r
-    if (t) {\r
-        if (t->n_ctx > 0)\r
-            return t->ctx[t->n_ctx - 1];\r
-        else\r
-            return NULL;\r
-    } else\r
-        return NULL;\r
-}\r
-\r
-void push_context(kherr_context * c) {\r
-    kherr_thread * t;\r
-\r
-    t = (kherr_thread *) TlsGetValue(tls_error);\r
-    if (!t) {\r
-        attach_this_thread();\r
-        t = (kherr_thread *) TlsGetValue(tls_error);\r
-        assert(t);\r
-    }\r
-\r
-    if (t->n_ctx == t->nc_ctx) {\r
-        khm_size nc_new;\r
-        khm_size cb_new;\r
-        kherr_thread * nt;\r
-\r
-        nc_new = t->nc_ctx + THREAD_STACK_SIZE;\r
-        cb_new = sizeof(kherr_thread) + \r
-            sizeof(kherr_context *) * nc_new;\r
-\r
-        nt = PMALLOC(cb_new);\r
-        memcpy(nt, t, sizeof(kherr_thread) +\r
-               sizeof(kherr_context *) * t->n_ctx);\r
-        nt->ctx = (kherr_context **) &nt[1];\r
-        nt->nc_ctx = nc_new;\r
-\r
-        PFREE(t);\r
-        t = nt;\r
-        TlsSetValue(tls_error, t);\r
-    }\r
-\r
-    assert(t->n_ctx < t->nc_ctx);\r
-    t->ctx[t->n_ctx++] = c;\r
-\r
-    kherr_hold_context(c);\r
-}\r
-\r
-/* returned pointer is still held */\r
-kherr_context * pop_context(void) {\r
-    kherr_thread * t;\r
-    kherr_context * c;\r
-\r
-    t = (kherr_thread *) TlsGetValue(tls_error);\r
-    if (t) {\r
-        if (t->n_ctx > 0) {\r
-            c = t->ctx[--(t->n_ctx)];\r
-            return c;\r
-        } else\r
-            return NULL;\r
-    } else {\r
-        return NULL;\r
-    }\r
-}\r
-\r
-kherr_event * get_empty_event(void) {\r
-    kherr_event * e;\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    if(evt_free_list) {\r
-        LPOP(&evt_free_list, &e);\r
-    } else {\r
-        e = PMALLOC(sizeof(*e));\r
-    }\r
-    LeaveCriticalSection(&cs_error);\r
-    ZeroMemory(e, sizeof(*e));\r
-    e->severity = KHERR_NONE;\r
-    e->magic = KHERR_EVENT_MAGIC;\r
-\r
-    return e;\r
-}\r
-\r
-void free_event_params(kherr_event * e) {\r
-    if(parm_type(e->p1) == KEPT_STRINGT) {\r
-        assert((void *) parm_data(e->p1));\r
-        PFREE((void*) parm_data(e->p1));\r
-        ZeroMemory(&e->p1, sizeof(e->p1));\r
-    }\r
-    if(parm_type(e->p2) == KEPT_STRINGT) {\r
-        assert((void *) parm_data(e->p2));\r
-        PFREE((void*) parm_data(e->p2));\r
-        ZeroMemory(&e->p2, sizeof(e->p2));\r
-    }\r
-    if(parm_type(e->p3) == KEPT_STRINGT) {\r
-        assert((void *) parm_data(e->p3));\r
-        PFREE((void*) parm_data(e->p3));\r
-        ZeroMemory(&e->p3, sizeof(e->p3));\r
-    }\r
-    if(parm_type(e->p4) == KEPT_STRINGT) {\r
-        assert((void *) parm_data(e->p4));\r
-        PFREE((void*) parm_data(e->p4));\r
-        ZeroMemory(&e->p4, sizeof(e->p4));\r
-    }\r
-}\r
-\r
-void free_event(kherr_event * e) {\r
-\r
-    EnterCriticalSection(&cs_error);\r
-\r
-    assert(e->magic == KHERR_EVENT_MAGIC);\r
-\r
-#ifdef DEBUG_CONTEXT\r
-    kherr_debug_printf(L"Freeing event 0x%x\n", e);\r
-    if (!(e->flags & KHERR_RF_STR_RESOLVED))\r
-        resolve_event_strings(e);\r
-    if (e->short_desc)\r
-        kherr_debug_printf(L"  Desc(S):[%s]\n", e->short_desc);\r
-    if (e->long_desc)\r
-        kherr_debug_printf(L"  Desc(L):[%s]\n", e->long_desc);\r
-    if (e->suggestion)\r
-        kherr_debug_printf(L"  Suggest:[%s]\n", e->suggestion);\r
-    if (e->facility)\r
-        kherr_debug_printf(L"  Facility:[%s]\n", e->facility);\r
-#endif\r
-\r
-    if(e->flags & KHERR_RF_FREE_SHORT_DESC) {\r
-        assert(e->short_desc);\r
-        PFREE((void *) e->short_desc);\r
-    }\r
-    if(e->flags & KHERR_RF_FREE_LONG_DESC) {\r
-        assert(e->long_desc);\r
-        PFREE((void *) e->long_desc);\r
-    }\r
-    if(e->flags & KHERR_RF_FREE_SUGGEST) {\r
-        assert(e->suggestion);\r
-        PFREE((void *) e->suggestion);\r
-    }\r
-\r
-    free_event_params(e);\r
-\r
-    ZeroMemory(e, sizeof(e));\r
-\r
-    LPUSH(&evt_free_list, e);\r
-    LeaveCriticalSection(&cs_error);\r
-}\r
-\r
-kherr_context * get_empty_context(void) {\r
-    kherr_context * c;\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    if(ctx_free_list)\r
-        LPOP(&ctx_free_list, &c);\r
-    else {\r
-        c = PMALLOC(sizeof(kherr_context));\r
-    }\r
\r
-    ZeroMemory(c,sizeof(*c));\r
-    c->severity = KHERR_NONE;\r
-    c->flags = KHERR_CF_UNBOUND;\r
-    c->magic = KHERR_CONTEXT_MAGIC;\r
-    c->serial = ++ctx_serial;\r
-\r
-    LPUSH(&ctx_root_list, c);\r
-\r
-    LeaveCriticalSection(&cs_error);\r
-   \r
-    return c;\r
-}\r
-\r
-\r
-/* Assumes that the context has been deleted from all relevant\r
-   lists */\r
-void free_context(kherr_context * c) {\r
-    kherr_context * ch;\r
-    kherr_event * e;\r
-\r
-    assert(c->magic == KHERR_CONTEXT_MAGIC);\r
-#ifdef DEBUG_CONTEXT\r
-    kherr_debug_printf(L"Freeing context 0x%x\n", c);\r
-#endif\r
-\r
-    EnterCriticalSection(&cs_error);\r
-\r
-    if (c->desc_event)\r
-        free_event(c->desc_event);\r
-    c->desc_event = NULL;\r
-\r
-    TPOPCHILD(c, &ch);\r
-    while(ch) {\r
-        free_context(ch);\r
-        TPOPCHILD(c, &ch);\r
-    }\r
-    QGET(c, &e);\r
-    while(e) {\r
-        free_event(e);\r
-        QGET(c, &e);\r
-    }\r
-\r
-    c->serial = 0;\r
-\r
-    LPUSH(&ctx_free_list,c);\r
-    LeaveCriticalSection(&cs_error);\r
-\r
-#ifdef DEBUG_CONTEXT\r
-    kherr_debug_printf(L"Done with context 0x%x\n", c);\r
-#endif\r
-}\r
-\r
-void add_event(kherr_context * c, kherr_event * e)\r
-{\r
-    kherr_event * te;\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    te = QBOTTOM(c);\r
-    if (te && !(te->flags & KHERR_RF_COMMIT)) {\r
-       notify_ctx_event(KHERR_CTX_EVTCOMMIT, c);\r
-       te->flags |= KHERR_RF_COMMIT;\r
-    }\r
-\r
-    QPUT(c,e);\r
-    if(c->severity >= e->severity) {\r
-        if (e->severity <= KHERR_ERROR)\r
-            notify_ctx_event(KHERR_CTX_ERROR, c);\r
-\r
-        c->severity = e->severity;\r
-        c->err_event = e;\r
-        c->flags &= ~KHERR_CF_DIRTY;\r
-    }\r
-    LeaveCriticalSection(&cs_error);\r
-}\r
-\r
-void pick_err_event(kherr_context * c)\r
-{\r
-    kherr_event * e;\r
-    kherr_event * ce = NULL;\r
-    enum kherr_severity s;\r
-\r
-    s = KHERR_RESERVED_BANK;\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    e = QTOP(c);\r
-    while(e) {\r
-        if(!(e->flags & KHERR_RF_INERT) && \r
-           s >= e->severity) {\r
-            ce = e;\r
-            s = e->severity;\r
-        }\r
-        e = QNEXT(e);\r
-    }\r
-\r
-    if(ce) {\r
-        c->err_event = ce;\r
-        c->severity = ce->severity;\r
-    } else {\r
-        c->err_event = NULL;\r
-        c->severity = KHERR_NONE;\r
-    }\r
-\r
-    c->flags &= ~KHERR_CF_DIRTY;\r
-    LeaveCriticalSection(&cs_error);\r
-}\r
-\r
-static void arg_from_param(DWORD_PTR ** parm, kherr_param p) {\r
-    int t;\r
-\r
-    if (p.type != KEPT_NONE) {\r
-        t = parm_type(p);\r
-        if (t == KEPT_INT32 ||\r
-            t == KEPT_UINT32 ||\r
-            t == KEPT_STRINGC ||\r
-            t == KEPT_STRINGT ||\r
-            t == KEPT_PTR) {\r
-\r
-            *(*parm)++ = (DWORD_PTR) parm_data(p);\r
-\r
-        } else if (t == KEPT_INT64 ||\r
-                 t == KEPT_UINT64) {\r
-            *(*parm)++ = (DWORD_PTR) parm_data(p) & 0xffffffff;\r
-            *(*parm)++ = (DWORD_PTR) (parm_data(p) >> 32) & 0xffffffff;\r
-        } else\r
-            *(*parm)++ = 0;\r
-    }\r
-}\r
-\r
-/* The 'buf' parameter MUST point to a DWORD_PTR[8] array */\r
-static void args_from_event(DWORD_PTR * buf, kherr_event * e) {\r
-    arg_from_param(&buf, e->p1);\r
-    arg_from_param(&buf, e->p2);\r
-    arg_from_param(&buf, e->p3);\r
-    arg_from_param(&buf, e->p4);\r
-}\r
-\r
-static void resolve_string_resource(kherr_event * e,\r
-                                    const wchar_t ** str,\r
-                                    khm_int32 if_flag,\r
-                                    khm_int32 or_flag) {\r
-    wchar_t tfmt[KHERR_MAXCCH_STRING];\r
-    wchar_t tbuf[KHERR_MAXCCH_STRING];\r
-    size_t chars = 0;\r
-    size_t bytes = 0;\r
-\r
-    if(e->flags & if_flag) {\r
-        if(e->h_module != NULL)\r
-            chars = LoadString(e->h_module, (UINT)(INT_PTR) *str, \r
-                               tfmt, ARRAYLENGTH(tbuf));\r
-        if(e->h_module == NULL || chars == 0)\r
-            *str = NULL;\r
-        else {\r
-            wchar_t * s;\r
-            DWORD_PTR args[8];\r
-\r
-            args_from_event(args, e);\r
-\r
-            chars = FormatMessage(FORMAT_MESSAGE_FROM_STRING |\r
-                               FORMAT_MESSAGE_ARGUMENT_ARRAY,\r
-                               tfmt,\r
-                               0,\r
-                               0,\r
-                               tbuf,\r
-                               ARRAYLENGTH(tbuf),\r
-                               (va_list *) args);\r
-\r
-            if (chars == 0) {\r
-                *str = NULL;\r
-            } else {\r
-                bytes = (chars + 1) * sizeof(wchar_t);\r
-                s = PMALLOC(bytes);\r
-                assert(s);\r
-                StringCbCopy(s, bytes, tbuf);\r
-                *str = s;\r
-                e->flags |= or_flag;\r
-            }\r
-        }\r
-        e->flags &= ~if_flag;\r
-    }\r
-}\r
-\r
-static void resolve_msg_resource(kherr_event * e,\r
-                                const wchar_t ** str,\r
-                                khm_int32 if_flag,\r
-                                khm_int32 or_flag) {\r
-    wchar_t tbuf[KHERR_MAXCCH_STRING];\r
-    size_t chars = 0;\r
-    size_t bytes = 0;\r
-    DWORD_PTR args[8];\r
-\r
-    if(e->flags & if_flag) {\r
-        if(e->h_module != NULL) {\r
-            args_from_event(args, e);\r
-\r
-            chars = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE |\r
-                                  FORMAT_MESSAGE_ARGUMENT_ARRAY,\r
-                                  (LPCVOID) e->h_module,\r
-                                  (DWORD)(DWORD_PTR) *str,\r
-                                  0,\r
-                                  tbuf,\r
-                                  ARRAYLENGTH(tbuf),\r
-                                  (va_list *) args);\r
-        }\r
-\r
-        if(e->h_module == NULL || chars == 0) {\r
-            *str = NULL;\r
-        } else {\r
-            wchar_t * s;\r
-\r
-            /* MC inserts trailing \r\n to each message unless the\r
-               message is terminated with a %0.  We remove the last\r
-               line break since it is irrelevant to our handling of\r
-               the string in the UI. */\r
-            if (tbuf[chars-1] == L'\n')\r
-                tbuf[--chars] = L'\0';\r
-            if (tbuf[chars-1] == L'\r')\r
-                tbuf[--chars] = L'\0';\r
-\r
-            bytes = (chars + 1) * sizeof(wchar_t);\r
-            s = PMALLOC(bytes);\r
-            assert(s);\r
-            StringCbCopy(s, bytes, tbuf);\r
-            *str = s;\r
-            e->flags |= or_flag;\r
-        }\r
-        e->flags &= ~if_flag;\r
-    }\r
-}\r
-\r
-static void resolve_string(kherr_event * e,\r
-                           const wchar_t ** str,\r
-                           khm_int32 mask,\r
-                           khm_int32 free_if,\r
-                           khm_int32 or_flag) {\r
-\r
-    wchar_t tbuf[KHERR_MAXCCH_STRING];\r
-    size_t chars;\r
-    size_t bytes;\r
-    DWORD_PTR args[8];\r
-\r
-    if (((e->flags & mask) == 0 ||\r
-        (e->flags & mask) == free_if) &&\r
-        *str != NULL) {\r
-\r
-        args_from_event(args, e);\r
-        chars = FormatMessage(FORMAT_MESSAGE_FROM_STRING |\r
-                              FORMAT_MESSAGE_ARGUMENT_ARRAY,\r
-                              (LPCVOID) *str,\r
-                              0,\r
-                              0,\r
-                              tbuf,\r
-                              ARRAYLENGTH(tbuf),\r
-                              (va_list *) args);\r
-\r
-        if ((e->flags & mask) == free_if) {\r
-            PFREE((void *) *str);\r
-        }\r
-\r
-        e->flags &= ~mask;\r
-\r
-        if (chars == 0) {\r
-            *str = 0;\r
-        } else {\r
-            wchar_t * s;\r
-\r
-            bytes = (chars + 1) * sizeof(wchar_t);\r
-            s = PMALLOC(bytes);\r
-            assert(s);\r
-            StringCbCopy(s, bytes, tbuf);\r
-            *str = s;\r
-            e->flags |= or_flag;\r
-        }\r
-    }\r
-\r
-}\r
-\r
-void resolve_event_strings(kherr_event * e)\r
-{\r
-    resolve_string(e, &e->short_desc,\r
-                   KHERR_RFMASK_SHORT_DESC,\r
-                   KHERR_RF_FREE_SHORT_DESC,\r
-                   KHERR_RF_FREE_SHORT_DESC);\r
-\r
-    resolve_string(e, &e->long_desc,\r
-                   KHERR_RFMASK_LONG_DESC,\r
-                   KHERR_RF_FREE_LONG_DESC,\r
-                   KHERR_RF_FREE_LONG_DESC);\r
-\r
-    resolve_string(e, &e->suggestion,\r
-                   KHERR_RFMASK_SUGGEST,\r
-                   KHERR_RF_FREE_SUGGEST,\r
-                   KHERR_RF_FREE_SUGGEST);\r
-\r
-    resolve_string_resource(e, &e->short_desc,\r
-                            KHERR_RF_RES_SHORT_DESC,\r
-                            KHERR_RF_FREE_SHORT_DESC);\r
-\r
-    resolve_string_resource(e, &e->long_desc,\r
-                            KHERR_RF_RES_LONG_DESC, \r
-                            KHERR_RF_FREE_LONG_DESC);\r
-\r
-    resolve_string_resource(e, &e->suggestion,\r
-                            KHERR_RF_RES_SUGGEST, \r
-                            KHERR_RF_FREE_SUGGEST);\r
-\r
-    resolve_msg_resource(e, &e->short_desc,\r
-                         KHERR_RF_MSG_SHORT_DESC, \r
-                         KHERR_RF_FREE_SHORT_DESC);\r
-    resolve_msg_resource(e, &e->long_desc,\r
-                         KHERR_RF_MSG_LONG_DESC, \r
-                         KHERR_RF_FREE_LONG_DESC);\r
-    resolve_msg_resource(e, &e->suggestion,\r
-                         KHERR_RF_MSG_SUGGEST, \r
-                         KHERR_RF_FREE_SUGGEST);\r
-\r
-    /* get rid of dangling reference now that we have done everything\r
-       we can with it.  Since we have already dealt with all the\r
-       parameter inserts, we don't need the parameters anymore\r
-       either. */\r
-    free_event_params(e);\r
-\r
-    e->h_module = NULL;\r
-    e->flags |= KHERR_RF_STR_RESOLVED;\r
-}\r
-\r
-\r
-KHMEXP void KHMAPI kherr_evaluate_event(kherr_event * e) {\r
-    if (!e)\r
-        return;\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    resolve_event_strings(e);\r
-    LeaveCriticalSection(&cs_error);\r
-}\r
-\r
-KHMEXP void KHMAPI kherr_evaluate_last_event(void) {\r
-    kherr_context * c;\r
-    kherr_event * e;\r
-    DWORD tid;\r
-\r
-    c = peek_context();\r
-    if(!c)\r
-        return;\r
-    tid = GetCurrentThreadId();\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    e = QBOTTOM(c);\r
-    while (e != NULL && e->thread_id != tid)\r
-        e = QPREV(e);\r
-\r
-    if(!e)\r
-        goto _exit;\r
-\r
-    resolve_event_strings(e);\r
-\r
- _exit:\r
-    LeaveCriticalSection(&cs_error);\r
-}\r
-\r
-KHMEXP kherr_event * __cdecl\r
-kherr_reportf(const wchar_t * long_desc_fmt, ...) {\r
-    va_list vl;\r
-    wchar_t buf[1024];\r
-    kherr_event * e;\r
-\r
-    va_start(vl, long_desc_fmt);\r
-    StringCbVPrintf(buf, sizeof(buf), long_desc_fmt, vl);\r
-#ifdef DEBUG\r
-    OutputDebugString(buf);\r
-#endif\r
-    va_end(vl);\r
-\r
-    e = kherr_report(KHERR_DEBUG_1,\r
-                     NULL, NULL, NULL, buf, NULL, 0,\r
-                     KHERR_SUGGEST_NONE, _vnull(), _vnull(), _vnull(), _vnull(),\r
-                     KHERR_RF_CSTR_LONG_DESC\r
-#ifdef _WIN32\r
-                     ,NULL\r
-#endif\r
-                     );\r
-    if (e) {\r
-        kherr_evaluate_event(e);\r
-    }\r
-\r
-    return e;\r
-}\r
-\r
-KHMEXP kherr_event * __cdecl\r
-kherr_reportf_ex(enum kherr_severity severity,\r
-                 const wchar_t * facility,\r
-                 khm_int32 facility_id,\r
-#ifdef _WIN32\r
-                 HMODULE hModule,\r
-#endif\r
-                 const wchar_t * long_desc_fmt, ...) {\r
-    va_list vl;\r
-    wchar_t buf[1024];\r
-    kherr_event * e;\r
-\r
-    va_start(vl, long_desc_fmt);\r
-    StringCbVPrintf(buf, sizeof(buf), long_desc_fmt, vl);\r
-#ifdef DEBUG\r
-    OutputDebugString(buf);\r
-#endif\r
-    va_end(vl);\r
-\r
-    e = kherr_report(severity, NULL, facility, NULL, buf, NULL, facility_id,\r
-                     KHERR_SUGGEST_NONE,\r
-                     _vnull(),\r
-                     _vnull(),\r
-                     _vnull(),\r
-                     _vnull(), KHERR_RF_CSTR_LONG_DESC\r
-#ifdef _WIN32\r
-                     ,hModule\r
-#endif\r
-                     );\r
-    if (e) {\r
-        kherr_evaluate_event(e);\r
-    }\r
-\r
-    return e;\r
-}\r
-\r
-KHMEXP kherr_event * KHMAPI \r
-kherr_report(enum kherr_severity severity,\r
-             const wchar_t * short_desc,\r
-             const wchar_t * facility,\r
-             const wchar_t * location,\r
-             const wchar_t * long_desc,\r
-             const wchar_t * suggestion,\r
-             khm_int32 facility_id,\r
-             enum kherr_suggestion suggestion_id,\r
-             kherr_param p1,\r
-             kherr_param p2,\r
-             kherr_param p3,\r
-             kherr_param p4,\r
-             khm_int32 flags\r
-#ifdef _WIN32\r
-             ,HMODULE  h_module\r
-#endif\r
-             ) {\r
-    kherr_context * c;\r
-    kherr_event * e;\r
-\r
-    /*TODO: sanity check flags (ISPOW2) */\r
-\r
-    e = get_empty_event();\r
-\r
-    e->thread_id = GetCurrentThreadId();\r
-    e->time_ticks = GetTickCount();\r
-    GetSystemTimeAsFileTime(&e->time_ft);\r
-\r
-    e->severity = severity;\r
-    e->short_desc = short_desc;\r
-    e->facility = facility;\r
-    e->location = location;\r
-    e->long_desc = long_desc;\r
-    e->suggestion = suggestion;\r
-    e->facility_id = facility_id;\r
-    e->suggestion_id = suggestion_id;\r
-    e->p1 = p1;\r
-    e->p2 = p2;\r
-    e->p3 = p3;\r
-    e->p4 = p4;\r
-    e->flags = flags;\r
-#ifdef _WIN32\r
-    e->h_module = h_module;\r
-#endif\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    c = peek_context();\r
-\r
-    if(!c) {\r
-        /* the reason why we are doing it this way is because p1..p4,\r
-           the descriptions and the suggestion may contain allocations\r
-           that has to be freed. */\r
-        free_event(e);\r
-        e = NULL;\r
-    } else {\r
-        add_event(c,e);\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_error);\r
-\r
-    return e;\r
-}\r
-\r
-KHMEXP void KHMAPI kherr_suggest(wchar_t * suggestion, \r
-                                 enum kherr_suggestion suggestion_id,\r
-                                 khm_int32 flags) {\r
-    kherr_context * c;\r
-    kherr_event * e;\r
-    DWORD tid;\r
-\r
-    if (flags != KHERR_RF_CSTR_SUGGEST &&\r
-        flags != KHERR_RF_RES_SUGGEST &&\r
-        flags != KHERR_RF_MSG_SUGGEST &&\r
-        flags != KHERR_RF_FREE_SUGGEST)\r
-        return;\r
-\r
-    c = peek_context();\r
-    if(!c)\r
-        return;\r
-\r
-    tid = GetCurrentThreadId();\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    e = QBOTTOM(c);\r
-    while (e != NULL && e->thread_id != tid)\r
-        e = QPREV(e);\r
-\r
-    if(!e)\r
-        goto _exit;\r
-\r
-    /* if strings have already been resolved in this event, we cant\r
-       add any more unresolved strings. */\r
-    if ((flags == KHERR_RF_RES_SUGGEST ||\r
-         flags == KHERR_RF_MSG_SUGGEST) &&\r
-        (e->flags & KHERR_RF_STR_RESOLVED))\r
-        goto _exit;\r
-\r
-    e->suggestion = suggestion;\r
-    e->suggestion_id = suggestion_id;\r
-    e->flags |= flags;\r
-_exit:\r
-    LeaveCriticalSection(&cs_error);\r
-}\r
-\r
-KHMEXP void KHMAPI kherr_location(wchar_t * location) {\r
-    kherr_context * c;\r
-    kherr_event * e;\r
-    DWORD tid;\r
-\r
-    c = peek_context();\r
-    if(!c)\r
-        return;\r
-    tid = GetCurrentThreadId();\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    e = QBOTTOM(c);\r
-    while (e != NULL && e->thread_id != tid)\r
-        e = QPREV(e);\r
-\r
-    if(!e)\r
-        goto _exit;\r
-    e->location = location;\r
-_exit:\r
-    LeaveCriticalSection(&cs_error);\r
-}\r
-\r
-KHMEXP void KHMAPI kherr_facility(wchar_t * facility, \r
-                                  khm_int32 facility_id) {\r
-    kherr_context * c;\r
-    kherr_event * e;\r
-    DWORD tid;\r
-\r
-    c = peek_context();\r
-    if(!c)\r
-        return;\r
-    tid = GetCurrentThreadId();\r
-    EnterCriticalSection(&cs_error);\r
-    e = QBOTTOM(c);\r
-    while (e != NULL && e->thread_id != tid)\r
-        e = QPREV(e);\r
-\r
-    if(!e)\r
-        goto _exit;\r
-    e->facility = facility;\r
-    e->facility_id = facility_id;\r
-_exit:\r
-    LeaveCriticalSection(&cs_error);\r
-}\r
-\r
-KHMEXP void KHMAPI kherr_set_desc_event(void) {\r
-    kherr_context * c;\r
-    kherr_event * e;\r
-    DWORD tid;\r
-\r
-    c = peek_context();\r
-    if(!c)\r
-        return;\r
-    tid = GetCurrentThreadId();\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    e = QBOTTOM(c);\r
-    while (e != NULL && e->thread_id != tid)\r
-        e = QPREV(e);\r
-\r
-    if(!e || c->desc_event)\r
-        goto _exit;\r
-\r
-    QDEL(c,e);\r
-    c->desc_event = e;\r
-    e->severity = KHERR_NONE;\r
-    resolve_event_strings(e);\r
-\r
-    notify_ctx_event(KHERR_CTX_DESCRIBE, c);\r
-\r
-_exit:\r
-    LeaveCriticalSection(&cs_error);\r
-}\r
-\r
-KHMEXP void KHMAPI kherr_del_last_event(void) {\r
-    kherr_context * c;\r
-    kherr_event * e;\r
-    DWORD tid;\r
-\r
-    c = peek_context();\r
-\r
-    if(!c)\r
-        return;\r
-\r
-    tid = GetCurrentThreadId();\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    e = QBOTTOM(c);\r
-    while (e != NULL && e->thread_id != tid)\r
-        e = QPREV(e);\r
-\r
-    if(e) {\r
-        QDEL(c, e);\r
-        if(c->err_event == e) {\r
-            pick_err_event(c);\r
-        }\r
-        free_event(e);\r
-    }\r
-    LeaveCriticalSection(&cs_error);\r
-}\r
-\r
-KHMEXP void KHMAPI kherr_push_context(kherr_context * c)\r
-{\r
-    kherr_context * p;\r
-    int new_context = FALSE;\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    p = peek_context();\r
-    if(p && (c->flags & KHERR_CF_UNBOUND)) {\r
-        LDELETE(&ctx_root_list, c);\r
-        TADDCHILD(p,c);\r
-        c->flags &= ~KHERR_CF_UNBOUND;\r
-        kherr_hold_context(p);\r
-        new_context = TRUE;\r
-    }\r
-    push_context(c);\r
-\r
-    if (new_context)\r
-        notify_ctx_event(KHERR_CTX_BEGIN, c);\r
-\r
-    LeaveCriticalSection(&cs_error);\r
-}\r
-\r
-KHMEXP void KHMAPI kherr_push_new_context(khm_int32 flags) \r
-{\r
-    kherr_context * p;\r
-    kherr_context * c;\r
-\r
-    flags &= KHERR_CFMASK_INITIAL;\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    p = peek_context();\r
-    c = get_empty_context();\r
-    if(p) {\r
-        LDELETE(&ctx_root_list, c);\r
-        TADDCHILD(p,c);\r
-        c->flags &= ~KHERR_CF_UNBOUND;\r
-        kherr_hold_context(p);\r
-    }\r
-    c->flags |= flags;\r
-    push_context(c);\r
-\r
-    notify_ctx_event(KHERR_CTX_BEGIN, c);\r
-\r
-    LeaveCriticalSection(&cs_error);\r
-}\r
-\r
-kherr_param dup_parm(kherr_param p) {\r
-    if(parm_type(p) == KEPT_STRINGT) {\r
-        wchar_t * d = PWCSDUP((wchar_t *)parm_data(p));\r
-        return kherr_val(KEPT_STRINGT, (khm_ui_8) d);\r
-    } else\r
-        return p;\r
-}\r
-\r
-kherr_event * fold_context(kherr_context * c) {\r
-    kherr_event * e;\r
-    kherr_event * g;\r
-\r
-    if (!c)\r
-        return NULL;\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    if(!c->err_event || (c->flags & KHERR_CF_DIRTY)) {\r
-        pick_err_event(c);\r
-    }\r
-    if(c->err_event) {\r
-        g = c->err_event;\r
-        e = get_empty_event();\r
-        *e = *g;\r
-        g->short_desc = NULL;\r
-        g->long_desc = NULL;\r
-        g->suggestion = NULL;\r
-        g->flags &=\r
-            ~(KHERR_RF_FREE_SHORT_DESC |\r
-              KHERR_RF_FREE_LONG_DESC |\r
-              KHERR_RF_FREE_SUGGEST);\r
-        LINIT(e);\r
-        e->p1 = dup_parm(g->p1);\r
-        e->p2 = dup_parm(g->p2);\r
-        e->p3 = dup_parm(g->p3);\r
-        e->p4 = dup_parm(g->p4);\r
-    } else {\r
-        e = c->desc_event;\r
-        c->desc_event = NULL;\r
-    }\r
-\r
-    if (e)\r
-        e->flags |= KHERR_RF_CONTEXT_FOLD;\r
-\r
-    LeaveCriticalSection(&cs_error);\r
-\r
-    return e;\r
-}\r
-\r
-KHMEXP void KHMAPI kherr_hold_context(kherr_context * c) {\r
-    assert(c && c->magic == KHERR_CONTEXT_MAGIC);\r
-    EnterCriticalSection(&cs_error);\r
-    c->refcount++;\r
-    LeaveCriticalSection(&cs_error);\r
-}\r
-\r
-KHMEXP void KHMAPI kherr_release_context(kherr_context * c) {\r
-    assert(c && c->magic == KHERR_CONTEXT_MAGIC);\r
-    EnterCriticalSection(&cs_error);\r
-    c->refcount--;\r
-    if (c->refcount == 0) {\r
-        kherr_event * e;\r
-        kherr_context * p;\r
-\r
-       e = QBOTTOM(c);\r
-       if (e && !(e->flags & KHERR_RF_COMMIT)) {\r
-           notify_ctx_event(KHERR_CTX_EVTCOMMIT, c);\r
-           e->flags |= KHERR_RF_COMMIT;\r
-       }\r
-\r
-        notify_ctx_event(KHERR_CTX_END, c);\r
-\r
-        p = TPARENT(c);\r
-        if (p) {\r
-            e = fold_context(c);\r
-            if (e)\r
-                add_event(p, e);\r
-\r
-            TDELCHILD(p, c);\r
-            kherr_release_context(p);\r
-        } else {\r
-            LDELETE(&ctx_root_list, c);\r
-        }\r
-        free_context(c);\r
-    }\r
-    LeaveCriticalSection(&cs_error);\r
-}\r
-\r
-KHMEXP void KHMAPI kherr_pop_context(void) {\r
-    kherr_context * c;\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    c = pop_context();\r
-    if(c) {\r
-        kherr_release_context(c);\r
-    }\r
-    LeaveCriticalSection(&cs_error);\r
-}\r
-\r
-KHMEXP kherr_context * KHMAPI kherr_peek_context(void) {\r
-    kherr_context * c;\r
-\r
-    c = peek_context();\r
-    if (c)\r
-        kherr_hold_context(c);\r
-\r
-    return c;\r
-}\r
-\r
-KHMEXP khm_boolean KHMAPI kherr_is_error(void) {\r
-    kherr_context * c = peek_context();\r
-    return kherr_is_error_i(c);\r
-}\r
-\r
-KHMEXP khm_boolean KHMAPI kherr_is_error_i(kherr_context * c) {\r
-    if(c && c->severity <= KHERR_ERROR)\r
-        return TRUE;\r
-    else\r
-        return FALSE;\r
-}\r
-\r
-KHMEXP void KHMAPI kherr_clear_error(void) {\r
-    kherr_context * c = peek_context();\r
-    if (c)\r
-        kherr_clear_error_i(c);\r
-}\r
-\r
-KHMEXP void KHMAPI kherr_clear_error_i(kherr_context * c) {\r
-    kherr_event * e;\r
-    if (c) {\r
-        EnterCriticalSection(&cs_error);\r
-        e = QTOP(c);\r
-        while(e) {\r
-            e->flags |= KHERR_RF_INERT;\r
-            e = QNEXT(e);\r
-        }\r
-        c->severity = KHERR_NONE;\r
-        c->err_event = NULL;\r
-        c->flags &= ~KHERR_CF_DIRTY;\r
-        LeaveCriticalSection(&cs_error);\r
-    }\r
-}\r
-\r
-KHMEXP void KHMAPI kherr_set_progress(khm_ui_4 num, khm_ui_4 denom) {\r
-    kherr_context * c = peek_context();\r
-    if(c) {\r
-        EnterCriticalSection(&cs_error);\r
-        c->progress_denom = denom;\r
-        c->progress_num = num;\r
-        LeaveCriticalSection(&cs_error);\r
-    }\r
-}\r
-\r
-KHMEXP void KHMAPI kherr_get_progress(khm_ui_4 * num, khm_ui_4 * denom) {\r
-    kherr_context * c = peek_context();\r
-    kherr_get_progress_i(c,num,denom);\r
-}\r
-\r
-KHMEXP void KHMAPI kherr_get_progress_i(kherr_context * c, \r
-                                        khm_ui_4 * num, \r
-                                        khm_ui_4 * denom) {\r
-    if(c) {\r
-        EnterCriticalSection(&cs_error);\r
-        *num = c->progress_num;\r
-        *denom = c->progress_denom;\r
-        LeaveCriticalSection(&cs_error);\r
-    } else {\r
-        *num = 0;\r
-        *denom = 0;\r
-    }\r
-}\r
-\r
-KHMEXP kherr_event * KHMAPI kherr_get_first_event(kherr_context * c)\r
-{\r
-    kherr_event * e;\r
-    EnterCriticalSection(&cs_error);\r
-    e = QTOP(c);\r
-    LeaveCriticalSection(&cs_error);\r
-    return e;\r
-}\r
-\r
-KHMEXP kherr_event * KHMAPI kherr_get_next_event(kherr_event * e)\r
-{\r
-    kherr_event * ee;\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    ee = QNEXT(e);\r
-    LeaveCriticalSection(&cs_error);\r
-    return ee;\r
-}\r
-\r
-KHMEXP kherr_event * KHMAPI kherr_get_prev_event(kherr_event * e)\r
-{\r
-    kherr_event * ee;\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    ee = QPREV(e);\r
-    LeaveCriticalSection(&cs_error);\r
-\r
-    return ee;\r
-}\r
-\r
-KHMEXP kherr_event * KHMAPI kherr_get_last_event(kherr_context * c)\r
-{\r
-    kherr_event * e;\r
-    EnterCriticalSection(&cs_error);\r
-    e = QBOTTOM(c);\r
-    LeaveCriticalSection(&cs_error);\r
-    return e;\r
-}\r
-\r
-KHMEXP kherr_context * KHMAPI kherr_get_first_context(kherr_context * c)\r
-{\r
-    kherr_context * cc;\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    if (c) {\r
-        cc = TFIRSTCHILD(c);\r
-        if (cc)\r
-            kherr_hold_context(cc);\r
-    } else {\r
-        cc = ctx_root_list;\r
-        if (cc)\r
-            kherr_hold_context(cc);\r
-    }\r
-    LeaveCriticalSection(&cs_error);\r
-    return cc;\r
-}\r
-\r
-KHMEXP kherr_context * KHMAPI kherr_get_next_context(kherr_context * c)\r
-{\r
-    kherr_context * cc;\r
-    EnterCriticalSection(&cs_error);\r
-    cc = LNEXT(c);\r
-    if (cc)\r
-        kherr_hold_context(cc);\r
-    LeaveCriticalSection(&cs_error);\r
-    return cc;\r
-}\r
-\r
-KHMEXP kherr_event * KHMAPI kherr_get_err_event(kherr_context * c)\r
-{\r
-    kherr_event * e;\r
-    EnterCriticalSection(&cs_error);\r
-    if(!c->err_event) {\r
-        pick_err_event(c);\r
-    }\r
-    e = c->err_event;\r
-    LeaveCriticalSection(&cs_error);\r
-    return e;\r
-}\r
-\r
-KHMEXP kherr_event * KHMAPI kherr_get_desc_event(kherr_context * c)\r
-{\r
-    kherr_event * e;\r
-\r
-    EnterCriticalSection(&cs_error);\r
-    e = c->desc_event;\r
-    LeaveCriticalSection(&cs_error);\r
-    return e;\r
-}\r
-\r
-KHMEXP kherr_param kherr_dup_string(const wchar_t * s)\r
-{\r
-    wchar_t * dest;\r
-    size_t cb_s;\r
-\r
-    if (s == NULL)\r
-        return _vnull();\r
-\r
-    if (FAILED(StringCbLength(s, KHERR_MAXCB_STRING, &cb_s)))\r
-        cb_s = KHERR_MAXCB_STRING;\r
-    else\r
-        cb_s += sizeof(wchar_t);\r
-\r
-    dest = PMALLOC(cb_s);\r
-    assert(dest != NULL);\r
-    dest[0] = L'\0';\r
-\r
-    StringCbCopy(dest, cb_s, s);\r
-\r
-    return _tstr(dest);\r
-}\r
-\r
-\r
-#if 0\r
-KHMEXP kherr_param kherr_val(khm_octet ptype, khm_ui_8 pvalue) {\r
-    kherr_param p;\r
-    p.type = ptype;\r
-    p.data = pvalue;\r
-\r
-    return p;\r
-}\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+#include<stdarg.h>
+
+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;
+
+kherr_serial ctx_serial = 0;
+
+#ifdef DEBUG
+#define DEBUG_CONTEXT
+
+KHMEXP void kherr_debug_printf(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,
+                                         kherr_serial serial) {
+
+    khm_size idx;
+
+    assert(h);
+
+    EnterCriticalSection(&cs_error);
+    if( ctx_handlers == NULL) {
+        nc_ctx_handlers = CTX_ALLOC_INCR;
+        n_ctx_handlers = 0;
+        ctx_handlers = PMALLOC(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 = PMALLOC(sizeof(*new_ctxs) * new_nc);
+        memcpy(new_ctxs, ctx_handlers, n_ctx_handlers * sizeof(*new_ctxs));
+
+        PFREE(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;
+
+    /* Since commit events are the most frequent, we put those
+       handlers at the top of the list.  When dispatching a commit
+       event, we stop looking at the list when we find a filter that
+       doesn't filter for commit events. */
+    if (filter & KHERR_CTX_EVTCOMMIT) {
+       idx = 0;
+       memmove(&ctx_handlers[1], &ctx_handlers[0],
+               n_ctx_handlers * sizeof(ctx_handlers[0]));
+    } else {
+       idx = n_ctx_handlers;
+    }
+
+    ctx_handlers[idx].h = h;
+    ctx_handlers[idx].filter = filter;
+    ctx_handlers[idx].serial = serial;
+
+    n_ctx_handlers++;
+
+    LeaveCriticalSection(&cs_error);
+}
+
+KHMEXP void KHMAPI kherr_remove_ctx_handler(kherr_ctx_handler h,
+                                            kherr_serial serial) {
+    khm_size i;
+    EnterCriticalSection(&cs_error);
+
+    for (i=0 ; i < n_ctx_handlers; i++) {
+        if (ctx_handlers[i].h == h &&
+            ctx_handlers[i].serial == serial) {
+            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;
+
+    kherr_ctx_handler h;
+
+    for (i=0; i<n_ctx_handlers; i++) {
+        if (ctx_handlers[i].h && (ctx_handlers[i].filter & e) &&
+            (ctx_handlers[i].serial == 0 ||
+             ctx_handlers[i].serial == c->serial)) {
+            if (IsBadCodePtr((FARPROC) ctx_handlers[i].h)) {
+                ctx_handlers[i].h = NULL;
+            } else {
+                h = ctx_handlers[i].h;
+                (*h)(e,c);
+
+                /* a context handler is allowed to remove itself
+                   during a callback.  It is, however, not allowed to
+                   remove anything else. */
+                if (h != ctx_handlers[i].h)
+                    i--;
+            }
+        } else if (e == KHERR_CTX_EVTCOMMIT &&
+                  !(ctx_handlers[i].filter & KHERR_CTX_EVTCOMMIT)) {
+           /* All handlers that filter for commit events are at the
+              top of the list.  If this handler wasn't filtering for
+              it, then there's no point in goint further down the
+              list. */
+           break;
+       }
+    }
+}
+
+void attach_this_thread(void) {
+    kherr_thread * t;
+
+    t = (kherr_thread *) TlsGetValue(tls_error);
+    if (t)
+        return;
+
+    t = PMALLOC(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]);
+        }
+        PFREE(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 = PMALLOC(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;
+
+        PFREE(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 = PMALLOC(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));
+        PFREE((void*) parm_data(e->p1));
+        ZeroMemory(&e->p1, sizeof(e->p1));
+    }
+    if(parm_type(e->p2) == KEPT_STRINGT) {
+        assert((void *) parm_data(e->p2));
+        PFREE((void*) parm_data(e->p2));
+        ZeroMemory(&e->p2, sizeof(e->p2));
+    }
+    if(parm_type(e->p3) == KEPT_STRINGT) {
+        assert((void *) parm_data(e->p3));
+        PFREE((void*) parm_data(e->p3));
+        ZeroMemory(&e->p3, sizeof(e->p3));
+    }
+    if(parm_type(e->p4) == KEPT_STRINGT) {
+        assert((void *) parm_data(e->p4));
+        PFREE((void*) parm_data(e->p4));
+        ZeroMemory(&e->p4, sizeof(e->p4));
+    }
+}
+
+void free_event(kherr_event * e) {
+
+    EnterCriticalSection(&cs_error);
+
+    assert(e->magic == KHERR_EVENT_MAGIC);
+
+#ifdef DEBUG_CONTEXT
+    kherr_debug_printf(L"Freeing event 0x%x\n", e);
+    if (!(e->flags & KHERR_RF_STR_RESOLVED))
+        resolve_event_strings(e);
+    if (e->short_desc)
+        kherr_debug_printf(L"  Desc(S):[%s]\n", e->short_desc);
+    if (e->long_desc)
+        kherr_debug_printf(L"  Desc(L):[%s]\n", e->long_desc);
+    if (e->suggestion)
+        kherr_debug_printf(L"  Suggest:[%s]\n", e->suggestion);
+    if (e->facility)
+        kherr_debug_printf(L"  Facility:[%s]\n", e->facility);
+#endif
+
+    if(e->flags & KHERR_RF_FREE_SHORT_DESC) {
+        assert(e->short_desc);
+        PFREE((void *) e->short_desc);
+    }
+    if(e->flags & KHERR_RF_FREE_LONG_DESC) {
+        assert(e->long_desc);
+        PFREE((void *) e->long_desc);
+    }
+    if(e->flags & KHERR_RF_FREE_SUGGEST) {
+        assert(e->suggestion);
+        PFREE((void *) e->suggestion);
+    }
+
+    free_event_params(e);
+
+    ZeroMemory(e, sizeof(e));
+
+    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 = PMALLOC(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_CONTEXT
+    kherr_debug_printf(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_CONTEXT
+    kherr_debug_printf(L"Done with context 0x%x\n", c);
+#endif
+}
+
+void add_event(kherr_context * c, kherr_event * e)
+{
+    kherr_event * te;
+
+    EnterCriticalSection(&cs_error);
+    te = QBOTTOM(c);
+    if (te && !(te->flags & KHERR_RF_COMMIT)) {
+       notify_ctx_event(KHERR_CTX_EVTCOMMIT, c);
+       te->flags |= KHERR_RF_COMMIT;
+    }
+
+    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.type != KEPT_NONE) {
+        t = parm_type(p);
+        if (t == KEPT_INT32 ||
+            t == KEPT_UINT32 ||
+            t == KEPT_STRINGC ||
+            t == KEPT_STRINGT ||
+            t == KEPT_PTR) {
+
+            *(*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 = 0;
+    size_t bytes = 0;
+
+    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 = PMALLOC(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 = 0;
+    size_t bytes = 0;
+    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 = PMALLOC(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) {
+            PFREE((void *) *str);
+        }
+
+        e->flags &= ~mask;
+
+        if (chars == 0) {
+            *str = 0;
+        } else {
+            wchar_t * s;
+
+            bytes = (chars + 1) * sizeof(wchar_t);
+            s = PMALLOC(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) {
+    if (!e)
+        return;
+
+    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 * __cdecl
+kherr_reportf(const wchar_t * long_desc_fmt, ...) {
+    va_list vl;
+    wchar_t buf[1024];
+    kherr_event * e;
+
+    va_start(vl, long_desc_fmt);
+    StringCbVPrintf(buf, sizeof(buf), long_desc_fmt, vl);
+#ifdef DEBUG
+    OutputDebugString(buf);
+#endif
+    va_end(vl);
+
+    e = kherr_report(KHERR_DEBUG_1,
+                     NULL, NULL, NULL, buf, NULL, 0,
+                     KHERR_SUGGEST_NONE, _vnull(), _vnull(), _vnull(), _vnull(),
+                     KHERR_RF_CSTR_LONG_DESC
+#ifdef _WIN32
+                     ,NULL
+#endif
+                     );
+    if (e) {
+        kherr_evaluate_event(e);
+    }
+
+    return e;
+}
+
+KHMEXP kherr_event * __cdecl
+kherr_reportf_ex(enum kherr_severity severity,
+                 const wchar_t * facility,
+                 khm_int32 facility_id,
+#ifdef _WIN32
+                 HMODULE hModule,
+#endif
+                 const wchar_t * long_desc_fmt, ...) {
+    va_list vl;
+    wchar_t buf[1024];
+    kherr_event * e;
+
+    va_start(vl, long_desc_fmt);
+    StringCbVPrintf(buf, sizeof(buf), long_desc_fmt, vl);
+#ifdef DEBUG
+    OutputDebugString(buf);
+#endif
+    va_end(vl);
+
+    e = kherr_report(severity, NULL, facility, NULL, buf, NULL, facility_id,
+                     KHERR_SUGGEST_NONE,
+                     _vnull(),
+                     _vnull(),
+                     _vnull(),
+                     _vnull(), KHERR_RF_CSTR_LONG_DESC
+#ifdef _WIN32
+                     ,hModule
+#endif
+                     );
+    if (e) {
+        kherr_evaluate_event(e);
+    }
+
+    return e;
+}
+
+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. */
+        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 = PWCSDUP((wchar_t *)parm_data(p));
+        return kherr_val(KEPT_STRINGT, (khm_ui_8) 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;
+
+       e = QBOTTOM(c);
+       if (e && !(e->flags & KHERR_RF_COMMIT)) {
+           notify_ctx_event(KHERR_CTX_EVTCOMMIT, c);
+           e->flags |= KHERR_RF_COMMIT;
+       }
+
+        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_event * KHMAPI kherr_get_prev_event(kherr_event * e)
+{
+    kherr_event * ee;
+
+    EnterCriticalSection(&cs_error);
+    ee = QPREV(e);
+    LeaveCriticalSection(&cs_error);
+
+    return ee;
+}
+
+KHMEXP kherr_event * KHMAPI kherr_get_last_event(kherr_context * c)
+{
+    kherr_event * e;
+    EnterCriticalSection(&cs_error);
+    e = QBOTTOM(c);
+    LeaveCriticalSection(&cs_error);
+    return e;
+}
+
+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 _vnull();
+
+    if (FAILED(StringCbLength(s, KHERR_MAXCB_STRING, &cb_s)))
+        cb_s = KHERR_MAXCB_STRING;
+    else
+        cb_s += sizeof(wchar_t);
+
+    dest = PMALLOC(cb_s);
+    assert(dest != NULL);
+    dest[0] = L'\0';
+
+    StringCbCopy(dest, cb_s, s);
+
+    return _tstr(dest);
+}
+
+
+#if 0
+KHMEXP kherr_param kherr_val(khm_octet ptype, khm_ui_8 pvalue) {
+    kherr_param p;
+    p.type = ptype;
+    p.data = pvalue;
+
+    return p;
+}
+#endif
index 7950587646b2ba0c988621076d96e87c6ba5c1e9..fff3d5031ed1c10cd722143d5eadc7b0700c4fda 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KHERR_H\r
-#define __KHIMAIRA_KHERR_H\r
-\r
-/*! \defgroup kherr NetIDMgr Error Reporting\r
-\r
-    Error reporting functions provide a mechanism to construct\r
-    meaningful and user friendly error reports for the user.\r
-\r
-    Unlike most of the other NetIDMgr API's, the error reporting APIs\r
-    are lightweight and usually do not return an error value.  This is\r
-    mostly because, these functions are called \b after an error\r
-    occurs.\r
-\r
- @{*/\r
-#include<khdefs.h>\r
-#include<khlist.h>\r
-\r
-/*! \name Customizable macros\r
-@{ */\r
-#ifndef KHERR_FACILITY\r
-/*! \brief The default facility when reporting errors\r
-\r
-    When including this header file, if the KHERR_FACILITY macro is\r
-    defined to be a wide character string, then it will be used as the\r
-    default facility when for the convenience macros.  All of the\r
-    calls to the convenience macros in the source file would then have\r
-    that facility.\r
-\r
-    If left undefined, the convenience macros will leave the facility\r
-    value undefined.\r
- */ \r
-#define KHERR_FACILITY NULL\r
-#endif\r
-\r
-#ifndef KHERR_FACILITY_ID\r
-/*! \brief The default facility ID when reporting errors\r
-\r
-    When including this header file, if the KHERR_FACILITY_ID macro is\r
-    defined to be non-zero, then it will be used as the default\r
-    facility identifier for the convenience macros.  All of the calls\r
-    to the convenience macros in the source file would then have that\r
-    facility identifier.\r
-\r
-    The default value of 0 means that the facility is undefined.\r
- */\r
-#define KHERR_FACILITY_ID 0\r
-#endif\r
-\r
-/*! \define KHERR_HMODULE (undefined)\r
-    \brief The default module handle\r
-\r
-    When including this header file, if the KHERR_HMODULE macro is\r
-    defined to be an identifier that holds the module handle, then the\r
-    convenience macros that specify a module handle will use it.\r
-\r
-    A default value is not defined for KHERR_HMODULE.  Any attempt to\r
-    invoke any of the convenience macros that use it should generate a\r
-    compile time error.\r
- */\r
-#ifdef _WIN32\r
-#ifndef KHERR_HMODULE\r
-#endif\r
-#endif\r
-/*@}*/\r
-\r
-/*! \brief Parameter types\r
- */\r
-enum kherr_parm_types {\r
-    KEPT_NONE = 0,\r
-    KEPT_INT32 = 1,\r
-    KEPT_UINT32,\r
-    KEPT_INT64,\r
-    KEPT_UINT64,\r
-    KEPT_STRINGC,               /*!< String constant */\r
-    KEPT_STRINGT,               /*!< String.  Will be freed using\r
-                                  free() when the event is freed */\r
-    KEPT_PTR                    /*!< Pointer type. */\r
-};\r
-\r
-\r
-typedef struct tag_kherr_param {\r
-    khm_octet type;\r
-    khm_ui_8  data;\r
-} kherr_param;\r
-\r
-/*! \brief Severity levels\r
-\r
-    Larger the value, the less severe it is.\r
-*/\r
-enum tag_kherr_severity {\r
-  KHERR_FATAL = 0,              /*!< Fatal error.*/\r
-  KHERR_ERROR,                  /*!< Non-fatal error.  We'll probably\r
-                                 survive.  See the suggested action. */\r
-  KHERR_WARNING,                /*!< Warning. Something almost broke\r
-                                 or soon will.  See the suggested\r
-                                 action. */\r
-  KHERR_INFO,                   /*!< Informational. Something happened\r
-                                  that we would like you to know\r
-                                  about. */\r
-  KHERR_DEBUG_1 = 64,           /*!< Verbose debug level 1 (high)\r
-                                 Events at this severity level are\r
-                                 not required to be based on\r
-                                 localized strings. */\r
-  KHERR_DEBUG_2 = 65,           /*!< Verbose debug level 2 (medium)\r
-                                 Events at this severity level are\r
-                                 not required to be based on\r
-                                 localized strings. */\r
-  KHERR_DEBUG_3 = 66,           /*!< Verbose debug level 3 (low)\r
-                                 Events at this severity level are\r
-                                 not required to be based on\r
-                                 localized strings. */\r
-  KHERR_RESERVED_BANK = 127,    /*!< Internal use */\r
-  KHERR_NONE = 128              /*!< Nothing interesting has happened\r
-                                  so far */\r
-};\r
-\r
-typedef enum tag_kherr_severity kherr_severity;\r
-\r
-/*! \brief Suggestions */\r
-enum tag_kherr_suggestion {\r
-    KHERR_SUGGEST_NONE = 0,     /*!< No suggestions.  */\r
-    KHERR_SUGGEST_ABORT,        /*!< Abort whatever it was you were\r
-                                 trying.  It's not gonna work. */\r
-    KHERR_SUGGEST_RETRY,        /*!< Retry.  It might work the second\r
-                                 or third time over */\r
-    KHERR_SUGGEST_IGNORE,       /*!< Ignore. It might go away. */\r
-    KHERR_SUGGEST_INTERACT,     /*!< Further user interaction is\r
-                                  necessary to resolve the situation.\r
-                                  The suggest string in the event\r
-                                  should be prompted to the user. */\r
-    KHERR_SUGGEST_OTHER,        /*!< Something else. */\r
-};\r
-\r
-typedef enum tag_kherr_suggestion kherr_suggestion;\r
-\r
-/*! \brief An event */\r
-typedef struct tag_kherr_event {\r
-    khm_int32   magic;          /*!< Magic number.  Always set to\r
-                                  KHERR_EVENT_MAGIC */\r
-    DWORD       thread_id;      /*!< The thread which reported this\r
-                                  event. */\r
-    const wchar_t * short_desc; /*!< Short description or title\r
-                                   (localized) */\r
-    const wchar_t * facility;   /*!< Facility name of the reporter\r
-                                  (not localized) */\r
-    const wchar_t * location;   /*!< Location.  Usually the function\r
-                                 name or such of where the event\r
-                                 occured (not localized) */\r
-    const wchar_t * long_desc;  /*!< A long description of what went\r
-                                 wrong (localized, formatted) */\r
-    const wchar_t * suggestion; /*!< A suggested way to fix it\r
-                                 (localized,formatted) */\r
-\r
-    kherr_severity   severity;  \r
-                                /*!< Severity level.  One of the\r
-                                 severity levels listed in\r
-                                 enumeration ::kherr_severity */\r
-    khm_int32   facility_id;    /*!< Left to the application to\r
-                                 interpret */\r
-    kherr_suggestion suggestion_id; \r
-                                /*!< One of the suggestion ID's from\r
-                                 the enumeration\r
-                                 ::kherr_suggestion */\r
-\r
-    int         flags;          /*!< Flags. */\r
-\r
-    kherr_param p1;             /*!< Parameter 1 for formatting */\r
-    kherr_param p2;             /*!< Parameter 2 for formatting */\r
-    kherr_param p3;             /*!< Parameter 3 for formatting */\r
-    kherr_param p4;             /*!< Parameter 4 for formatting */\r
-\r
-    DWORD       time_ticks;     /*!< Time at which event was reported\r
-                                  (as returned by GetTickCount(). */\r
-    FILETIME    time_ft;        /*!< Time at which event was reported.\r
-                                  Current system time as FILETIME. */\r
-\r
-#ifdef _WIN32\r
-    HMODULE     h_module;       /*!< Handle to the module which should\r
-                                  resolve any unresolved resources\r
-                                  references above.  */\r
-#endif\r
-\r
-    LDCL(struct tag_kherr_event);\r
-} kherr_event;\r
-\r
-#define KHERR_EVENT_MAGIC 0x0423e84f\r
-\r
-/*! \brief Flags for kherr_event\r
-\r
-    Each set of flags that define the type of resource for one value\r
-    is mutually exclusive.\r
- */\r
-enum kherr_event_flags {\r
-    KHERR_RF_CSTR_SHORT_DESC= 0x00000000, \r
-                                /*!< Short description is a constant\r
-                                  string */\r
-    KHERR_RF_RES_SHORT_DESC = 0x00000001, \r
-                                /*!< Short description is a string\r
-                                  resource */\r
-    KHERR_RF_MSG_SHORT_DESC = 0x00000002, \r
-                                /*!< Short description is a message\r
-                                  resource */\r
-    KHERR_RF_FREE_SHORT_DESC= 0x00000004, \r
-                                /*!< Short description is an allocated\r
-                                  string */\r
-    KHERR_RFMASK_SHORT_DESC = 0x00000007,\r
-\r
-    KHERR_RF_CSTR_LONG_DESC = 0x00000000, \r
-                                /*!< Long description is a constant\r
-                                  string */\r
-    KHERR_RF_RES_LONG_DESC  = 0x00000008, \r
-                                /*!< Long description is a string\r
-                                  resource */\r
-    KHERR_RF_MSG_LONG_DESC  = 0x00000010, \r
-                                /*!< Long description is a message\r
-                                  resouce  */\r
-    KHERR_RF_FREE_LONG_DESC = 0x00000020, \r
-                                /*!< Long description is an allocated\r
-                                  string */\r
-    KHERR_RFMASK_LONG_DESC  = 0x00000038,\r
-\r
-    KHERR_RF_CSTR_SUGGEST   = 0x00000000, \r
-                                /*!< Suggestion is a constant\r
-                                  string */\r
-    KHERR_RF_RES_SUGGEST    = 0x00000040, \r
-                                /*!< Suggestion is a string\r
-                                  resource */\r
-    KHERR_RF_MSG_SUGGEST    = 0x00000080, \r
-                                /*!< Suggestion is a message\r
-                                  resource */\r
-    KHERR_RF_FREE_SUGGEST   = 0x00000100,  \r
-                                /*!< Suggestion is an allocated\r
-                                  string */\r
-    KHERR_RFMASK_SUGGEST    = 0x000001C0,\r
-\r
-    KHERR_RF_STR_RESOLVED   = 0x00010000,\r
-                                /*!< The string resources in the event\r
-                                  have been resolved. */\r
-    KHERR_RF_CONTEXT_FOLD   = 0x00020000,\r
-                                /*!< The event is a representation of\r
-                                  a folded context. */\r
-\r
-    KHERR_RF_INERT          = 0x00040000,\r
-                                /*!< Inert event.  The event has\r
-                                  already been dealt with and is no\r
-                                  longer considered significant. */\r
-    KHERR_RF_COMMIT         = 0x00080000\r
-                               /*!< Committed event.  The commit\r
-                                 handlers for this event have already\r
-                                 been called. */\r
-};\r
-\r
-/*! \brief Serial number for error contexts */\r
-typedef khm_ui_4 kherr_serial;\r
-\r
-/*! \brief An error context\r
-*/\r
-typedef struct tag_kherr_context {\r
-    khm_int32      magic;       /*!< Magic number. Always set to\r
-                                  KHERR_CONTEXT_MAGIC */\r
-\r
-    kherr_serial   serial;      /*!< Context instance serial number.\r
-                                  Context objects themselves may be\r
-                                  reused for different contexts as\r
-                                  they are freed and reallocated.\r
-                                  However every instance of a context\r
-                                  is guaranteed to have a unique\r
-                                  serial number as specified in this\r
-                                  field.  If an external entity wants\r
-                                  to keep track of the context, it\r
-                                  should keep track of the serial\r
-                                  number as well as the pointer to the\r
-                                  context object. */\r
-\r
-    kherr_severity severity;    \r
-                               /*!< Severity level.  One of the\r
-                                 severity levels listed below. This\r
-                                 is the severity level of the context\r
-                                 and is the maximum severity level of\r
-                                 all the events in the queue of\r
-                                 events. */\r
-\r
-    khm_int32      flags;       /*!< Flags.  Used internally. */\r
-    khm_ui_4       refcount;    /*!< Reference count. Used\r
-                                  internally */\r
-\r
-    kherr_event    *desc_event; /*!< Description event. The event that\r
-                                  describes the error context.  This\r
-                                  points to an event that is not in\r
-                                  the event queue. */\r
-\r
-    kherr_event    *err_event;  /*!< Significant event.  The last one\r
-                                 that caused the severity level to be\r
-                                 what it is right now.  This points\r
-                                 to an event that is listed in the\r
-                                 event queue for this context.*/\r
-\r
-    khm_ui_4 progress_num;      /*!< Progress numerator */\r
-    khm_ui_4 progress_denom;    /*!< Progress denominator */\r
-\r
-    TDCL(struct tag_kherr_context);\r
-    QDCL(struct tag_kherr_event);\r
-} kherr_context;\r
-\r
-#define KHERR_CONTEXT_MAGIC 0x34f3238c\r
-\r
-enum kherr_context_flags {\r
-    KHERR_CF_NONE          = 0x00000000,\r
-                                /*!< None. */\r
-\r
-    KHERR_CF_DIRTY         = 0x00000001,\r
-                                /*!< Used Internally.  Denotes that\r
-                                  the err_event and severity may need\r
-                                  to be recalculated.  Cannot be set\r
-                                  as an initial flag. */\r
-\r
-    KHERR_CF_OWN_PROGRESS  = 0x00000002,\r
-                                /*!< The context maintains its own\r
-                                  progress meter as opposed to one\r
-                                  that is derived from child\r
-                                  contexts. */\r
-\r
-    KHERR_CF_UNBOUND       = 0x00000004,\r
-                                /*!< Unbound context.  The context\r
-                                  can't be used to log events.  Call\r
-                                  kherr_push_context() to associate\r
-                                  the context with the global context\r
-                                  hierarchy. Cannot be set as an\r
-                                  initial flag. */\r
-\r
-    KHERR_CF_TRANSITIVE    = 0x00000008,\r
-                                /*!< Transitive. The context is\r
-                                  automatically made the current\r
-                                  context for all other threads that\r
-                                  handle messages sent or posted by\r
-                                  threads whose current error context\r
-                                  is this one. */\r
-\r
-    KHERR_CFMASK_INITIAL = 0x0000000a,\r
-                                /*!< Allowed initial flags */\r
-};\r
-\r
-/*! \brief Maximum length of a string field in characters including terminating NULL\r
- */\r
-#define KHERR_MAXCCH_STRING 1024\r
-\r
-/*! \brief Maximum length of a string field in bytes including terminating NULL\r
- */\r
-#define KHERR_MAXCB_STRING (KHERR_MAXCCH_STRING * sizeof(wchar_t))\r
-\r
-/*! \brief Context event\r
-\r
-    \see kherr_add_ctx_handler()\r
-*/\r
-enum kherr_ctx_event {\r
-    KHERR_CTX_BEGIN  = 0x0001,  /*!< A new context was created */\r
-    KHERR_CTX_DESCRIBE=0x0002,  /*!< A context was described */\r
-    KHERR_CTX_END    = 0x0004,  /*!< A context was closed */\r
-    KHERR_CTX_ERROR  = 0x0008,  /*!< A context switched to an error\r
-                                  state */\r
-    KHERR_CTX_EVTCOMMIT = 0x0010 /*!< A event was committed into the\r
-                                  context */\r
-};\r
-\r
-/*! \brief Context event handler\r
-\r
-    Context event handlers are invoked when specific events occur with\r
-    respect to an error context.  The ::kherr_ctx_event parameter\r
-    specifies which event occurred using one of the event values\r
-    described in the enumeration.  The error context in which this\r
-    event occurred is specified by the ::kherr_context pointer.\r
-\r
-    Note that if the handler needs to keep track of the error context\r
-    for later processing, it also needs to keep track of the \a serial\r
-    field of the error context.  The same context object may be\r
-    reused, but the serial number is guaranteed to be unique.\r
-\r
-    \see kherr_add_ctx_handler()\r
- */\r
-typedef void (KHMAPI * kherr_ctx_handler)(enum kherr_ctx_event, \r
-                                         kherr_context *);\r
-\r
-/*! \brief Add a context event handler\r
-\r
-    An application can register an event handler that gets notified of\r
-    events that pertain to error contexts.  More than one handler can\r
-    be registered.  The order in which the handlers are called is\r
-    undefined for any specific event.\r
-\r
-    These event occur in the context of individual application\r
-    threads.  The handler will be called from within the thread that\r
-    caused the event.  Therefore it is important that the handler is\r
-    both reentrant and returns quickly.\r
-\r
-    The events that the handler will be notified of are explained\r
-    below:\r
-\r
-    <b>KHERR_CTX_BEGIN</b>: Notification that a new context was\r
-    created.  A pointer to the context will be supplied to the\r
-    handler.  The supplied pointer should not be used to obtain a hold\r
-    on the context, as it will prevent the context from being closed.\r
-\r
-    <b>KHERR_CTX_DESCRIBE</b>: The thread called\r
-    kherr_set_desc_event() to set the description of a context.  Once\r
-    again, the pointer should not be used to obtain a hold on the\r
-    context.\r
-\r
-    <b>KHERR_CTX_ERROR</b>: The last event that was reported for the\r
-    context was an error event (the severity was was equal or higher\r
-    than KHERR_ERROR).  The pointer may be used to obtain a hold on\r
-    the context.  However, it is the application's resonsibility to\r
-    make sure that the hold is released later.  Otherwise the event\r
-    will never be closed.\r
-\r
-    <b>KHERR_CTX_END</b>: Closure.  This event is signalled when the\r
-    last open handle to the context is closed and there is no thread\r
-    that is currently active which has this context in its error\r
-    context stack.  At the time the handler is invoked, the context is\r
-    still intact.  The pointer that is supplied should not be used to\r
-    obtain a handle on the context.\r
-\r
-    <b>KHERR_CTX_EVTCOMMIT</b>: An event was committed into the error\r
-    context.  An event is committed when another event is reported\r
-    after the event, or if the context is closed.  Since the last\r
-    event that is reported can still be modified by adding new\r
-    information, the event remains open until it is no longer the last\r
-    event or the context is no longer active.  When this notification\r
-    is received, the last event in the context's event queue is the\r
-    event that was committed.\r
-\r
-    \param[in] h Context event handler, of type ::kherr_ctx_handler\r
-\r
-    \param[in] filter A combination of ::kherr_ctx_event values\r
-        indication which notifications should be sent to the handler.\r
-        If a \a filter value of zero is provided, all of the events\r
-        will be sent to the handler.\r
-\r
-    \param[in] serial The serial number of the error context that\r
-        should be tracked.  If this is zero, all error contexts can\r
-        trigger the handler.\r
- */\r
-KHMEXP void KHMAPI kherr_add_ctx_handler(kherr_ctx_handler h, \r
-                                         khm_int32 filter,\r
-                                         kherr_serial serial);\r
-\r
-/*! \brief Remove a context event handler\r
-\r
-    Undoes what was done with kherr_add_ctx_handler()\r
-\r
-    \see kherr_add_ctx_handler()\r
- */\r
-KHMEXP void KHMAPI kherr_remove_ctx_handler(kherr_ctx_handler h,\r
-                                            kherr_serial serial);\r
-\r
-\r
-/*! \brief Report an error\r
-\r
-    Creates an event, fills in the details specified in the arguments,\r
-    and adds it to the current error context.\r
-\r
-    If the current thread does not have an error context, no reporting\r
-    happens.  However, if any of the supplied strings or parameters\r
-    are marked as allocated, they will be freed before the function\r
-    returns.\r
-\r
-    Certain parameters that expect strings can instead be given string\r
-    resources, message resources or allocated strings in addition to\r
-    constant string.  By default, the parameters are expected to be\r
-    constant strings.\r
-\r
-    <b>Allocated strings</b>: The application can allocate memory for\r
-    a string.  Since the application is not notified when the event is\r
-    no longer used and freed, it \b must indicate that the string is\r
-    an allocated string by setting the appropriate flag in the \a\r
-    flags parameter.  When the event is no longer used, the memory\r
-    pointed to by the relevant pointer will be freed through a call to\r
-    free().  Not all string parameters take allocated strings.  See\r
-    individual parameter documentation for details.\r
-\r
-    <b>String resources</b>: On WIN32, string resources can be passed\r
-    in to kherr_report() using the MAKEINTRESOURCE macro.  However,\r
-    the application \b must specify that the parameter is a string\r
-    resource using the appropriate flag in the \a flags parameter.\r
-    The error reporting engine will expand the string against the\r
-    module handle passed in the \a h_module parameter when the value\r
-    of the string is required.  Not all string parameters take string\r
-    resources.  See individual parameter documentation for details.\r
-    Strings loaded through string resources cannot be longer than\r
-    ::KHERR_MAXCCH_STRING in characters inclusive of terminating NULL.\r
-\r
-    <b>Message resources</b>: On WIN32, message resources can be\r
-    passed in to kherr_report() by specifying the message ID where it\r
-    ordinarily expects a pointer to a constant string.  The\r
-    application \b must indicate that the string is a message resource\r
-    by using the appropriate flag in the \a flags parameter.  When the\r
-    value of the string is needed, it is expanded against the module\r
-    handle passed in the \a h_module parameter using the message ID.\r
-    Not all string parameters take message resources.  See individual\r
-    parameter documentation for details.  Note that the facility and\r
-    severity values associated with a message resource are ignored.\r
-    Strings loaded through message resources cannot be longer than\r
-    ::KHERR_MAXCCH_STRING in characters inclusive of terminating NULL.\r
-\r
-    <b>Formatted fields</b>: Parameters that are formatted can have\r
-    can have parameter inserts like in printf(). However, specifying\r
-    inserts is different from printf() and follows the conventions\r
-    used in WIN32 API FormatMessage().  This is because for localized\r
-    strings, the order of the parameters in the string may be\r
-    different.  See the documentation for FormatMessage() for details\r
-    on the format string.  The same set of parameters (i.e. \a p1, \a\r
-    p2, \a p3, \a p4) is used for all formatted strings with\r
-    appropriate marshalling for 64 bit types.  The size of the string\r
-    after expansion must not exceed 65536 bytes inclusive of\r
-    terminating NULL.\r
-\r
-    \param[in] severity One of ::kherr_severity_level\r
-    \param[in] short_desc Short description or title (localized).  Can\r
-        be a string resource, message resource, allocated string or\r
-        constant string.  The \a flags parameter should indicate the\r
-        type of string used.\r
-    \param[in] facility Facility name of the reporter (not localized)\r
-    \param[in] location Usually the function name or such of where the\r
-        event occured (not localized)\r
-    \param[in] long_desc Long description of event (localized,\r
-        formatted). Can be a string resource, message resource,\r
-        allocated string or constant string.  The \a flags parameter\r
-        should indicate the type of string used.\r
-    \param[in] suggestion Suggested action to correct situation, if\r
-        applicable (localized). Can be a string resource, message\r
-        resource, allocated string or constant string.  The \a flags\r
-        parameter should indicate the type of string used.\r
-    \param[in] facility_id Identifier of facility.  Application\r
-        defined.\r
-    \param[in] suggestion_id One of the suggestion identifiers from\r
-        ::kherr_suggestion_ids\r
-    \param[in] p1 First parameter. Used for formatting.\r
-    \param[in] p2 Second parameter. Used for formatting.\r
-    \param[in] p3 Third parameter. Used for formatting.\r
-    \param[in] p4 Fourth parameter. Used for formatting.\r
-    \param[in] flags Flags.  See ::kherr_report_flags\r
-    \param[in] h_module Handle to a module that resolves any string or\r
-        message resources used for the \a short_description , \a\r
-        long_desc or \a suggestion parameters.  This parameter is only\r
-        available on WIN32.\r
-\r
-    \note With the exception of parameters of type KEPT_STRINGT and\r
-        parameters which are flagged for freeing using the \a flags\r
-        parameter, all other string parameters are assumed to be\r
-        pointers to constant strings.  The strings are not copied and\r
-        the pointers are used as is.  Also, no clean-up is performed\r
-        when the event is freed other than that implied by \a flags.\r
- */\r
-KHMEXP kherr_event * KHMAPI kherr_report(\r
-    enum kherr_severity severity,\r
-    const wchar_t * short_desc,\r
-    const wchar_t * facility,\r
-    const wchar_t * location,\r
-    const wchar_t * long_desC,\r
-    const wchar_t * suggestion,\r
-    khm_int32 facility_id,\r
-    enum kherr_suggestion suggestion_id,\r
-    kherr_param p1,\r
-    kherr_param p2,\r
-    kherr_param p3,\r
-    kherr_param p4,\r
-    khm_int32 flags\r
-#ifdef _WIN32\r
-    ,HMODULE  h_module\r
-#endif\r
-);\r
-\r
-/*! \brief Report a formatted message\r
-\r
-    The format string \a long_desc_fmt should be a string constant and\r
-    the format specifiers follow that of \a sprintf.  This creates an\r
-    event with the long description set to the expansion of the format\r
-    string against the arguments.\r
- */\r
-KHMEXP kherr_event * __cdecl\r
-kherr_reportf_ex(enum kherr_severity severity,\r
-                 const wchar_t * facility,\r
-                 khm_int32 facility_id,\r
-#ifdef _WIN32\r
-                 HMODULE hModule,\r
-#endif\r
-                 const wchar_t * long_desc_fmt,\r
-                 ...);\r
-#define _reportf_ex kherr_reportf_ex\r
-\r
-/*! \brief Report a formatted message\r
-\r
-    The format string \a long_desc_fmt should be a string constant and\r
-    the format specifiers follow that of \a sprintf.  This creates an\r
-    event with the long description set to the expansion of the format\r
-    string against the arguments.\r
- */\r
-KHMEXP kherr_event * __cdecl\r
-kherr_reportf(const wchar_t * long_desc_fmt,\r
-              ...);\r
-#define _reportf kherr_reportf\r
-\r
-/*! \brief Create a parameter out of a transient string\r
-\r
-    A parameter is created by duplicating the string that is passed\r
-    into the function.  If the string exceeds KHERR_MAXCCH_STRING,\r
-    then only the first part of the string that fits within the limit\r
-    is duplicated.\r
-\r
-    The resulign ::kherr_param must be passed in to kherr_report().\r
-    The event logging framework will free the duplicated string once\r
-    the data is no longer required.\r
- */\r
-KHMEXP kherr_param kherr_dup_string(const wchar_t * s);\r
-\r
-__inline KHMEXP kherr_param\r
-kherr_val(khm_octet ptype, khm_ui_8 pvalue) {\r
-    kherr_param p;\r
-\r
-    p.type = ptype;\r
-    p.data = pvalue;\r
-\r
-    return p;\r
-}\r
-\r
-#define _int32(i)   kherr_val(KEPT_INT32, (khm_ui_8) i)\r
-#define _uint32(ui) kherr_val(KEPT_UINT32, (khm_ui_8) ui)\r
-#define _int64(i)   kherr_val(KEPT_INT64, (khm_ui_8) i)\r
-#define _uint64(ui) kherr_val(KEPT_UINT64, (khm_ui_8) ui)\r
-#define _cstr(cs)   kherr_val(KEPT_STRINGC, (khm_ui_8) cs)\r
-#define _tstr(ts)   kherr_val(KEPT_STRINGT, (khm_ui_8) ts)\r
-#define _cptr(p)    kherr_val(KEPT_PTR, (khm_ui_8) p)\r
-#define _vnull()    kherr_val(KEPT_NONE, 0)\r
-#define _dupstr(s)  kherr_dup_string(s)\r
-\r
-/* convenience macros for calling kherr_report */\r
-#ifdef KHERR_HMODULE\r
-\r
-#define _report_cs0(severity, long_description)                 \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, _vnull(), _vnull(), _vnull(), _vnull(), 0, KHERR_HMODULE)\r
-\r
-#define _report_cs1(severity, long_description, p1)             \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, p1, _vnull(), _vnull(), _vnull(), 0, KHERR_HMODULE)\r
-\r
-#define _report_cs2(severity, long_description, p1, p2)         \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, p1, p2, _vnull(), _vnull(), 0, KHERR_HMODULE)\r
-\r
-#define _report_cs3(severity, long_description, p1, p2, p3)     \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, p1, p2, p3, _vnull(), 0, KHERR_HMODULE)\r
-\r
-#define _report_cs4(severity, long_description, p1, p2, p3, p4) \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, p1, p2, p3, p4, 0, KHERR_HMODULE)\r
-\r
-#else\r
-\r
-#define _report_cs0(severity, long_description)                 \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, _vnull(), _vnull(), _vnull(), _vnull(), 0, NULL)\r
-\r
-#define _report_cs1(severity, long_description, p1)             \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, p1, _vnull(), _vnull(), _vnull(), 0, NULL)\r
-\r
-#define _report_cs2(severity, long_description, p1, p2)         \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, p1, p2, _vnull(), _vnull(), 0, NULL)\r
-\r
-#define _report_cs3(severity, long_description, p1, p2, p3)     \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, p1, p2, p3, _vnull(), 0, NULL)\r
-\r
-#define _report_cs4(severity, long_description, p1, p2, p3, p4) \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, p1, p2, p3, p4, 0, NULL)\r
-#endif /* !defined(KHERR_HMODULE) */\r
-\r
-#ifdef _WIN32\r
-#define _report_sr0(severity, long_desc_id)                     \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, MAKEINTRESOURCE(long_desc_id), NULL, KHERR_FACILITY_ID, 0, _vnull(), _vnull(), _vnull(), _vnull(), KHERR_RF_RES_LONG_DESC, KHERR_HMODULE)\r
-\r
-#define _report_sr1(severity, long_desc_id, p1)                 \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, MAKEINTRESOURCE(long_desc_id), NULL, KHERR_FACILITY_ID, 0, p1, _vnull(), _vnull(), _vnull(), KHERR_RF_RES_LONG_DESC, KHERR_HMODULE)\r
-\r
-#define _report_sr2(severity, long_desc_id, p1, p2)             \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, MAKEINTRESOURCE(long_desc_id), NULL, KHERR_FACILITY_ID, 0, p1, p2, _vnull(), _vnull(), KHERR_RF_RES_LONG_DESC, KHERR_HMODULE)\r
-\r
-#define _report_sr3(severity, long_desc_id, p1, p2, p3)         \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, MAKEINTRESOURCE(long_desc_id), NULL, KHERR_FACILITY_ID, 0, p1, p2, p3, _vnull(), KHERR_RF_RES_LONG_DESC, KHERR_HMODULE)\r
-\r
-#define _report_sr4(severity, long_desc_id, p1, p2, p3, p4)     \\r
-    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)\r
-#endif\r
-\r
-#ifdef _WIN32\r
-#define _report_mr0(severity, long_desc_msg_id)                     \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, (wchar_t *)(long_desc_msg_id), NULL, KHERR_FACILITY_ID, 0, _vnull(), _vnull(), _vnull(), _vnull(), KHERR_RF_MSG_LONG_DESC, KHERR_HMODULE)\r
-\r
-#define _report_mr1(severity, long_desc_msg_id, p1)                 \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, (wchar_t *)(long_desc_msg_id), NULL, KHERR_FACILITY_ID, 0, p1, _vnull(), _vnull(), _vnull(), KHERR_RF_MSG_LONG_DESC, KHERR_HMODULE)\r
-\r
-#define _report_mr2(severity, long_desc_msg_id, p1, p2)             \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, (wchar_t *)(long_desc_msg_id), NULL, KHERR_FACILITY_ID, 0, p1, p2, _vnull(), _vnull(), KHERR_RF_MSG_LONG_DESC, KHERR_HMODULE)\r
-\r
-#define _report_mr3(severity, long_desc_msg_id, p1, p2, p3)         \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, (wchar_t *)(long_desc_msg_id), NULL, KHERR_FACILITY_ID, 0, p1, p2, p3, _vnull(), KHERR_RF_MSG_LONG_DESC, KHERR_HMODULE)\r
-\r
-#define _report_mr4(severity, long_desc_msg_id, p1, p2, p3, p4)     \\r
-    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)\r
-#endif\r
-\r
-#define _report_ts0(severity, long_desc_ptr)                     \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_desc_ptr), NULL, KHERR_FACILITY_ID, 0, _vnull(), _vnull(), _vnull(), _vnull(), KHERR_RF_FREE_LONG_DESC, NULL)\r
-\r
-#define _report_ts1(severity, long_desc_ptr, p1)                 \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_desc_ptr), NULL, KHERR_FACILITY_ID, 0, p1, _vnull(), _vnull(), _vnull(), KHERR_RF_FREE_LONG_DESC, NULL)\r
-\r
-#define _report_ts2(severity, long_desc_ptr, p1, p2)             \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_desc_ptr), NULL, KHERR_FACILITY_ID, 0, p1, p2, _vnull(), _vnull(), KHERR_RF_FREE_LONG_DESC, NULL)\r
-\r
-#define _report_ts3(severity, long_desc_ptr, p1, p2, p3)         \\r
-    kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_desc_ptr), NULL, KHERR_FACILITY_ID, 0, p1, p2, p3, _vnull(), KHERR_RF_FREE_LONG_DESC, NULL)\r
-\r
-#define _report_ts4(severity, long_desc_ptr, p1, p2, p3, p4)     \\r
-    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)\r
-\r
-/*! \brief Set the suggestion and suggestion identifier for the last event\r
-\r
-    The event that will be modified is the last event reported by the\r
-    calling thread.\r
- */\r
-KHMEXP void KHMAPI kherr_suggest(wchar_t * suggestion, khm_int32 suggestion_id, khm_int32 flags);\r
-#define _suggest_cs(cs,sid) kherr_suggest((cs), (sid), KHERR_RF_CSTR_SUGGEST)\r
-#define _suggest_ts(ts,sid) kherr_suggest((ts), (sid), KHERR_RF_FREE_SUGGEST)\r
-#define _suggest_sr(sr,sid) kherr_suggest(MAKEINTRESOURCE(sr), (sid), KHERR_RF_RES_SUGGEST)\r
-#define _suggest_mr(mr,sid) kherr_suggest((wchar_t *)(DWORD_PTR)(mr), (sid), KHERR_RF_MSG_SUGGEST)\r
-\r
-/*! \brief Set the location string for the last event\r
-\r
-    The event that will be modified is the last event reported by the\r
-    calling thread.\r
- */\r
-KHMEXP void KHMAPI kherr_location(wchar_t * location);\r
-#define _location(l) kherr_location(l)\r
-\r
-/*! \brief Set the facility string and identifier for the last event\r
-\r
-    The event that will be modified is the last event reported by the\r
-    calling thread.\r
- */\r
-KHMEXP void KHMAPI kherr_facility(wchar_t * facility, khm_int32 facility_id);\r
-#define _facility(f,fid) kherr_facility((f),(fid))\r
-\r
-/*! \brief Marks the last event as the descriptor event for the current error context\r
-\r
-    Note that marking an event as the descriptor event has the effect\r
-    of removing the event from event queue.  The event will henceforth\r
-    be used as the descriptor for the context.  The only effective\r
-    fields of a descriptor event are \a short_desc, \a long_desc, \a\r
-    facility, \a facility_id and the parameters which are used for\r
-    resolving formatted strings in the aforementioned fields.\r
-\r
-    Upon calling kherr_set_desc_event(), the event will be\r
-    automatically evaluated as if kherr_evaluate_event() was called.\r
-\r
-    The event that will be referenced is the last event reported by\r
-    the calling thread.\r
- */\r
-KHMEXP void KHMAPI kherr_set_desc_event(void);\r
-#define _describe kherr_set_desc_event\r
-\r
-/*! \brief Delete the last event\r
-\r
-    The event that will be deleted is the last event reported by the\r
-    calling thread.\r
- */\r
-KHMEXP void KHMAPI kherr_del_last_event(void);\r
-#define _del_event kherr_del_last_event\r
-\r
-/*! \brief Create a new context\r
-\r
-    The created context is not bound to any thread or any context\r
-    hierarchy.  Hence it cannot be used to capture any events until it\r
-    is used in a call to kherr_push_context().\r
-\r
-    Release the returned context pointer with a call to\r
-    kherr_release_context().\r
-\r
-    \param[in] flags Initial flags for the context. Combination of\r
-        ::kherr_context_flags\r
-\r
-    \note This function is for internal use only.\r
- */\r
-KHMEXP kherr_context * KHMAPI kherr_create_new_context(khm_int32 flags);\r
-\r
-/*! \brief Obtain a hold on a context */\r
-KHMEXP void KHMAPI kherr_hold_context(kherr_context * c);\r
-\r
-/*! \brief Release a context */\r
-KHMEXP void KHMAPI kherr_release_context(kherr_context * c);\r
-\r
-/*! \brief Push an empty context\r
-\r
-    Creates an empty context, adds it as a child of the current\r
-    thread's error context.  If the current thread does not have an\r
-    error context, then the created error context will be a root level\r
-    context.\r
-\r
-    The new context will be the current error context for the calling\r
-    thread.\r
-\r
-    \param[in] flags Initial flags for the context. Combination of\r
-        ::kherr_context_flags\r
-\r
-    \see kherr_push_new_context() for more information about thread\r
-        specific context stacks.\r
-\r
- */\r
-KHMEXP void KHMAPI kherr_push_new_context(khm_int32 flags);\r
-#define _begin_task kherr_push_new_context\r
-\r
-/*! \brief Push a context\r
-\r
-    Each thread has a stack of error contexts.  The topmost one is\r
-    current.  The thread can push or pop contexts on to the stack\r
-    independently of the hierarchy of contexts (the only exception, as\r
-    explained below is when the context that is being pushed is\r
-    unbound).\r
-\r
-    If the context being pushed by kherr_push_context() is unbound,\r
-    then it will be attached to the current context of the thread as a\r
-    child.  Once the new context is pushed to the top of the stack, it\r
-    will become the current context for the thread.\r
-\r
-    The calling thread must call kherr_pop_context() to remove the\r
-    context from the top of the stack.  Each call to\r
-    kherr_push_new_context() or kher_push_context() must have a\r
-    corresponding kherr_pop_context() call.\r
-\r
-    When the thread terminates, all of the contexts in the thread's\r
-    context stack will be automatically removed.\r
-\r
-    \see kherr_pop_context()\r
- */\r
-KHMEXP void KHMAPI kherr_push_context(kherr_context * c);\r
-\r
-/*! \brief Pop a context\r
-\r
-    Remove the current error context from the thread's context stack.\r
-    If no other open handles exist to the error context, this causes\r
-    the error context to collapse into it's parent context or vanish\r
-    entirely unless the context contains an error.\r
-\r
-    \see kherr_push_context() for more information about thread\r
-        specific context stacks.\r
- */\r
-KHMEXP void KHMAPI kherr_pop_context(void);\r
-#define _end_task kherr_pop_context\r
-\r
-/*! \brief Retrieve the current error context\r
-\r
-    The returned pointer must be released with a call to\r
-    kherr_release_context().\r
-*/\r
-KHMEXP kherr_context * KHMAPI kherr_peek_context(void);\r
-\r
-/*! \brief Check if the current error context indicates an error\r
-\r
-    \return TRUE if there is an error. FALSE otherwise.\r
-    \see kherr_analyze()\r
- */\r
-KHMEXP khm_boolean KHMAPI kherr_is_error(void);\r
-\r
-/*! \brief Check if an error context indicates an error\r
-\r
-    \return TRUE if there is an error. FALSE otherwise.\r
-    \see kherr_analyze()\r
- */\r
-KHMEXP khm_boolean KHMAPI kherr_is_error_i(kherr_context * c);\r
-\r
-/*! \brief Clear the error state of the current context */\r
-KHMEXP void KHMAPI kherr_clear_error(void);\r
-\r
-/*! \brief Clear the error state of an error context */\r
-KHMEXP void KHMAPI kherr_clear_error_i(kherr_context * c);\r
-\r
-/*! \brief Set the progress meter of the current error context\r
-\r
-    Setting \a denom to zero removes the progress meter.\r
- */\r
-KHMEXP void KHMAPI kherr_set_progress(khm_ui_4 num, khm_ui_4 denom);\r
-#define _progress(num,denom) kherr_set_progress((num),(denom))\r
-\r
-/*! \brief Get the progress meter of the current error context\r
- */\r
-KHMEXP void KHMAPI kherr_get_progress(khm_ui_4 * num, khm_ui_4 * denom);\r
-\r
-/*! \brief Get the progress meter of an error context\r
- */\r
-KHMEXP void KHMAPI kherr_get_progress_i(kherr_context * c, khm_ui_4 * num, khm_ui_4 * denom);\r
-\r
-/*! \brief Get the first event in a context\r
-\r
-    The returned pointer is only valid as long as there is a hold on\r
-    \a c.  Once the context is released with a call to\r
-    kherr_release_context() all pointers to events in the context\r
-    become invalid.\r
-\r
-    In addition, the last event in a context may still be "active".  A\r
-    thread can still modify the last event as long as the context is\r
-    active.\r
-\r
-    \see kherr_get_next_event(), kherr_get_prev_event(),\r
-    kherr_get_last_event()\r
- */\r
-KHMEXP kherr_event * KHMAPI kherr_get_first_event(kherr_context * c);\r
-\r
-/*! \brief Get the next event\r
-\r
-    Call kherr_get_first_event() to obtain the first event in a\r
-    context.  Subsequent calls to kherr_get_next_event() will yield\r
-    other events in the order in which they were reported.  The list\r
-    ends when kherr_get_next_event() returns NULL.\r
-\r
-    The returned pointer is only valid as long as there is a hold on\r
-    \a c.  Once the context is released with a call to\r
-    kherr_release_context() all pointers to events in the context\r
-    become invalid.\r
-\r
-    In addition, the last event in a context may still be "active".  A\r
-    thread can still modify the last event as long as the context is\r
-    active.\r
-\r
-    \see kherr_get_first_event(), kherr_get_prev_event(),\r
-    kherr_get_last_event()\r
- */\r
-KHMEXP kherr_event * KHMAPI kherr_get_next_event(kherr_event * e);\r
-\r
-/*! \brief Get the previous event\r
-\r
-    Returns a pointer to the event that was reported in the context\r
-    containing \a e prior to \a e being reported.\r
-\r
-    The returned pointer is only valid as long as there is a hold on\r
-    the error context.  Once the context is released with a call to\r
-    kherr_release_context() all pointers to events in the context\r
-    become invalid.\r
-\r
-    In addition, the last event in a context may still be "active". A\r
-    thread can still modify the last event as long as the context is\r
-    active.\r
-\r
-    \see kherr_get_first_event(), kherr_get_next_event(),\r
-    kherr_get_last_event()\r
- */\r
-KHMEXP kherr_event * KHMAPI kherr_get_prev_event(kherr_event * e);\r
-\r
-/*! \brief Get the last event in an error context\r
-\r
-    Returns a pointer to the last error event that that was reported\r
-    to the context \a c.\r
-\r
-    The returned pointer is only valid as long as there is a hold on\r
-    the error context.  Once the context is released with a call to\r
-    kherr_release_context(), all pointers to events in the context\r
-    become invalid.\r
-\r
-    In addtion, the last event in a context may still be "active".  A\r
-    thread can still modify the last event as long as the context is\r
-    active.\r
-\r
-    \see kherr_get_first_event(), kherr_get_next_event(),\r
-    kherr_get_prev_event()\r
- */\r
-KHMEXP kherr_event * KHMAPI kherr_get_last_event(kherr_context * c);\r
-\r
-/*! \brief Get the first child context of a context\r
-\r
-    Contexts are arranged in a hiearchy.  This function returns the\r
-    first child of an error context.  Use kherr_get_next_context() to\r
-    obtain the other contexts.  If \a c is \a NULL, this returns the\r
-    first root level context.\r
-\r
-    The returned pointer must be released with a call to\r
-    kherr_release_context()\r
- */\r
-KHMEXP kherr_context * KHMAPI kherr_get_first_context(kherr_context * c);\r
-\r
-/*! \brief Get the next sibling context of a context\r
-\r
-    The returned pointer must be released with a call to\r
-    kherr_release_context()\r
-\r
-    \see kherr_get_first_context()\r
- */\r
-KHMEXP kherr_context * KHMAPI kherr_get_next_context(kherr_context * c);\r
-\r
-/*! \brief Get the desciption event for the context\r
-\r
-    The description event is the event that was denoted using\r
-    kherr_set_desc_event() as the event which describes the context.\r
-\r
-    The returned pointer is only valid as long as there is a hold on\r
-    \a c.  Once the context is released with a call to\r
-    kherr_release_context() all pointers to events in the context\r
-    becomes invalid.\r
- */\r
-KHMEXP kherr_event * KHMAPI kherr_get_desc_event(kherr_context * c);\r
-\r
-/*! \brief Get the error event for the context\r
-\r
-    The error event for a context is the last event that had the\r
-    highest severity level.\r
-\r
-    The returned pointer is only valid as long as there is a hold on\r
-    \a c.  Once the context is released with a call to\r
-    kherr_release_context() all pointers to events in the context\r
-    becomes invalid.\r
- */\r
-KHMEXP kherr_event * KHMAPI kherr_get_err_event(kherr_context * c);\r
-\r
-/*! \brief Evaluate an event\r
-\r
-    When an event is reported, all the parameters and resource\r
-    references that were passed to kherr_report() are kept as-is until\r
-    the actual string values are required by the error reporting\r
-    library.  However, if the string fields are required before then,\r
-    an application can call kherr_evaluate_event() to get them.\r
-\r
-    This function does the following:\r
-\r
-    - Load any referenced string or message resources that are\r
-      referenced in the event's short description, long description or\r
-      suggestion.\r
-\r
-    - Expand any inserts using the parameters that were passed in.\r
-\r
-    - Free up allocated strings in for the descriptions or suggestion\r
-      fields and any parameters.\r
-\r
-    - Update the string fields in the event to contain the newly\r
-      generated strings.\r
-\r
- */\r
-KHMEXP void KHMAPI kherr_evaluate_event(kherr_event * e);\r
-\r
-/*! \brief Evaluate the last event\r
-\r
-    Same as kherr_evaluate_event(), but operates on the last event\r
-    logged by the current thread.\r
-\r
-    \see kherr_evaluate_event()\r
- */\r
-KHMEXP void KHMAPI kherr_evaluate_last_event(void);\r
-#define _resolve kherr_evaluate_last_event\r
-\r
-/*! \defgroup kherr_fids Standard Facility IDs\r
-@{*/\r
-#define KHM_FACILITY_KMM       1\r
-#define KHM_FACILITY_KCDB      2\r
-#define KHM_FACILITY_UI        3\r
-#define KHM_FACILITY_KRB5      64\r
-#define KHM_FACILITY_KRB4      65\r
-#define KHM_FACILITY_AFS       66\r
-#define KHM_FACILITY_USER      128\r
-/*@}*/\r
-\r
-/*@}*/\r
-\r
-/* In debug mode, outputs the formatted string to the debug console */\r
-#ifdef DEBUG\r
-KHMEXP void kherr_debug_printf(wchar_t * fmt, ...);\r
-#endif\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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_NONE = 0,
+    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 */
+    KEPT_PTR                    /*!< Pointer type. */
+};
+
+
+typedef struct tag_kherr_param {
+    khm_octet type;
+    khm_ui_8  data;
+} kherr_param;
+
+/*! \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_1 = 64,           /*!< Verbose debug level 1 (high)
+                                 Events at this severity level are
+                                 not required to be based on
+                                 localized strings. */
+  KHERR_DEBUG_2 = 65,           /*!< Verbose debug level 2 (medium)
+                                 Events at this severity level are
+                                 not required to be based on
+                                 localized strings. */
+  KHERR_DEBUG_3 = 66,           /*!< Verbose debug level 3 (low)
+                                 Events at this severity level are
+                                 not required to be based on
+                                 localized strings. */
+  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. */
+    KHERR_RF_COMMIT         = 0x00080000
+                               /*!< Committed event.  The commit
+                                 handlers for this event have already
+                                 been called. */
+};
+
+/*! \brief Serial number for error contexts */
+typedef khm_ui_4 kherr_serial;
+
+/*! \brief An error context
+*/
+typedef struct tag_kherr_context {
+    khm_int32      magic;       /*!< Magic number. Always set to
+                                  KHERR_CONTEXT_MAGIC */
+
+    kherr_serial   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 */
+    KHERR_CTX_EVTCOMMIT = 0x0010 /*!< A event was committed into the
+                                  context */
+};
+
+/*! \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 (KHMAPI * 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.
+
+    <b>KHERR_CTX_EVTCOMMIT</b>: An event was committed into the error
+    context.  An event is committed when another event is reported
+    after the event, or if the context is closed.  Since the last
+    event that is reported can still be modified by adding new
+    information, the event remains open until it is no longer the last
+    event or the context is no longer active.  When this notification
+    is received, the last event in the context's event queue is the
+    event that was committed.
+
+    \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.
+
+    \param[in] serial The serial number of the error context that
+        should be tracked.  If this is zero, all error contexts can
+        trigger the handler.
+ */
+KHMEXP void KHMAPI kherr_add_ctx_handler(kherr_ctx_handler h, 
+                                         khm_int32 filter,
+                                         kherr_serial serial);
+
+/*! \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,
+                                            kherr_serial serial);
+
+
+/*! \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 Report a formatted message
+
+    The format string \a long_desc_fmt should be a string constant and
+    the format specifiers follow that of \a sprintf.  This creates an
+    event with the long description set to the expansion of the format
+    string against the arguments.
+ */
+KHMEXP kherr_event * __cdecl
+kherr_reportf_ex(enum kherr_severity severity,
+                 const wchar_t * facility,
+                 khm_int32 facility_id,
+#ifdef _WIN32
+                 HMODULE hModule,
+#endif
+                 const wchar_t * long_desc_fmt,
+                 ...);
+#define _reportf_ex kherr_reportf_ex
+
+/*! \brief Report a formatted message
+
+    The format string \a long_desc_fmt should be a string constant and
+    the format specifiers follow that of \a sprintf.  This creates an
+    event with the long description set to the expansion of the format
+    string against the arguments.
+ */
+KHMEXP kherr_event * __cdecl
+kherr_reportf(const wchar_t * long_desc_fmt,
+              ...);
+#define _reportf kherr_reportf
+
+/*! \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);
+
+__inline KHMEXP kherr_param
+kherr_val(khm_octet ptype, khm_ui_8 pvalue) {
+    kherr_param p;
+
+    p.type = ptype;
+    p.data = pvalue;
+
+    return p;
+}
+
+#define _int32(i)   kherr_val(KEPT_INT32, (khm_ui_8) i)
+#define _uint32(ui) kherr_val(KEPT_UINT32, (khm_ui_8) ui)
+#define _int64(i)   kherr_val(KEPT_INT64, (khm_ui_8) i)
+#define _uint64(ui) kherr_val(KEPT_UINT64, (khm_ui_8) ui)
+#define _cstr(cs)   kherr_val(KEPT_STRINGC, (khm_ui_8) cs)
+#define _tstr(ts)   kherr_val(KEPT_STRINGT, (khm_ui_8) ts)
+#define _cptr(p)    kherr_val(KEPT_PTR, (khm_ui_8) p)
+#define _vnull()    kherr_val(KEPT_NONE, 0)
+#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, _vnull(), _vnull(), _vnull(), _vnull(), 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, _vnull(), _vnull(), _vnull(), 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, _vnull(), _vnull(), 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, _vnull(), 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, _vnull(), _vnull(), _vnull(), _vnull(), 0, NULL)
+
+#define _report_cs1(severity, long_description, p1)             \
+    kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, p1, _vnull(), _vnull(), _vnull(), 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, _vnull(), _vnull(), 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, _vnull(), 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, _vnull(), _vnull(), _vnull(), _vnull(), 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, _vnull(), _vnull(), _vnull(), 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, _vnull(), _vnull(), 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, _vnull(), 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, _vnull(), _vnull(), _vnull(), _vnull(), 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, _vnull(), _vnull(), _vnull(), 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, _vnull(), _vnull(), 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, _vnull(), 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, _vnull(), _vnull(), _vnull(), _vnull(), 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, _vnull(), _vnull(), _vnull(), 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, _vnull(), _vnull(), 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, _vnull(), 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
+    become invalid.
+
+    In addition, the last event in a context may still be "active".  A
+    thread can still modify the last event as long as the context is
+    active.
+
+    \see kherr_get_next_event(), kherr_get_prev_event(),
+    kherr_get_last_event()
+ */
+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
+    become invalid.
+
+    In addition, the last event in a context may still be "active".  A
+    thread can still modify the last event as long as the context is
+    active.
+
+    \see kherr_get_first_event(), kherr_get_prev_event(),
+    kherr_get_last_event()
+ */
+KHMEXP kherr_event * KHMAPI kherr_get_next_event(kherr_event * e);
+
+/*! \brief Get the previous event
+
+    Returns a pointer to the event that was reported in the context
+    containing \a e prior to \a e being reported.
+
+    The returned pointer is only valid as long as there is a hold on
+    the error context.  Once the context is released with a call to
+    kherr_release_context() all pointers to events in the context
+    become invalid.
+
+    In addition, the last event in a context may still be "active". A
+    thread can still modify the last event as long as the context is
+    active.
+
+    \see kherr_get_first_event(), kherr_get_next_event(),
+    kherr_get_last_event()
+ */
+KHMEXP kherr_event * KHMAPI kherr_get_prev_event(kherr_event * e);
+
+/*! \brief Get the last event in an error context
+
+    Returns a pointer to the last error event that that was reported
+    to the context \a c.
+
+    The returned pointer is only valid as long as there is a hold on
+    the error context.  Once the context is released with a call to
+    kherr_release_context(), all pointers to events in the context
+    become invalid.
+
+    In addtion, the last event in a context may still be "active".  A
+    thread can still modify the last event as long as the context is
+    active.
+
+    \see kherr_get_first_event(), kherr_get_next_event(),
+    kherr_get_prev_event()
+ */
+KHMEXP kherr_event * KHMAPI kherr_get_last_event(kherr_context * c);
+
+/*! \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
+/*@}*/
+
+/*@}*/
+
+/* In debug mode, outputs the formatted string to the debug console */
+#ifdef DEBUG
+KHMEXP void kherr_debug_printf(wchar_t * fmt, ...);
+#endif
+
+#endif
index da33c596e222ff754573a0693a31424f6b4d0844..fb6412aa24ee6fdab18e7d68dd9bbe2f9e24b3b0 100644 (file)
@@ -1,69 +1,69 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KHERRORINTERNAL_H\r
-#define __KHIMAIRA_KHERRORINTERNAL_H\r
-\r
-#include<windows.h>\r
-#include<kherr.h>\r
-#include<utils.h>\r
-#include<strsafe.h>\r
-\r
-typedef struct tag_kherr_thread {\r
-    khm_size nc_ctx;\r
-    khm_size n_ctx;\r
-    kherr_context ** ctx;\r
-} kherr_thread;\r
-\r
-#define THREAD_STACK_SIZE 8\r
-\r
-typedef struct tag_kherr_handler_node {\r
-    khm_int32         filter;\r
-    kherr_ctx_handler h;\r
-    kherr_serial      serial;\r
-} kherr_handler_node;\r
-\r
-#define CTX_ALLOC_INCR 4\r
-\r
-#define EVENT_MASK_UNRESOLVED \\r
-    (KHERR_RF_RES_SHORT_DESC|KHERR_RF_MSG_SHORT_DESC| \\r
-    KHERR_RF_RES_LONG_DESC|KHERR_RF_MSG_LONG_DESC| \\r
-    KHERR_RF_RES_SUGGEST|KHERR_RF_MSG_SUGGEST)\r
-\r
-extern CRITICAL_SECTION cs_error;\r
-extern DWORD tls_error;\r
-extern kherr_context * ctx_free_list;\r
-extern kherr_event * evt_free_list;\r
-extern kherr_handler_node * ctx_handlers;\r
-extern khm_size n_ctx_handlers;\r
-\r
-#define parm_type(p) ((p).type)\r
-#define parm_data(p) ((p).data)\r
-\r
-void resolve_event_strings(kherr_event *);\r
-void attach_this_thread(void);\r
-void detach_this_thread(void);\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<utils.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_serial      serial;
+} 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) ((p).type)
+#define parm_data(p) ((p).data)
+
+void resolve_event_strings(kherr_event *);
+void attach_this_thread(void);
+void detach_this_thread(void);
+#endif
index 0ae2292046194a9c78522b6c0c0d94c9e2d9d833..c7afd7bc25e43784b6209c835927e17148a92b7c 100644 (file)
@@ -1,52 +1,52 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kherrinternal.h>\r
-\r
-void\r
-kherr_process_attach(void) {\r
-    InitializeCriticalSection(&cs_error);\r
-    tls_error = TlsAlloc();\r
-}\r
-\r
-void\r
-kherr_process_detach(void) {\r
-    TlsFree(tls_error);\r
-    DeleteCriticalSection(&cs_error);\r
-}\r
-\r
-void\r
-kherr_thread_attach(void) {\r
-            /* We don't call attach_this_thread() here since we only\r
-               want to create a context stack for this thread if\r
-               someone wants one. */\r
-            /* attach_this_thread(); */\r
-}\r
-\r
-void\r
-kherr_thread_detach(void) {\r
-            detach_this_thread();\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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();
+}
index 5e955953dfcdd05a96d572a648d2e0b0268b4390..721865c4d242b9d113a0d575c7e2e2c26394f497 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kmminternal.h>\r
-#include<assert.h>\r
-\r
-khm_boolean kmmint_load_locale_lib(kmm_module_i * m, kmm_module_locale * l)\r
-{\r
-    HMODULE h;\r
-\r
-    if(l->filename != NULL) {\r
-        wchar_t path[MAX_PATH];\r
-        DWORD dw;\r
-\r
-        /* construct the path name */\r
-        assert(m->h_module != NULL);\r
-\r
-        dw = PathIsFileSpec(l->filename);\r
-\r
-        assert(dw);\r
-        if (!dw)\r
-            return FALSE;\r
-\r
-        dw = GetModuleFileName(m->h_module, path, ARRAYLENGTH(path));\r
-        assert(dw != 0);\r
-        if (dw == 0)\r
-            return FALSE;\r
-\r
-        PathRemoveFileSpec(path);\r
-        dw = PathAppend(path, l->filename);\r
-        assert(dw);\r
-        if (!dw)\r
-            return FALSE;\r
-\r
-        h = LoadLibrary(path);\r
-        if(!h)\r
-            return FALSE;\r
-\r
-        EnterCriticalSection(&cs_kmm);\r
-        m->h_resource = h;\r
-        m->lcid_resource = l->language;\r
-        LeaveCriticalSection(&cs_kmm);\r
-\r
-        return TRUE;\r
-\r
-    } else {\r
-        /*  in this case, the language resources are assumed to be in the\r
-            main module library itself. */\r
-\r
-        EnterCriticalSection(&cs_kmm);\r
-        m->h_resource = m->h_module;\r
-        m->lcid_resource = l->language;\r
-        LeaveCriticalSection(&cs_kmm);\r
-\r
-        return TRUE;\r
-    }\r
-}\r
-\r
-\r
-KHMEXP khm_int32 KHMAPI kmm_set_locale_info(kmm_module module, kmm_module_locale * locales, khm_int32 n_locales)\r
-{\r
-    kmm_module_i * m;\r
-    LANGID lcid;\r
-    int i;\r
-    int * f;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    m = kmm_module_from_handle(module);\r
-\r
-    if(!m || m->state != KMM_MODULE_STATE_INIT)\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-\r
-    if(!locales || n_locales < 0)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    f = PMALLOC(n_locales * sizeof(int));\r
-    if(!f)\r
-        return KHM_ERROR_UNKNOWN;\r
-    ZeroMemory(f, sizeof(int) * n_locales);\r
-\r
-    lcid = GetUserDefaultLangID();\r
-\r
-    /* first search for an exact match */\r
-    for(i=0; i<n_locales; i++) {\r
-        if(locales[i].language == lcid) {\r
-            f[i] = TRUE;\r
-            if(kmmint_load_locale_lib(m, &locales[i]))\r
-                break;\r
-        }\r
-    }\r
-\r
-    if(i<n_locales)\r
-        goto _exit;\r
-\r
-    /* ok, that didn't work.  Try an inexact match. */\r
-    for(i=0; i<n_locales; i++) {\r
-        if(!f[i] && (PRIMARYLANGID(locales[i].language) == PRIMARYLANGID(lcid))) {\r
-            f[i] = TRUE;\r
-            if(kmmint_load_locale_lib(m,&locales[i]))\r
-                break;\r
-        }\r
-    }\r
-\r
-    if(i < n_locales)\r
-        goto _exit;\r
-\r
-    /* hmm. no matches yet. just try to locate the default locale */\r
-    for(i=0; i<n_locales; i++) {\r
-        if(!f[i] && (locales[i].flags & KMM_MLOC_FLAG_DEFAULT)) {\r
-            f[i] = TRUE;\r
-            if(kmmint_load_locale_lib(m,&locales[i]))\r
-                break;\r
-        }\r
-    }\r
-\r
-    if(i < n_locales)\r
-        goto _exit;\r
-\r
-    /* give up */\r
-    rv = KHM_ERROR_NOT_FOUND;\r
-\r
-_exit:\r
-    PFREE(f);\r
-    return rv;\r
-}\r
-\r
-#ifdef _WIN32\r
-KHMEXP HMODULE     KHMAPI kmm_get_resource_hmodule(kmm_module vm)\r
-{\r
-    if(!kmm_is_module(vm))\r
-        return NULL;\r
-    else\r
-        return (kmm_module_from_handle(vm))->h_resource;\r
-}\r
-#endif\r
-\r
-KHMEXP kmm_module KHMAPI\r
-kmm_this_module(void) {\r
-    kmm_plugin_i * p;\r
-    kmm_module_i * m;\r
-    kmm_module vm;\r
-\r
-    p = TlsGetValue(tls_kmm);\r
-    if (!kmm_is_plugin(p))\r
-        return NULL;\r
-\r
-    m = p->module;\r
-    vm = kmm_handle_from_module(m);\r
-\r
-    kmm_hold_module(vm);\r
-\r
-    return vm;\r
-}\r
-\r
-KHMEXP kmm_plugin KHMAPI\r
-kmm_this_plugin(void) {\r
-    kmm_plugin_i * p;\r
-    kmm_plugin vp;\r
-\r
-    p = TlsGetValue(tls_kmm);\r
-    if (!kmm_is_plugin(p))\r
-        return NULL;\r
-\r
-    vp = kmm_handle_from_plugin(p);\r
-\r
-    kmm_hold_plugin(vp);\r
-\r
-    return vp;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+#include<assert.h>
+
+khm_boolean kmmint_load_locale_lib(kmm_module_i * m, kmm_module_locale * l)
+{
+    HMODULE h;
+
+    if(l->filename != NULL) {
+        wchar_t path[MAX_PATH];
+        DWORD dw;
+
+        /* construct the path name */
+        assert(m->h_module != NULL);
+
+        dw = PathIsFileSpec(l->filename);
+
+        assert(dw);
+        if (!dw)
+            return FALSE;
+
+        dw = GetModuleFileName(m->h_module, path, ARRAYLENGTH(path));
+        assert(dw != 0);
+        if (dw == 0)
+            return FALSE;
+
+        PathRemoveFileSpec(path);
+        dw = PathAppend(path, l->filename);
+        assert(dw);
+        if (!dw)
+            return FALSE;
+
+        h = LoadLibrary(path);
+        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_PARAM;
+
+    f = PMALLOC(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(kmmint_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(kmmint_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(kmmint_load_locale_lib(m,&locales[i]))
+                break;
+        }
+    }
+
+    if(i < n_locales)
+        goto _exit;
+
+    /* give up */
+    rv = KHM_ERROR_NOT_FOUND;
+
+_exit:
+    PFREE(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;
+}
index d10f2150e634c0f50f8666ded344b1519090822a..735f006add451f26c0d5c0943ddaafd61fea88d5 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KMM_H\r
-#define __KHIMAIRA_KMM_H\r
-\r
-#include<khdefs.h>\r
-#include<kmq.h>\r
-\r
-/*! \defgroup kmm NetIDMgr Module Manager\r
-@{*/\r
-\r
-/*! \brief A handle to a module.\r
-*/\r
-typedef khm_handle kmm_module;\r
-\r
-/*! \brief A handle to a plugin.\r
- */\r
-typedef khm_handle kmm_plugin;\r
-\r
-/*! \name Limits\r
-  @{*/\r
-\r
-/*! \brief Maximum number of characters in a name in KMM including the terminating NULL */\r
-#define KMM_MAXCCH_NAME 256\r
-\r
-/*! \brief Maximum number of bytes in a name in KMM including the terminating NULL */\r
-#define KMM_MAXCB_NAME (sizeof(wchar_t) * KMM_MAXCCH_NAME)\r
-\r
-/*! \brief Maximum number of characters in a description in KMM including the terminating NULL */\r
-#define KMM_MAXCCH_DESC 512\r
-\r
-/*! \brief Maximum number of bytes in a description in KMM including the terminating NULL */\r
-#define KMM_MAXCB_DESC (sizeof(wchar_t) * KMM_MAXCCH_NAME)\r
-\r
-/*! \brief Maximum number of characters in a vendor string in KMM including the terminating NULL */\r
-#define KMM_MAXCCH_VENDOR 256\r
-\r
-/*! \brief Maximum number of bytes in a vendor string in KMM including the terminating NULL */\r
-#define KMM_MAXCB_VENDOR (sizeof(wchar_t) * KMM_MAXCCH_VENDOR)\r
-\r
-/*! \brief Maximum number of characters in a support URI in KMM including the terminating NULL */\r
-#define KMM_MAXCCH_SUPPORT 256\r
-\r
-/*! \brief Maximum number of bytes in a vendor string in KMM including the terminating NULL */\r
-#define KMM_MAXCB_SUPPORT (sizeof(wchar_t) * KMM_MAXCCH_SUPPORT)\r
-\r
-/*! \brief Maximum number of dependencies per plugin\r
-*/\r
-#define KMM_MAX_DEPENDENCIES    8\r
-\r
-/*! \brief Maximum number of dependants per plugin\r
- */\r
-#define KMM_MAX_DEPENDANTS      32\r
-\r
-/*! \brief Maximum number of characters a dependency string including trailing double NULL */\r
-#define KMM_MAXCCH_DEPS (KMM_MAXCCH_NAME * KMM_MAX_DEPENDENCIES + 1)\r
-\r
-/*! \brief Maximum number of bytes in a dependency string including trailing double NULL */\r
-#define KMM_MAXCB_DEPS (sizeof(wchar_t) * KMM_MAXCCH_DEPS)\r
-/*@}*/ /* Limits */\r
-\r
-/*! \brief Plugin registration\r
-\r
-    \see ::khm_cred_provider\r
-*/\r
-typedef struct tag_kmm_plugin_reg {\r
-    wchar_t *   name;           /*!< Name of the plugin.  Maximum of\r
-                                     KMM_MAXCCH_NAME characters\r
-                                     including the terminating\r
-                                     NULL. Required. */\r
-\r
-    wchar_t *   module;         /*!< Name of module that owns the\r
-                                     plugin. Maximum of\r
-                                     KMM_MAXCCH_NAME characters\r
-                                     including terminating NULL.\r
-                                     Required. */\r
-\r
-    khm_int32   type;           /*!< Type plugin type.  One of\r
-                                     KHM_PITYPE_*. Required. */\r
-    khm_int32   flags;          /*!< Unused. Set to 0 */\r
-    kmq_callback_t msg_proc;    /*!< Message processor.  Required. */\r
-    wchar_t *   dependencies;   /*!< Dependencies.  Note that this is\r
-                                     a multi string.  (you can use the\r
-                                     KHC multi string functions to\r
-                                     manipulate multi strings or to\r
-                                     convert a comma separated list of\r
-                                     dependencies to a multi string).\r
-                                     Each string in the multi string\r
-                                     is a name of a plugin that this\r
-                                     plugin depends on.  Optional (set\r
-                                     to NULL if this plugin has no\r
-                                     dependencies). Maximum of\r
-                                     KMM_MAXCCH_DEPS characters\r
-                                     including terminating double\r
-                                     NULL.*/\r
-\r
-    wchar_t *   description;    /*!< Description of the plugin.\r
-                                     Maximum of KMM_MAXCCH_DESC\r
-                                     characters including the\r
-                                     terminating\r
-                                     NULL. Localized. Optional (set to\r
-                                     NULL if not provided) */\r
-#ifdef _WIN32\r
-    HICON       icon;           /*!< Icon used to represent the\r
-                                     plugin. Optional. (set to NULL if\r
-                                     not provided) */\r
-#endif\r
-} kmm_plugin_reg;\r
-\r
-/*! \brief Plugin information\r
-*/\r
-typedef struct tag_kmm_plugin_info {\r
-    kmm_plugin_reg reg;         /*!< Registration info */\r
-\r
-    khm_int32   state;          /*!< Current status of the plugin.\r
-                                  One of ::_kmm_plugin_states */\r
-\r
-    khm_int32   failure_count;  /*!< Number of recorded failures in\r
-                                    the plugin */\r
-    FILETIME    failure_time;   /*!< Time of first recorded failure */\r
-    khm_int32   failure_reason; /*!< The reason for the first recorded\r
-                                    failure */\r
-\r
-    kmm_plugin  h_plugin;       /*!< Handle to plugin */\r
-\r
-    khm_int32   flags;          /*!< Flags for the plugin. Currently\r
-                                  this can only specify\r
-                                  ::KMM_PLUGIN_FLAG_DISABLED. */\r
-} kmm_plugin_info;\r
-\r
-/*! \brief The plugin is disabled\r
-\r
-    This flag will be set in the \a flags field of the\r
-    ::kmm_plugin_info structure for a plugin that has been marked as\r
-    disabled.  If the plugin is currently running, but marked as\r
-    disabled for future sessions, then this bit will be set in \a\r
-    flags , but the \a state of the plugin will indicate that the\r
-    plugin is running.\r
- */\r
-#define KMM_PLUGIN_FLAG_DISABLED    0x00000400\r
-\r
-/*! \name Plugin types\r
-@{*/\r
-/*! \brief A credentials provider\r
-\r
-    \see \ref pi_pt_cred for more information.\r
- */\r
-#define KHM_PITYPE_CRED     1\r
-\r
-/*! \brief A identity provider \r
-\r
-    \see \ref pi_pt_cred for more information\r
- */\r
-#define KHM_PITYPE_IDENT    2\r
-\r
-/*! \brief A configuration provider\r
-\r
-    \see \ref pi_pt_conf for more information.\r
- */\r
-#define KHM_PITYPE_CONFIG   3\r
-\r
-/*! \brief Undefined plugin type\r
-\r
-    The plugin doesn't provide any credential type.\r
- */\r
-#define KHM_PITYPE_MISC     4\r
-\r
-/*@}*/\r
-\r
-/*! \brief Plugin states */\r
-enum _kmm_plugin_states {\r
-    KMM_PLUGIN_STATE_FAIL_INIT = -6,    /*!< Failed to initialize */\r
-    KMM_PLUGIN_STATE_FAIL_UNKNOWN = -5, /*!< Failed due to unknown\r
-                                          reasons */\r
-    KMM_PLUGIN_STATE_FAIL_MAX_FAILURE = -4, /*!< The plugin has\r
-                                          reached the maximum number\r
-                                          of failures and cannot be\r
-                                          initialized until the\r
-                                          failure count is reset */\r
-    KMM_PLUGIN_STATE_FAIL_NOT_REGISTERED = -3, /*!< Failed because the\r
-                                          plugin was not registered\r
-                                          and automatic registration\r
-                                          failed. */\r
-    KMM_PLUGIN_STATE_FAIL_DISABLED = -2,/*!< Failed because plugin was\r
-                                          disabled by the user. */\r
-    KMM_PLUGIN_STATE_FAIL_LOAD = -1,    /*!< The plugin failed to load\r
-                                          due to some unknown\r
-                                          reason. */\r
-    KMM_PLUGIN_STATE_NONE = 0,          /*!< Unknown state */\r
-    KMM_PLUGIN_STATE_PLACEHOLDER,       /*!< Placeholder.  The plugin\r
-                                          hasn't been provided by\r
-                                          anyone yet, but the plugin\r
-                                          record has been created to\r
-                                          keep track of\r
-                                          dependencies. */\r
-    KMM_PLUGIN_STATE_REG,               /*!< The plugin is registered\r
-                                          but not initialized */\r
-    KMM_PLUGIN_STATE_PREINIT,           /*!< The plugin is in the\r
-                                          process of being\r
-                                          initialized */\r
-    KMM_PLUGIN_STATE_HOLD,              /*!< On hold.  One or more\r
-                                          dependencies of this plugin\r
-                                          has not been resolved */\r
-    KMM_PLUGIN_STATE_INIT,              /*!< The plugin was initialized */\r
-    KMM_PLUGIN_STATE_RUNNING,           /*!< The plugin is running */\r
-    KMM_PLUGIN_STATE_EXITED             /*!< The plugin has been stopped. */\r
-};\r
-\r
-/*! \brief Module registration */\r
-typedef struct tag_kmm_module_reg {\r
-    wchar_t *   name;               /*!< Identifier for the module */\r
-    wchar_t *   path;               /*!< Full pathname to module\r
-                                        binary */\r
-\r
-    wchar_t *   description;        /*!< Description of module */\r
-\r
-    wchar_t *   vendor;             /*!< Vendor/copyright string */\r
-\r
-    wchar_t *   support;            /*!< Support URL/contact */\r
-\r
-    khm_int32   n_plugins;          /*!< Number of plugins that are\r
-                                        active */\r
-    kmm_plugin_reg * plugin_reg_info;  /*!< Array of kmm_plugin_reg\r
-                                        records for each active\r
-                                        plugin */\r
-} kmm_module_reg;\r
-\r
-/*! \brief Module information record */\r
-typedef struct tag_kmm_module_info {\r
-    kmm_module_reg reg;             /*!< Registration info */\r
-\r
-    khm_ui_4    language;           /*!< Currently loaded langugage */\r
-\r
-    khm_int32   state;              /*!< Current status of the\r
-                                        module */\r
-\r
-    khm_version file_version;       /*!< File version for the\r
-                                        module */\r
-    khm_version product_version;    /*!< Product version for the\r
-                                        module */\r
-\r
-    khm_int32   failure_count;      /*!< Number of times the module\r
-                                        has failed to load */\r
-    FILETIME    failure_time;       /*!< Time of first recorded\r
-                                        failure */\r
-    khm_int32   failure_reason;     /*!< Reason for first failure.\r
-                                        One of the module status\r
-                                        values */\r
-\r
-    kmm_module  h_module;           /*!< Handle to the module. */\r
-} kmm_module_info;\r
-\r
-/*! \brief Module states\r
-*/\r
-enum KMM_MODULE_STATES {\r
-    KMM_MODULE_STATE_FAIL_INCOMPAT=-12, /*!< The library containing\r
-                                          the module was not\r
-                                          compatible with this version\r
-                                          of NetIDMgr. */\r
-    KMM_MODULE_STATE_FAIL_INV_MODULE=-11, /*!< The library containing\r
-                                            the module was invalid. */\r
-    KMM_MODULE_STATE_FAIL_UNKNOWN=-10,   /*!< Module could not be\r
-                                          loaded due to unknown\r
-                                          reasons. */\r
-    KMM_MODULE_STATE_FAIL_MAX_FAILURE=-9,/*!< The module has failed\r
-                                          too many times already.  Not\r
-                                          attempting to restart it\r
-                                          again */\r
-    KMM_MODULE_STATE_FAIL_DUPLICATE=-8, /*!< An attempt was made to\r
-                                          load the same module\r
-                                          twice. */\r
-    KMM_MODULE_STATE_FAIL_NOT_REGISTERED=-7, /*!< The module is not\r
-                                          found among the registered\r
-                                          module list */\r
-    KMM_MODULE_STATE_FAIL_NO_PLUGINS=-6,/*!< The module provided no\r
-                                          plugins, or all the plugins\r
-                                          that are provided are\r
-                                          disabled */\r
-    KMM_MODULE_STATE_FAIL_DISABLED=-5,  /*!< Module is disabled and\r
-                                          cannot be loaded */\r
-    KMM_MODULE_STATE_FAIL_LOAD=-4,      /*!< The module failed to\r
-                                          initialize */\r
-    KMM_MODULE_STATE_FAIL_INVALID=-3,   /*!< The module was invalid.\r
-                                          Typically caused by the\r
-                                          required entrypoints not\r
-                                          being present */\r
-    KMM_MODULE_STATE_FAIL_SIGNATURE=-2, /*!< The module failed to load\r
-                                          due to an unverifiable\r
-                                          signature */\r
-    KMM_MODULE_STATE_FAIL_NOT_FOUND=-1, /*!< The module was not\r
-                                          found */\r
-    KMM_MODULE_STATE_NONE=0,            /*!< Unknown state. The handle\r
-                                          is possibly invalid */\r
-    KMM_MODULE_STATE_PREINIT,           /*!< The module is being\r
-                                          loaded. init_module() hasn't\r
-                                          been called yet */\r
-    KMM_MODULE_STATE_INIT,              /*!< In init_module() */\r
-    KMM_MODULE_STATE_INITPLUG,          /*!< Initializing plugins */\r
-    KMM_MODULE_STATE_RUNNING,           /*!< Running */\r
-    KMM_MODULE_STATE_EXITPLUG,          /*!< Currently exiting plugins */\r
-    KMM_MODULE_STATE_EXIT,              /*!< Currently exiting */\r
-    KMM_MODULE_STATE_EXITED             /*!< Exited */\r
-};\r
-\r
-/*! \brief Start the Module Manager\r
-\r
-    \note Only called by the NetIDMgr core.\r
-*/\r
-KHMEXP void KHMAPI \r
-kmm_init(void);\r
-\r
-/*! \brief Stop the Module Manager\r
-\r
-    \note Only called by the NetIDMgr core.\r
-*/\r
-KHMEXP void KHMAPI \r
-kmm_exit(void);\r
-\r
-/*! \brief Return the plugin handle for the current plugin\r
-\r
-    The returned handle represents the plugin which owns the current\r
-    thread.  The returned handle must be released by calling\r
-    kmm_release_plugin().  Returns NULL if the current thread is not\r
-    owned by any plugin.\r
- */\r
-KHMEXP kmm_plugin KHMAPI \r
-kmm_this_plugin(void);\r
-\r
-/*! \brief Return the module handle for the current module\r
-\r
-    The returned handle represents the module which owns the current\r
-    thread.  The returned handle must be released by calling\r
-    kmm_release_module()\r
-*/\r
-KHMEXP kmm_module KHMAPI \r
-kmm_this_module(void);\r
-\r
-/*! \name Flags for kmm_load_module()\r
-@{*/\r
-/*!\brief Load synchronously\r
-\r
-    If this flag is set, then the function waits for the module to be\r
-    loaded.  The default is to load the module asynchronously.\r
-\r
-    When loading a module asynchronously, the kmm_load_module()\r
-    function returns KHM_ERROR_SUCCESS and exits without waiting for\r
-    the module to load.  If \a result is not NULL, it will receive a\r
-    valid handle to the module.\r
-\r
-    When loading a module synchronously, kmm_load_module() will wait\r
-    for the module to completely load.  If it fails to load properly,\r
-    it will return an error code and set \a result to NULL.\r
-*/\r
-#define KMM_LM_FLAG_SYNC    1\r
-\r
-/*! \brief Do not load\r
-\r
-    Indicates that the module shouldn't actually be loaded.  If the\r
-    specified module name identifies a module that has already been\r
-    loaded, then the function returns a held handle to the existing\r
-    module (use kmm_release_module() to free the handle).  Otherwise,\r
-    the function returns KHM_ERROR_NOT_FOUND.\r
-*/\r
-#define KMM_LM_FLAG_NOLOAD  2\r
-/*@}*/\r
-\r
-/*! \brief Load a module\r
-\r
-    The \a modulename parameter specifies a module to load.  Depending\r
-    on the configuration, not all of the plugins that are provided by\r
-    the module may be loaded.  If no plugins are successfully loaded,\r
-    the module will be immediately unloaded.\r
-\r
-    If the module is currently loaded or is being loaded, then a valid\r
-    handle to the existing module is returned.\r
-\r
-    When called with KMM_LM_FLAG_SYNC, the function does not return\r
-    until the module and the associated plugins are all initialized,\r
-    or an error occurs.\r
-\r
-    If the KMM_LM_FLAG_NOLOAD flag is set, then a handle to an\r
-    existing instance of the module will be returned.  If the module\r
-    hasn't been loaded yet, then no handle is returned and the\r
-    function returns KHM_ERROR_NOT_FOUND.\r
-\r
-    See the associated NetIDMgr Module Manager documentation on the\r
-    sequence of events associated with loading a module.\r
-\r
-    \param[in] modulename Name of the module.  The module should have\r
-        been registered under this name prior to the call.\r
-    \param[in] flags Combination of KMM_LM_FLAG_*\r
-    \param[out] result Receives a handle to the loaded module.  If the\r
-        result is not required, set this to NULL. If \a result is not\r
-        NULL, and km_load_module() returns KHM_ERROR_SUCCESS, then\r
-        kmm_release_module() must be called to release the handle to\r
-        the module.  Otherwise, \a result receives NULL.  If a handle\r
-        is returned, it will be valid regardless of whether the module\r
-        fails to load or not.  You can use kmm_get_module_state() to\r
-        query the progress of the loading process.  See\r
-        ::KMM_LM_FLAG_SYNC.\r
-\r
-    \retval KHM_ERROR_SUCCESS The call succeeded.  If \a\r
-        KMM_LM_FLAG_SYNC was specified, this means that the module was\r
-        successfully loaded.  Otherwise, it only means that the module\r
-        has been queued up for loading.  Use kmm_get_module_state() to\r
-        determine if it was successfully loaded.  If \a result is not\r
-        NULL, a valid handle is returned.\r
-    \retval KHM_ERROR_EXISTS The module is already loaded or has been\r
-        already queued for loading.  If \a result is not NULL, a valid\r
-        handle to the existing module instance is returned.\r
-    \retval KHM_ERROR_NOT_FOUND If called with KMM_LM_FLAG_NOLOAD,\r
-        indicates that the module has not been loaded.  Otherwise only\r
-        returned when called with KMM_LM_FLAG_SYNC.  The module image\r
-        was not found.  No handle is returned.\r
-    \retval KHM_ERROR_INVALID_SIGNATURE Only returned when called with\r
-        KMM_LM_FLAG_SYNC.  The module was signed with an invalid\r
-        certificate.  No handle is returned.\r
-    \retval KHM_ERROR_UNKNOWN Only returned when called with\r
-        KMM_LM_FLAG_SYNC.  Some other error has occured.  No handle is\r
-        returned.\r
-\r
-    \see \ref pi_fw_pm_load\r
-    \see ::KMM_LM_FLAG_SYNC, ::KMM_LM_FLAG_NOLOAD\r
-*/\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_load_module(wchar_t * modname, khm_int32 flags, kmm_module * result);\r
-\r
-/*! \brief Hold a handle to a module\r
-\r
-    Use kmm_release_module() to release the hold.\r
-*/\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_hold_module(kmm_module module);\r
-\r
-/*! \brief Release a handle to a module\r
-\r
-    Release a held referece to a module that was returned in a call to \r
-    kmm_load_module().\r
-*/\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_release_module(kmm_module m);\r
-\r
-/*! \brief Query the state of a module\r
-\r
-    When loading a module asynchronously you can query the state of\r
-    the loading process using this.  The return value is a status\r
-    indicator.\r
-\r
-    \return The return value is one of the ::KMM_MODULE_STATES\r
-        enumerations.\r
-*/\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_get_module_state(kmm_module m);\r
-\r
-/*! \brief Unload a module\r
-\r
-    See the associated NetIDMgr Module Manager documentation on the\r
-    sequence of events associated with unloading a module.\r
-\r
-    \see \ref pi_fw_pm_unload\r
-*/\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_unload_module(kmm_module module);\r
-\r
-/*! \brief Loads the default modules as specified in the configuration\r
-\r
-    The configuration can specify the default set of modules to load.\r
-    This function dispatches the necessary message for loading these\r
-    modules and reutnrs.\r
-*/\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_load_default_modules(void);\r
-\r
-/*! \brief Checks whether there are any pending loads\r
-\r
-    Returns TRUE if there are modules still waiting to be loaded.\r
-*/\r
-KHMEXP khm_boolean  KHMAPI\r
-kmm_load_pending(void);\r
-\r
-#ifdef _WIN32\r
-\r
-/*! \brief Returns the Windows module handle from a handle to a NetIDMgr module.\r
-    Although it is possible to obtain the Windows module handle and\r
-    use it to call Windows API functions, it is not recommended to do\r
-    so.  This is because that might cause the state of the module to\r
-    change in ways which are inconsistent from the internal data\r
-    structures that kmm maintains.\r
- */\r
-KHMEXP HMODULE     KHMAPI \r
-kmm_get_hmodule(kmm_module m);\r
-#endif\r
-\r
-/*! \brief Hold a plugin\r
-\r
-    Obtains a hold on a plugin.  The plugin handle will remain valid\r
-    until the hold is released with a call to kmm_release_plugin().\r
-    No guarantees are made on the handle once the handle is released.\r
- */\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_hold_plugin(kmm_plugin p);\r
-\r
-/*! \brief Release a plugin\r
-\r
-    Releases a hold on a plugin obtained through a call to\r
-    kmm_hold_plugin().  The plugin handle should no longer be\r
-    considered valied once this is called.\r
- */\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_release_plugin(kmm_plugin p);\r
-\r
-/*! \brief Provide a plugin\r
-\r
-    This function must be called for each plugin that the module\r
-    provides.\r
-\r
-    Note that this function returns immediately and does not\r
-    initialize the plugin.  All plugins that are provided by a\r
-    module will be initialized once the init_module() function\r
-    returns.  If the plugin has dependencies, it will be kept in a\r
-    held state until the plugins that it depends on are successfully\r
-    initialized.  If the dependencies are not resolved (the dependent\r
-    plugins are not loaded), then plugin will not be initialized.\r
-\r
-    If the plugin is not registered and \a plugin contains enough\r
-    information to perform the registration, then it will be\r
-    automatically registered.  However, if the plugin is not\r
-    registered and cannot be registered using the provided\r
-    information, the plugin will not be initialized properly.  Note\r
-    that automatic registration will always register the plugin in the\r
-    user configuration store.\r
-\r
-    The \a name and \a msg_proc members of \a plugin are required to\r
-    have valid values.  The \a icon member may optionally be\r
-    specified.  The other fields can be specified if the plugin should\r
-    be automatically registered, however, the \a module field will be\r
-    ignored and will be determined by the \a module handle.\r
-\r
-    \param[in] module Handle to this module that is providing the plugin.\r
-    \param[in] plugin A plugin descriptor.\r
-\r
-    \retval KHM_ERROR_SUCCESS Succeeded.\r
-    \retval KHM_ERROR_INVALID_OPERATION The function was not called\r
-        during init_module()\r
-    \retval KHM_ERROR_INVALID_PARAM One or more parameters were invalid\r
-    \retval KHM_ERROR_DUPLICATE The plugin was already provided\r
-\r
-    \note This can only be called when handing init_module()\r
-*/\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_provide_plugin(kmm_module module, kmm_plugin_reg * plugin);\r
-\r
-/*! \brief Query the state of a plugin.\r
-\r
-    \return One of ::_kmm_plugin_states\r
-*/\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_get_plugin_state(wchar_t * plugin);\r
-\r
-/*! \defgroup kmm_reg Registration\r
-\r
-    The functions for managing plugin and module registration.  These\r
-    functions are also available as static linked libraries for use by\r
-    external applications which must register or unregister plugins or\r
-    modules.\r
-@{*/\r
-\r
-/*! \brief Obtain the configuration space for a named plugin\r
-\r
-    Note that the named plugin does not have to actually exist.\r
-    Configuration spaces for plugins are based solely on the plugin\r
-    name and hence can be accessed regardless of whether the specific\r
-    plugin is loaded or not.\r
-\r
-    \param[in] flags Controls the options for opening the\r
-        configuration space.  If KHM_FLAG_CREATE is specified, then\r
-        the configuration space for the plugin named \a plugin wil be\r
-        created if it doesn't already exist.  The \a flags parameter\r
-        is directly passed into a call to khc_open_space().\r
-\r
-    \param[in] plugin Name of the plugin.  The name can not contain\r
-        slashes.\r
-\r
-    \param[out] result Receives a configuration space handle.  The\r
-        calling application should free the handle using\r
-        khc_close_space().\r
-\r
-    \see khc_open_space()\r
-    \see khc_close_space()\r
- */\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_get_plugin_config(wchar_t * plugin, khm_int32 flags, khm_handle * result);\r
-\r
-/*! \brief Obtain the configuration space or a named module\r
-\r
-    The named module does not have to actually exist.  Configuration\r
-    spaces for modules are based on the basename of the module\r
-    (including the extension).\r
-\r
-    \param[in] module Name of the module.\r
-\r
-    \param[in] flags The flags used to call khc_open_space().  You can\r
-        use this to specify a particular configuration store if\r
-        needed.\r
-\r
-    \param[out] result Receives the handle to a configuration space if\r
-        successful.  Call khc_close_space() to close the handle.\r
-\r
-    \see khc_open_space()\r
-    \see khc_close_space()\r
-*/\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_get_module_config(wchar_t * module, khm_int32 flags, khm_handle * result);\r
-\r
-/*! \brief Retrieve a handle to the configuration space for plugins\r
-\r
-    The configuration space for plugins is a container which holds the\r
-    configuration subspaces for all the plugins.  This is the config\r
-    space which must be used to load a configuration space for a\r
-    plugin.\r
-\r
-    \param[in] flags The flags to pass in to the call to\r
-        khc_open_space().  The flags can be used to select a specific\r
-        configuration store if needed.\r
-\r
-    \param[out] result Receives a handle to the configuration\r
-        space. Call khc_close_space() to close the handle\r
-\r
-    \see khc_open_space()\r
-    \see khc_close_space()\r
- */\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_get_plugins_config(khm_int32 flags, khm_handle * result);\r
-\r
-/*! \brief Retrieve the handle to the configuration space for modules\r
-\r
-    The configuration space for modules is a container which hold the\r
-    configuration subspaces for all the modules.  Each module\r
-    registration ends up in this subspace.\r
-\r
-    \param[in] flags The flags to pass in to the call to\r
-        khc_open_space().  The flags can be used to select a specific\r
-        configuration store if needed.\r
-\r
-    \param[out] result Receives a handle to the configuration space.\r
-        Call khc_close_space() to close the handle.\r
-\r
-    \see khc_open_space()\r
-    \see khc_close_space()\r
- */\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_get_modules_config(khm_int32 flags, khm_handle * result);\r
-\r
-/*! \brief Return information about a loaded module\r
-\r
-    The retrieves a block of information about a module.  Refer to\r
-    ::kmm_module_info for information about the format of the returned\r
-    data.\r
-\r
-    Note that the size of the required buffer is actually greater than\r
-    the size of the ::kmm_module_info structure and accomodates the\r
-    ::kmm_plugin_info structures and strings required to complete the\r
-    information block.\r
-\r
-    Call the function with \a buffer set to NULL and \a cb_buffer\r
-    pointing at a khm_size variable to obtain the required size of the\r
-    buffer.\r
-\r
-    \param[in] module_name Name of a module\r
-    \param[in] flags Flags indicating which types of information to\r
-        return\r
-    \param[out] buffer Points to a buffer that recieves information.\r
-        Set this to NULL if only the size of the buffer is required.\r
-    \param[in,out] On entry, contains the size of the buffer pointed\r
-        to by \a buffer if \a buffer is not NULL. On exit, contains\r
-        the required size of the buffer or the number of actual bytes\r
-        copied.\r
-\r
-    \retval KHM_ERROR_SUCCESS The requested information was copied\r
-    \retval KHM_ERROR_INVALID_PARAM One of the parameters was invalid\r
-    \retval KHM_ERROR_TOO_LONG The buffer was not large enough or was\r
-        NULL.  The number of bytes requied is in \a cb_buffer.\r
-    \retval KHM_ERROR_NOT_FOUND The specified module is not a\r
-        registered module.\r
- */\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_get_module_info(wchar_t *  module_name, khm_int32 flags, \r
-                    kmm_module_info * buffer, khm_size * cb_buffer);\r
-\r
-/*! \brief Get information about a module\r
-\r
-    Similar to kmm_get_module_info(), but uses a module handle instead\r
-    of a name, and uses internal buffers for providing string fields.\r
-\r
-    The information that is returned should be freed using a call to\r
-    kmm_release_module_info_i().\r
-\r
-    \see kmm_release_module_info_i()\r
- */\r
-KHMEXP khm_int32   KHMAPI\r
-kmm_get_module_info_i(kmm_module module, kmm_module_info * info);\r
-\r
-/*! \brief Release module information\r
-\r
-    Releases the information returned by a previous call to\r
-    kmm_get_module_info_i().  The contents of the ::kmm_module_info\r
-    structure should not have been modified in any way between calling\r
-    kmm_get_module_info_i() and kmm_release_module_info_i().\r
- */\r
-KHMEXP khm_int32   KHMAPI\r
-kmm_release_module_info_i(kmm_module_info * info);\r
-\r
-/*! \brief Obtain information about a plugin\r
-\r
-    Retrieve a block of information about a plugin.  See\r
-    ::kmm_plugin_info for details about what information can be\r
-    returned.  Note that some fields may not be available if the\r
-    module is not loaded.\r
-\r
-    Note that the size of the required buffer is greater than the size\r
-    of the ::kmm_plugin_info structure and accounts for strings as\r
-    well.  Call kmm_get_plugin_info() with \a buffer set to NULL and\r
-    \a cb_buffer set to point to a variable of type \a khm_size to\r
-    obtain the required size of the structure.\r
-\r
-    \param[in] plugin_name Name of the plugin\r
-    \param[out] buffer The buffer to receive the plugin information.\r
-        Set to \a NULL if only the size of the buffer is required.\r
-    \param[in,out] cb_buffer On entry, points to variable that\r
-        specifies the size of the buffer pointed to by \a buffer is \a\r
-        buffer is not \a NULL.  On exit, holds the number of bytes\r
-        copied or the required size of the buffer.\r
-\r
-    \retval KHM_ERROR_SUCCESS The requested information was\r
-        successfully copied to the \a buffer\r
-    \retval KHM_ERROR_TOO_LONG The buffer was either \a NULL or\r
-        insufficient to hold the requested information.  The required\r
-        size of the buffer was stored in \a cb_buffer\r
-    \retval KHM_ERROR_INVALID_PARAM One or more parameters were\r
-        invlaid.\r
-    \retval KHM_ERROR_NOT_FOUND The specified plugin was not found\r
-        among the registered plugins.\r
-*/\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_get_plugin_info(wchar_t * plugin_name, \r
-                    kmm_plugin_info * buffer, \r
-                    khm_size * cb_buffer);\r
-\r
-/*! \brief Obtain information about a plugin using a plugin handle\r
-\r
-    Similar to kmm_get_plugin_info() but uses a plugin handle instead\r
-    of a plugin name.  If the call is successful, the \a info\r
-    structure will be filled with information about the plugin.  The\r
-    returned info should not be modified in any way and may contain\r
-    pointers to internal buffers.\r
-\r
-    The returned information must be released with a call to\r
-    kmm_release_plugin_info_i().\r
- */\r
-KHMEXP khm_int32   KHMAPI\r
-kmm_get_plugin_info_i(kmm_plugin p, kmm_plugin_info * info);\r
-\r
-/*! \brief Release plugin information returned by kmm_get_plugin_info_i\r
-\r
-    The information returned by kmm_get_plugin_info_i() should not be\r
-    modified in any way before calling kmm_release_plugin_info_i().\r
-    Once the call completes, the contents of \a info will be\r
-    initialized to zero.\r
- */\r
-KHMEXP khm_int32   KHMAPI\r
-kmm_release_plugin_info_i(kmm_plugin_info * info);\r
-\r
-/*! \brief Enumerates plugins\r
-\r
-    Enumerates through known plugins.  This list may not include\r
-    plugins which were not loaded by NetIDMgr in this session.\r
-\r
-    If the call is successful, a handle to the next plugin in the list\r
-    will be placed in \a p_next.  The returned handle must be freed\r
-    with a call to kmm_release_plugin().\r
-\r
-    If the \a p parameter is set to NULL, then the first plugin handle\r
-    will be placed in \a p_next.  The handles will not be returned in\r
-    any specific order.  In addition, the enumeration may not include\r
-    all known plugins if the list of plugins changes during\r
-    enumeration.\r
- */\r
-KHMEXP khm_int32   KHMAPI\r
-kmm_get_next_plugin(kmm_plugin p, kmm_plugin * p_next);\r
-\r
-/*! \brief Enables or disables a plugin\r
-\r
-    This function currently does not take effect immediately.  However\r
-    it marks the plugin as enabled or disabled so that the next time\r
-    NetIDMgr starts, the module manager will act accordingly.\r
-\r
-    \param[in] p Handle to the plugin\r
-\r
-    \param[in] enable If non-zero, the plugin will be marked as\r
-        enabled.  Otherwise the plugin will be marked as disabled.\r
- */\r
-KHMEXP khm_int32   KHMAPI\r
-kmm_enable_plugin(kmm_plugin p, khm_boolean enable);\r
-\r
-/*! \brief Register a plugin\r
-\r
-    The \a plugin member defines the plugin to be registered.  The \a\r
-    msg_proc and \a icon members of the structure are ignored.\r
-\r
-    At the time kmm_register_plugin() is called, the module specified\r
-    by \a module member of the \a plugin parameter must have been already\r
-    registered.  Otherwise the function call fails.\r
-\r
-    If the plugin has already been registered, then all the fields in\r
-    the plugin registration will be updated to be in sync with the\r
-    information provided in the \a plugin parameter.  The failure\r
-    counts and associated statistics will not be reset when the\r
-    configuration information is updated.\r
-\r
-    If the plugin has not been registered, the a new registration\r
-    entry is created in the configuration space indicated by the \a\r
-    config_flags parameter.  In addition, the plugin will be added to\r
-    the list of plugins associated with the owning module.\r
-\r
-    Note that the module that owns the plugin must be registered in\r
-    the same configuration store as the plugin.\r
-\r
-    \param[in] plugin Registration info for the plugin.  The \a\r
-        msg_proc and \a icon members are ignored.  All other fields\r
-        are required.  The \a description member should be localized\r
-        to the system locale when registering a plugin in the machine\r
-        configuration store and should be localized to the user locale\r
-        when registering a plugin in the user configuration store.\r
-    \param[in] config_flags Flags for the configuration provider.\r
-        These flags are used verbatim to call khc_open_space(), hence\r
-        they may be used to pick whether or not the registration is\r
-        per machine or per user.\r
-\r
-    \see kmm_register_module()\r
- */\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_register_plugin(kmm_plugin_reg * plugin, khm_int32 config_flags);\r
-\r
-/*! \brief Register a module\r
-\r
-    The \a module parameter specifies the parameters for the module\r
-    registration.\r
-\r
-    The \a plugin_info member should point to an array of\r
-    ::kmm_plugin_info structures unless the \a n_plugins member is\r
-    zero, in which case \a plugin_info can be \a NULL.  Plugins can be\r
-    registered separately using kmm_register_plugin().\r
-\r
-    \param[in] module Information about the module.  The name and path\r
-        fields are required. The \a plugin_info field can only be \a\r
-        NULL if \a n_plugins is zero.\r
-\r
-    \param[in] config_flags Flags used to call khc_open_space().  This\r
-        can be used to choose the configuration store in which the\r
-        module registration will be performed.\r
-  */\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_register_module(kmm_module_reg * module, khm_int32 config_flags);\r
-\r
-/*! \brief Unregister a plugin\r
-\r
-    Registration information associated with the plugin will be\r
-    removed.  In addtion, the plugin will be removed from the list of\r
-    plugins provided by the owner module.\r
-\r
-    \param[in] plugin Names the plugin to be removed\r
-    \param[in] config_flags Flags used to call khc_open_space(). Can\r
-        be used to choose the configuraiton store that is affected by\r
-        the call.\r
-\r
-    \note kmm_unregister_plugin() has no effect on whether the plugin\r
-        is loaded or not.  The caller must make sure that the plugin\r
-        is unloaded and the associated module is either also unloaded\r
-        or in a state where the plugin can be unregistered.\r
- */\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_unregister_plugin(wchar_t * plugin, khm_int32 config_flags);\r
-\r
-/*! \brief Unregister a module\r
-\r
-    Registration information associated with the module as well as all\r
-    the plugins provided by the module will be removed from the\r
-    configuration store.\r
-\r
-    \param[in] module Names the module to be removed\r
-\r
-    \param[in] config_flags Flags used to call khc_open_space().  Can\r
-        be used to choose the configuration store affected by the\r
-        call.\r
-\r
-    \note kmm_unregister_module() has no effect on the loaded state of\r
-        the module.  The caller should make sure that the module is\r
-        unloaded and in a state where it can be unregistered.\r
- */\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_unregister_module(wchar_t * module, khm_int32 config_flags);\r
-\r
-/*@}*/ /* kmm_reg */\r
-\r
-/*! \defgroup kmm_loc Internationalization support\r
-\r
-    See \ref pi_localization for more information about\r
-    internationalization.\r
-\r
-@{*/\r
-\r
-/*! \brief Locale descriptor record\r
-\r
-    See kmm_set_locale()\r
-*/\r
-typedef struct tag_kmm_module_locale {\r
-    khm_ui_4    language; /*!< A language ID.  On Windows, you can use the\r
-                            MAKELANGID macro to generate this value. */\r
-    wchar_t *   filename; /*!< The filename corresponding to this language.\r
-                            Use NULL to indicate that resources for this\r
-                            language are to be found in the main module. */\r
-    khm_int32   flags;    /*!< Flags.  Combination of KMM_MLOC_FLAG_* */\r
-} kmm_module_locale;\r
-\r
-#define LOCALE_DEF(language_id, filename, flags) {language_id, filename, flags}\r
-\r
-/*! \brief Default (fallback) locale\r
-*/\r
-#define KMM_MLOC_FLAG_DEFAULT 1\r
-\r
-\r
-/*! \brief Sets the locale for a loaded module.\r
-\r
-    The given locale records are searched in the given order until a\r
-    locale that matches the current user locale is found.  If no\r
-    locales match, then the first locale with the\r
-    ::KMM_MLOC_FLAG_DEFAULT flag set will be loaded.  If no locales\r
-    have that flag set, then the first locale is loaded.\r
-\r
-    You can obtain a handle to the loaded library using\r
-    kmm_get_resource_hmodule().  This function does not return until a\r
-    matched library is loaded.\r
-\r
-    Note that the ::kmm_module_locale structure only specifies a\r
-    module name for the resource module.  This resource module must\r
-    exist in the same directory as the \a module.\r
-\r
-    \param[in] module The module handle\r
-    \param[in] locales An array of ::kmm_module_locale objects\r
-    \param[in] n_locales The number of objects in the array pointed to by \a locales\r
-\r
-    \retval KHM_ERROR_SUCCESS Succeeded.\r
-    \retval KHM_ERROR_NOT_FOUND A matching locale resource library was not found.\r
-    \retval KHM_ERROR_INVALID_OPERATION The function was called on a module which is currently not being initalized.\r
-\r
-    \see \ref pi_localization\r
-    \see kmm_get_resource_hmodule()\r
-\r
-    \note This can only be called when handing init_module()\r
-*/\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_set_locale_info(kmm_module module, \r
-                    kmm_module_locale * locales, \r
-                    khm_int32 n_locales);\r
-\r
-#ifdef _WIN32\r
-\r
-/*! \brief Return the Windows module handle of the resource library of a NetIDMgr module.\r
-\r
-    NetIDMgr allows the specification of an alternate resource library\r
-    that will be used to load localized resources from.  This function\r
-    returns a handle to this library.\r
-    \r
-    While you can use the convenience macros to access resources in a\r
-    localization library using the module handle, it is recommended,\r
-    for performance reasons, to use this function to obtain the handle\r
-    to the resource library and then use that handle in calls to\r
-    LoadString, LoadImage etc. directly.\r
-*/\r
-KHMEXP HMODULE     KHMAPI \r
-kmm_get_resource_hmodule(kmm_module m);\r
-\r
-/*! \name Convenience Macros\r
-@{*/\r
-/*! \brief Convenience macro for using calling LoadAccelerators using a module handle\r
-\r
-    \param[in] module A handle to a loaded module.  The corresponding resource \r
-        module will be located through a call to kmm_get_resource_hmodule()\r
-*/\r
-#define kmm_LoadAccelerators(module, lpTableName) \\r
-    (LoadAccelerators(kmm_get_resource_hmodule(module), lpTableName))\r
-\r
-/*! \brief Convenience macro for using calling LoadBitmap using a module handle\r
-\r
-    \param[in] module A handle to a loaded module.  The corresponding resource \r
-        module will be located through a call to kmm_get_resource_hmodule()\r
-*/\r
-#define kmm_LoadBitmap(module, lpBitmapName) \\r
-    (LoadBitmap(kmm_get_resource_hmodule(module), lpBitmapName))\r
-\r
-/*! \brief Convenience macro for using calling LoadImage using a module handle\r
-\r
-    \param[in] module A handle to a loaded module.  The corresponding resource \r
-        module will be located through a call to kmm_get_resource_hmodule()\r
-*/\r
-#define kmm_LoadImage(module, lpszName, uType, cxDesired, cyDesired, fuLoad) \\r
-    (LoadImage(kmm_get_resource_hmodule(module), lpszName, uType, cxDesired, cyDesired, fuLoad))\r
-\r
-/*! \brief Convenience macro for using calling LoadCursor using a module handle\r
-\r
-    \param[in] module A handle to a loaded module.  The corresponding resource \r
-        module will be located through a call to kmm_get_resource_hmodule()\r
-*/\r
-#define kmm_LoadCursor(module, lpCursorName) \\r
-    (LoadCursor(kmm_get_resource_hmodule(module), lpCursorName))\r
-\r
-/*! \brief Convenience macro for using calling LoadIcon using a module handle\r
-\r
-    \param[in] module A handle to a loaded module.  The corresponding resource \r
-        module will be located through a call to kmm_get_resource_hmodule()\r
-*/\r
-#define kmm_LoadIcon(module, lpIconName) \\r
-    (LoadIcon(kmm_get_resource_hmodule(module), lpIconName))\r
-\r
-/*! \brief Convenience macro for using calling LoadMenu using a module handle\r
-\r
-    \param[in] module A handle to a loaded module.  The corresponding resource \r
-        module will be located through a call to kmm_get_resource_hmodule()\r
-*/\r
-#define kmm_LoadMenu(module, lpMenuName) \\r
-    (LoadMenu(kmm_get_resource_hmodule(module), lpMenuName))\r
-\r
-/*! \brief Convenience macro for using calling LoadString using a module handle\r
-\r
-    \param[in] module A handle to a loaded module.  The corresponding resource \r
-        module will be located through a call to kmm_get_resource_hmodule()\r
-*/\r
-#define kmm_LoadString(module, uID, lpBuffer, nBufferMax) \\r
-    (LoadString(kmm_get_resource_hmodule(module), uID, lpBuffer, nBufferMax))\r
-/*@}*/ /* Convenience Macros */\r
-#endif\r
-/*@}*/ /* group kmm_loc */\r
-/*@}*/ /* group kmm */\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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_MAXCCH_NAME)
+
+/*! \brief Maximum number of characters in a vendor string in KMM including the terminating NULL */
+#define KMM_MAXCCH_VENDOR 256
+
+/*! \brief Maximum number of bytes in a vendor string in KMM including the terminating NULL */
+#define KMM_MAXCB_VENDOR (sizeof(wchar_t) * KMM_MAXCCH_VENDOR)
+
+/*! \brief Maximum number of characters in a support URI in KMM including the terminating NULL */
+#define KMM_MAXCCH_SUPPORT 256
+
+/*! \brief Maximum number of bytes in a vendor string in KMM including the terminating NULL */
+#define KMM_MAXCB_SUPPORT (sizeof(wchar_t) * KMM_MAXCCH_SUPPORT)
+
+/*! \brief Maximum number of dependencies per plugin
+*/
+#define KMM_MAX_DEPENDENCIES    8
+
+/*! \brief Maximum number of dependants per plugin
+ */
+#define KMM_MAX_DEPENDANTS      32
+
+/*! \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 */
+
+    khm_int32   flags;          /*!< Flags for the plugin. Currently
+                                  this can only specify
+                                  ::KMM_PLUGIN_FLAG_DISABLED. */
+} kmm_plugin_info;
+
+/*! \brief The plugin is disabled
+
+    This flag will be set in the \a flags field of the
+    ::kmm_plugin_info structure for a plugin that has been marked as
+    disabled.  If the plugin is currently running, but marked as
+    disabled for future sessions, then this bit will be set in \a
+    flags , but the \a state of the plugin will indicate that the
+    plugin is running.
+ */
+#define KMM_PLUGIN_FLAG_DISABLED    0x00000400
+
+/*! \name Plugin types
+@{*/
+/*! \brief A credentials provider
+
+    \see \ref pi_pt_cred for more information.
+ */
+#define KHM_PITYPE_CRED     1
+
+/*! \brief A identity provider 
+
+    \see \ref pi_pt_cred for more information
+ */
+#define KHM_PITYPE_IDENT    2
+
+/*! \brief A configuration provider
+
+    \see \ref pi_pt_conf for more information.
+ */
+#define KHM_PITYPE_CONFIG   3
+
+/*! \brief Undefined plugin type
+
+    The plugin doesn't provide any credential type.
+ */
+#define KHM_PITYPE_MISC     4
+
+/*@}*/
+
+/*! \brief Plugin states */
+enum _kmm_plugin_states {
+    KMM_PLUGIN_STATE_FAIL_INIT = -6,    /*!< Failed to initialize */
+    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 */
+
+    wchar_t *   support;            /*!< Support URL/contact */
+
+    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_INCOMPAT=-12, /*!< The library containing
+                                          the module was not
+                                          compatible with this version
+                                          of NetIDMgr. */
+    KMM_MODULE_STATE_FAIL_INV_MODULE=-11, /*!< The library containing
+                                            the module was invalid. */
+    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_PARAM 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_PARAM 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_PARAM 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 Enables or disables a plugin
+
+    This function currently does not take effect immediately.  However
+    it marks the plugin as enabled or disabled so that the next time
+    NetIDMgr starts, the module manager will act accordingly.
+
+    \param[in] p Handle to the plugin
+
+    \param[in] enable If non-zero, the plugin will be marked as
+        enabled.  Otherwise the plugin will be marked as disabled.
+ */
+KHMEXP khm_int32   KHMAPI
+kmm_enable_plugin(kmm_plugin p, khm_boolean enable);
+
+/*! \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.  The name and path
+        fields are required. The \a plugin_info field can only 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.
+
+    Note that the ::kmm_module_locale structure only specifies a
+    module name for the resource module.  This resource module must
+    exist in the same directory as the \a module.
+
+    \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
index 932bdf8ef3d69868b1138563aa127111e657b4ea..52c34ac03ff4f565a1b8e46a0411d36aee634e5b 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kmminternal.h>\r
-#include<netidmgr_version.h>\r
-#include<assert.h>\r
-\r
-/* should only be accessed from the registrar thread */\r
-khm_size kmm_active_modules = 0;\r
-\r
-kmm_module_i * kmmint_get_module_i(wchar_t * name)\r
-{\r
-    kmm_module_i * m;\r
-    size_t sz;\r
-\r
-    if(FAILED(StringCbLength(name, KMM_MAXCB_NAME, &sz)))\r
-        return NULL;\r
-    sz += sizeof(wchar_t);\r
-\r
-    EnterCriticalSection(&cs_kmm);\r
-    m = (kmm_module_i *) hash_lookup(hash_modules, (void *) name);\r
-\r
-    if(m == NULL) {\r
-        m = PMALLOC(sizeof(kmm_module_i));\r
-        ZeroMemory(m, sizeof(kmm_module_i));\r
-\r
-        m->magic = KMM_MODULE_MAGIC;\r
-        m->name = PMALLOC(sz);\r
-        StringCbCopy(m->name, sz, name);\r
-        m->state = KMM_MODULE_STATE_NONE;\r
-\r
-        hash_add(hash_modules, (void *) m->name, (void *) m);\r
-        LPUSH(&kmm_all_modules, m);\r
-    }\r
-    LeaveCriticalSection(&cs_kmm);\r
-\r
-    return m;\r
-}\r
-\r
-kmm_module_i * kmmint_find_module_i(wchar_t * name)\r
-{\r
-    kmm_module_i * m;\r
-\r
-    EnterCriticalSection(&cs_kmm);\r
-    m = (kmm_module_i *) hash_lookup(hash_modules, (void *) name);\r
-    LeaveCriticalSection(&cs_kmm);\r
-\r
-    return m;\r
-}\r
-\r
-/* called with cs_kmm held */\r
-void kmmint_free_module(kmm_module_i * m)\r
-{\r
-    m->magic = 0;\r
-\r
-    hash_del(hash_modules, m->name);\r
-    LDELETE(&kmm_all_modules, m);\r
-\r
-    if (m->name)\r
-        PFREE(m->name);\r
-\r
-    if (m->description)\r
-        PFREE(m->description);\r
-\r
-    if (m->path)\r
-        PFREE(m->path);\r
-\r
-    if (m->vendor)\r
-        PFREE(m->vendor);\r
-\r
-    if (m->support)\r
-        PFREE(m->support);\r
-\r
-    if (m->version_info)\r
-        PFREE(m->version_info);\r
-\r
-    PFREE(m);\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI kmm_hold_module(kmm_module module)\r
-{\r
-    if(!kmm_is_module(module))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    EnterCriticalSection(&cs_kmm);\r
-    kmm_module_from_handle(module)->refcount++;\r
-    LeaveCriticalSection(&cs_kmm);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI kmm_release_module(kmm_module vm)\r
-{\r
-    kmm_module_i * m;\r
-\r
-    if(!kmm_is_module(vm))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_kmm);\r
-    m = kmm_module_from_handle(vm);\r
-    if(! --(m->refcount)) \r
-    {\r
-        /* note that a 0 ref count means that there are no active\r
-           plugins */\r
-        kmmint_free_module(m);\r
-    }\r
-    LeaveCriticalSection(&cs_kmm);\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-khm_int32\r
-kmmint_check_api_version(DWORD v) {\r
-    /* for now, we allow API versions in the range\r
-       KH_VERSION_API_MINCOMPAT through KH_VERSION_API, inclusive.  In\r
-       the future when we are swamped with so much time that we don't\r
-       know what to do with it, we can actually parse the\r
-       apiversion.txt file and create a compatibility table which we\r
-       can check against the functions used by the module and decide\r
-       whether or not it is compatible. */\r
-\r
-    if (v < KH_VERSION_API_MINCOMPAT ||\r
-        v > KH_VERSION_API)\r
-        return KHM_ERROR_INCOMPATIBLE;\r
-    else\r
-        return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-struct lang_code {\r
-    WORD language;\r
-    WORD codepage;\r
-};\r
-\r
-khm_int32\r
-kmmint_read_module_info(kmm_module_i * m) {\r
-    /* the only fields we can count on at this point are m->name and\r
-       m->path */\r
-    DWORD t;\r
-    size_t cb;\r
-    WORD lang;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    struct lang_code *languages;\r
-    int n_languages;\r
-    int i;\r
-    wchar_t resname[256];       /* the resource names are a lot shorter */\r
-    wchar_t * r;\r
-    VS_FIXEDFILEINFO *vff;\r
-\r
-    assert(m->name);\r
-    assert(m->path);\r
-\r
-    t = TRUE;\r
-    cb = GetFileVersionInfoSize(m->path,\r
-                                &t);\r
-    /* if successful, cb gets the size in bytes of the version info\r
-       structure and sets t to zero */\r
-    if (t) {\r
-        return KHM_ERROR_NOT_FOUND;\r
-    } else if (cb == 0) {\r
-        _report_mr1(KHERR_WARNING, MSG_RMI_NOT_FOUND, _dupstr(m->path));\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    if (m->version_info) {\r
-        PFREE(m->version_info);\r
-        m->version_info = NULL;\r
-    }\r
-\r
-    m->version_info = PMALLOC(cb);\r
-#ifdef DEBUG\r
-    assert(m->version_info);\r
-#endif\r
-\r
-    if(!GetFileVersionInfo(m->path,\r
-                           t, (DWORD) cb, m->version_info)) {\r
-        rv = KHM_ERROR_NOT_FOUND;\r
-        _report_mr1(KHERR_WARNING, MSG_RMI_NOT_FOUND, _dupstr(m->path));\r
-        _location(L"GetFileVersionInfo");\r
-        goto _cleanup;\r
-    }\r
-\r
-    if(!VerQueryValue(m->version_info,\r
-                     L"\\VarFileInfo\\Translation",\r
-                     (LPVOID*) &languages,\r
-                     &cb)) {\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        _report_mr1(KHERR_WARNING, MSG_RMI_NO_TRANS, _dupstr(m->path));\r
-        _location(L"VerQueryValue");\r
-        goto _cleanup;\r
-    }\r
-\r
-    n_languages = (int) (cb / sizeof(*languages));\r
-\r
-    /* Try searching for the user's default language first */\r
-    lang = GetUserDefaultLangID();\r
-    for (i = 0; i < n_languages; i++) {\r
-        if(languages[i].language == lang)\r
-            break;\r
-    }\r
-\r
-    /* If not, try the system default */\r
-    if (i >= n_languages) {\r
-        lang = GetSystemDefaultLangID();\r
-        for (i=0; i<n_languages; i++)\r
-            if (languages[i].language == lang)\r
-                break;\r
-    }\r
-\r
-    /* Then try EN_US */\r
-    if (i >= n_languages) {\r
-        lang = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);\r
-        for (i=0; i<n_languages; i++)\r
-            if (languages[i].language == lang)\r
-                break;\r
-    }\r
-\r
-    /* Language neutral? */\r
-    if (i >= n_languages) {\r
-        lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);\r
-        for (i=0; i<n_languages; i++)\r
-            if (languages[i].language == lang)\r
-                break;\r
-    }\r
-\r
-    /* Just use the first one? */\r
-    if (i >= n_languages) {\r
-        i = 0;\r
-    }\r
-\r
-    if (i >= n_languages) {\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        _report_mr0(KHERR_WARNING, MSG_RMI_NO_LOCAL);\r
-        goto _cleanup;\r
-    }\r
-\r
-    /* check module name */\r
-    StringCbPrintf(resname, sizeof(resname),\r
-                   L"\\StringFileInfo\\%04x%04x\\" TEXT(NIMV_MODULE),\r
-                   languages[i].language,\r
-                   languages[i].codepage);\r
-\r
-    if (!VerQueryValue(m->version_info,\r
-                       resname, (LPVOID *) &r, &cb)) {\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        _report_mr1(KHERR_WARNING, MSG_RMI_RES_MISSING, \r
-                    _cstr(TEXT(NIMV_MODULE)));\r
-        goto _cleanup;\r
-    }\r
-\r
-    if (cb > KMM_MAXCB_NAME ||\r
-        FAILED(StringCbLength(r, KMM_MAXCB_NAME, &cb))) {\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        _report_mr1(KHERR_WARNING, MSG_RMI_RES_TOO_LONG,\r
-                    _cstr(TEXT(NIMV_MODULE)));\r
-        goto _cleanup;\r
-    }\r
-\r
-    if (wcscmp(r, m->name)) {\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        _report_mr2(KHERR_WARNING, MSG_RMI_MOD_MISMATCH,\r
-                    _dupstr(r), _dupstr(m->name));\r
-        goto _cleanup;\r
-    }\r
-\r
-    /* check API version */\r
-    StringCbPrintf(resname, sizeof(resname),\r
-                   L"\\StringFileInfo\\%04x%04x\\" TEXT(NIMV_APIVER),\r
-                   languages[i].language,\r
-                   languages[i].codepage);\r
-\r
-    if (!VerQueryValue(m->version_info,\r
-                       resname, (LPVOID *) &r, &cb)) {\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        _report_mr1(KHERR_WARNING, MSG_RMI_RES_MISSING, \r
-                    _cstr(TEXT(NIMV_APIVER)));\r
-        goto _cleanup;\r
-    }\r
-\r
-    if (cb > KMM_MAXCB_NAME ||\r
-        FAILED(StringCbLength(r, KMM_MAXCB_NAME, &cb))) {\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        _report_mr1(KHERR_WARNING, MSG_RMI_RES_TOO_LONG,\r
-                    _cstr(TEXT(NIMV_APIVER)));\r
-        goto _cleanup;\r
-    }\r
-\r
-    t = wcstol(r, NULL, 10);\r
-\r
-    rv = kmmint_check_api_version(t);\r
-\r
-    if (KHM_FAILED(rv)) {\r
-        _report_mr2(KHERR_WARNING, MSG_RMI_API_MISMATCH,\r
-                    _int32(t), _int32(KH_VERSION_API));\r
-        goto _cleanup;\r
-    }\r
-\r
-    /* Looks good.  Now load the description, copyright, support URI\r
-       and file versions */\r
-    if (m->description) {\r
-        PFREE(m->description);\r
-        m->description = NULL;\r
-    }\r
-\r
-    StringCbPrintf(resname, sizeof(resname),\r
-                   L"\\StringFileInfo\\%04x%04x\\FileDescription",\r
-                   languages[i].language,\r
-                   languages[i].codepage);\r
-\r
-    if (!VerQueryValue(m->version_info,\r
-                       resname, (LPVOID *) &r, &cb)) {\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        _report_mr1(KHERR_WARNING, MSG_RMI_RES_MISSING, \r
-                    _cstr(L"FileDescription"));\r
-        goto _cleanup;\r
-    }\r
-\r
-    if (cb > KMM_MAXCB_DESC ||\r
-        FAILED(StringCbLength(r, KMM_MAXCB_DESC, &cb))) {\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        _report_mr1(KHERR_WARNING, MSG_RMI_RES_TOO_LONG,\r
-                    _cstr(L"FileDescription"));\r
-        goto _cleanup;\r
-    }\r
-\r
-    cb += sizeof(wchar_t);\r
-\r
-    m->description = PMALLOC(cb);\r
-#ifdef DEBUG\r
-    assert(m->description);\r
-#endif\r
-    StringCbCopy(m->description, cb, r);\r
-\r
-    /* on to the support URI */\r
-    if (m->support) {\r
-        PFREE(m->support);\r
-        m->support = NULL;\r
-    }\r
-\r
-    StringCbPrintf(resname, sizeof(resname),\r
-                   L"\\StringFileInfo\\%04x%04x\\" TEXT(NIMV_SUPPORT),\r
-                   languages[i].language,\r
-                   languages[i].codepage);\r
-\r
-    if (!VerQueryValue(m->version_info,\r
-                       resname, (LPVOID *) &r, &cb)) {\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        _report_mr1(KHERR_WARNING, MSG_RMI_RES_MISSING,\r
-                    _cstr(TEXT(NIMV_SUPPORT)));\r
-        goto _cleanup;\r
-    }\r
-\r
-    if (cb > KMM_MAXCB_SUPPORT ||\r
-        FAILED(StringCbLength(r, KMM_MAXCB_SUPPORT, &cb))) {\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        _report_mr1(KHERR_WARNING, MSG_RMI_RES_TOO_LONG,\r
-                    _cstr(TEXT(NIMV_SUPPORT)));\r
-        goto _cleanup;\r
-    }\r
-\r
-    cb += sizeof(wchar_t);\r
-\r
-    m->support = PMALLOC(cb);\r
-#ifdef DEBUG\r
-    assert(m->support);\r
-#endif\r
-    StringCbCopy(m->support, cb, r);\r
-\r
-    /* the vendor/copyright */\r
-    if (m->vendor) {\r
-        PFREE(m->vendor);\r
-        m->vendor = NULL;\r
-    }\r
-\r
-    StringCbPrintf(resname, sizeof(resname),\r
-                   L"\\StringFileInfo\\%04x%04x\\LegalCopyright",\r
-                   languages[i].language,\r
-                   languages[i].codepage);\r
-\r
-    if (!VerQueryValue(m->version_info,\r
-                       resname, (LPVOID *) &r, &cb)) {\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        _report_mr1(KHERR_WARNING, MSG_RMI_RES_MISSING, \r
-                    _cstr(L"LegalCopyright"));\r
-        goto _cleanup;\r
-    }\r
-\r
-    if (cb > KMM_MAXCB_SUPPORT ||\r
-        FAILED(StringCbLength(r, KMM_MAXCB_SUPPORT, &cb))) {\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        _report_mr1(KHERR_WARNING, MSG_RMI_RES_TOO_LONG,\r
-                    _cstr(L"LegalCopyright"));\r
-        goto _cleanup;\r
-    }\r
-\r
-    cb += sizeof(wchar_t);\r
-\r
-    m->vendor = PMALLOC(cb);\r
-#ifdef DEBUG\r
-    assert(m->vendor);\r
-#endif\r
-    StringCbCopy(m->vendor, cb, r);\r
-\r
-    if (!VerQueryValue(m->version_info,\r
-                       L"\\",\r
-                       (LPVOID *) &vff,\r
-                       &cb) ||\r
-        cb != sizeof(*vff)) {\r
-\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        _report_mr1(KHERR_WARNING, MSG_RMI_RES_MISSING, \r
-                    _cstr(L"Fixed Version Info"));\r
-        goto _cleanup;\r
-    }\r
-\r
-    m->file_version.major = HIWORD(vff->dwFileVersionMS);\r
-    m->file_version.minor = LOWORD(vff->dwFileVersionMS);\r
-    m->file_version.patch = HIWORD(vff->dwFileVersionLS);\r
-    m->file_version.aux   = LOWORD(vff->dwFileVersionLS);\r
-\r
-    m->prod_version.major = HIWORD(vff->dwProductVersionMS);\r
-    m->prod_version.minor = LOWORD(vff->dwProductVersionMS);\r
-    m->prod_version.patch = HIWORD(vff->dwProductVersionLS);\r
-    m->prod_version.aux   = LOWORD(vff->dwProductVersionLS);\r
-\r
-    rv = KHM_ERROR_SUCCESS;\r
-\r
- _cleanup:\r
-    if (KHM_FAILED(rv)) {\r
-        if (m->version_info) {\r
-            PFREE(m->version_info);\r
-            m->version_info = NULL;\r
-        }\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI kmm_load_module(wchar_t * modname, \r
-                                          khm_int32 flags, \r
-                                          kmm_module * result)\r
-{\r
-    kmm_module_i * m = NULL;\r
-    kmm_module_i * mi;\r
-    size_t cbsize;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if(FAILED(StringCbLength(modname, KMM_MAXCB_NAME, &cbsize)))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    cbsize += sizeof(wchar_t);\r
-\r
-    EnterCriticalSection(&cs_kmm);\r
-    mi = kmmint_find_module_i(modname);\r
-\r
-    if(mi != NULL) {\r
-        kmm_hold_module(kmm_handle_from_module(mi));\r
-        /* check if the module has either failed to load either or if\r
-        it has been terminated.  If so, we try once again to load the\r
-        module. */\r
-        if(!(flags & KMM_LM_FLAG_NOLOAD) && \r
-            (mi->state < 0 || mi->state == KMM_MODULE_STATE_EXITED)) \r
-        {\r
-            mi->state = KMM_MODULE_STATE_PREINIT;\r
-        }\r
-    }\r
-    LeaveCriticalSection(&cs_kmm);\r
-\r
-    if(flags & KMM_LM_FLAG_NOLOAD) {\r
-        if(result)\r
-            *result = mi;\r
-        else if(mi)\r
-            kmm_release_module(kmm_handle_from_module(mi));\r
-\r
-        return (mi)? KHM_ERROR_SUCCESS: KHM_ERROR_NOT_FOUND;\r
-    }\r
-\r
-    if(mi) {\r
-        m = mi;\r
-    } else {\r
-        m = kmmint_get_module_i(modname);\r
-        m->state = KMM_MODULE_STATE_PREINIT;\r
-        kmm_hold_module(kmm_handle_from_module(m));\r
-    }\r
-\r
-    /* the module is already running or is already being\r
-       worked on by the registrar */\r
-    if(m->state != KMM_MODULE_STATE_PREINIT) {\r
-        if(result)\r
-            *result = kmm_handle_from_module(m);\r
-        else\r
-            kmm_release_module(kmm_handle_from_module(m));\r
-\r
-        return KHM_ERROR_EXISTS;\r
-    }\r
-\r
-    kmmint_add_to_module_queue();\r
-\r
-    if(flags & KMM_LM_FLAG_SYNC) {\r
-        kmm_hold_module(kmm_handle_from_module(m));\r
-        kmq_send_message(KMSG_KMM, \r
-                         KMSG_KMM_I_REG, \r
-                         KMM_REG_INIT_MODULE, \r
-                         (void*) m);\r
-        if(m->state <= 0) {\r
-            /* failed to load ? */\r
-            if(m->state == KMM_MODULE_STATE_FAIL_NOT_FOUND)\r
-                rv = KHM_ERROR_NOT_FOUND;\r
-            else if(m->state == KMM_MODULE_STATE_FAIL_SIGNATURE)\r
-                rv = KHM_ERROR_INVALID_SIGNATURE;\r
-            else\r
-                rv = KHM_ERROR_UNKNOWN;\r
-\r
-            kmm_release_module(kmm_handle_from_module(m));\r
-            if(result)\r
-                *result = NULL;\r
-        } else {\r
-            if(result)\r
-                *result = kmm_handle_from_module(m);\r
-            else\r
-                kmm_release_module(kmm_handle_from_module(m));\r
-        }\r
-    } else {\r
-        kmm_hold_module(kmm_handle_from_module(m));\r
-        kmq_post_message(KMSG_KMM, \r
-                         KMSG_KMM_I_REG, \r
-                         KMM_REG_INIT_MODULE, \r
-                         (void*) m);\r
-        if(result)\r
-            *result = kmm_handle_from_module(m);\r
-        else\r
-            kmm_release_module(kmm_handle_from_module(m));\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_get_module_state(kmm_module m)\r
-{\r
-    if(!kmm_is_module(m))\r
-        return KMM_MODULE_STATE_NONE;\r
-    else\r
-        return kmm_module_from_handle(m)->state;\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI\r
-kmm_get_module_info_i(kmm_module vm, kmm_module_info * info) {\r
-    kmm_module_i * m;\r
-    khm_int32 rv;\r
-\r
-    EnterCriticalSection(&cs_kmm);\r
-    if (!kmm_is_module(vm) || !info)\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-    else {\r
-        m = kmm_module_from_handle(vm);\r
-\r
-        ZeroMemory(info, sizeof(*info));\r
-\r
-        info->reg.name = m->name;\r
-        info->reg.path = m->path;\r
-        info->reg.vendor = m->vendor;\r
-\r
-        info->reg.n_plugins = m->plugin_count;\r
-\r
-        info->state = m->state;\r
-\r
-        info->h_module = vm;\r
-\r
-        info->file_version = m->file_version;\r
-        info->product_version = m->prod_version;\r
-        kmm_hold_module(vm);\r
-\r
-        rv = KHM_ERROR_SUCCESS;\r
-    }\r
-    LeaveCriticalSection(&cs_kmm);\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI\r
-kmm_release_module_info_i(kmm_module_info * info) {\r
-    if (info->h_module)\r
-        kmm_release_module(info->h_module);\r
-\r
-    ZeroMemory(info, sizeof(*info));\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_unload_module(kmm_module module) {\r
-\r
-    if(!kmm_is_module(module))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    kmm_hold_module(module);\r
-    kmq_post_message(KMSG_KMM, \r
-                    KMSG_KMM_I_REG, \r
-                    KMM_REG_EXIT_MODULE, \r
-                    (void *) kmm_module_from_handle(module));\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_load_default_modules(void) {\r
-    khm_handle csm = NULL;\r
-    khm_handle cs_mod = NULL;\r
-    khm_int32 rv;\r
-    wchar_t buf[KMM_MAXCCH_NAME];\r
-    khm_size s;\r
-\r
-    rv = kmm_get_modules_config(0, &csm);\r
-    if(KHM_FAILED(rv))\r
-        return rv;\r
-\r
-    _begin_task(KHERR_CF_TRANSITIVE);\r
-    _report_mr0(KHERR_NONE, MSG_LOAD_DEFAULT);\r
-    _describe();\r
-\r
-    kmmint_add_to_module_queue();\r
-\r
-    while(KHM_SUCCEEDED(khc_enum_subspaces(csm, cs_mod, &cs_mod))) {\r
-\r
-        s = sizeof(buf);\r
-        if (KHM_FAILED(khc_get_config_space_name(cs_mod, buf, &s)))\r
-            continue;\r
-\r
-        /* check for schema subspace.  This is not an actual module. */\r
-        if (!wcscmp(buf, L"_Schema"))\r
-            continue;\r
-\r
-        kmm_load_module(buf, 0, NULL);\r
-    }\r
-\r
-    kmmint_remove_from_module_queue();\r
-\r
-    if(csm)\r
-        khc_close_space(csm);\r
-\r
-    _end_task();\r
-\r
-    return rv;\r
-}\r
-\r
-#ifdef _WIN32\r
-KHMEXP HMODULE     KHMAPI \r
-kmm_get_hmodule(kmm_module m)\r
-{\r
-    if(!kmm_is_module(m))\r
-        return NULL;\r
-    else\r
-        return kmm_module_from_handle(m)->h_module;\r
-}\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+#include<netidmgr_version.h>
+#include<assert.h>
+
+/* should only be accessed from the registrar thread */
+khm_size kmm_active_modules = 0;
+
+kmm_module_i * kmmint_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 = PMALLOC(sizeof(kmm_module_i));
+        ZeroMemory(m, sizeof(kmm_module_i));
+
+        m->magic = KMM_MODULE_MAGIC;
+        m->name = PMALLOC(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 * kmmint_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 kmmint_free_module(kmm_module_i * m)
+{
+    m->magic = 0;
+
+    hash_del(hash_modules, m->name);
+    LDELETE(&kmm_all_modules, m);
+
+    if (m->name)
+        PFREE(m->name);
+
+    if (m->description)
+        PFREE(m->description);
+
+    if (m->path)
+        PFREE(m->path);
+
+    if (m->vendor)
+        PFREE(m->vendor);
+
+    if (m->support)
+        PFREE(m->support);
+
+    if (m->version_info)
+        PFREE(m->version_info);
+
+    PFREE(m);
+}
+
+KHMEXP khm_int32   KHMAPI kmm_hold_module(kmm_module module)
+{
+    if(!kmm_is_module(module))
+        return KHM_ERROR_INVALID_PARAM;
+    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_PARAM;
+
+    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 */
+        kmmint_free_module(m);
+    }
+    LeaveCriticalSection(&cs_kmm);
+    return KHM_ERROR_SUCCESS;
+}
+
+khm_int32
+kmmint_check_api_version(DWORD v) {
+    /* for now, we allow API versions in the range
+       KH_VERSION_API_MINCOMPAT through KH_VERSION_API, inclusive.  In
+       the future when we are swamped with so much time that we don't
+       know what to do with it, we can actually parse the
+       apiversion.txt file and create a compatibility table which we
+       can check against the functions used by the module and decide
+       whether or not it is compatible. */
+
+    if (v < KH_VERSION_API_MINCOMPAT ||
+        v > KH_VERSION_API)
+        return KHM_ERROR_INCOMPATIBLE;
+    else
+        return KHM_ERROR_SUCCESS;
+}
+
+struct lang_code {
+    WORD language;
+    WORD codepage;
+};
+
+khm_int32
+kmmint_read_module_info(kmm_module_i * m) {
+    /* the only fields we can count on at this point are m->name and
+       m->path */
+    DWORD t;
+    size_t cb;
+    WORD lang;
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+    struct lang_code *languages;
+    int n_languages;
+    int i;
+    wchar_t resname[256];       /* the resource names are a lot shorter */
+    wchar_t * r;
+    VS_FIXEDFILEINFO *vff;
+
+    assert(m->name);
+    assert(m->path);
+
+    t = TRUE;
+    cb = GetFileVersionInfoSize(m->path,
+                                &t);
+    /* if successful, cb gets the size in bytes of the version info
+       structure and sets t to zero */
+    if (t) {
+        return KHM_ERROR_NOT_FOUND;
+    } else if (cb == 0) {
+        _report_mr1(KHERR_WARNING, MSG_RMI_NOT_FOUND, _dupstr(m->path));
+        return KHM_ERROR_INVALID_PARAM;
+    }
+
+    if (m->version_info) {
+        PFREE(m->version_info);
+        m->version_info = NULL;
+    }
+
+    m->version_info = PMALLOC(cb);
+#ifdef DEBUG
+    assert(m->version_info);
+#endif
+
+    if(!GetFileVersionInfo(m->path,
+                           t, (DWORD) cb, m->version_info)) {
+        rv = KHM_ERROR_NOT_FOUND;
+        _report_mr1(KHERR_WARNING, MSG_RMI_NOT_FOUND, _dupstr(m->path));
+        _location(L"GetFileVersionInfo");
+        goto _cleanup;
+    }
+
+    if(!VerQueryValue(m->version_info,
+                     L"\\VarFileInfo\\Translation",
+                     (LPVOID*) &languages,
+                     &cb)) {
+        rv = KHM_ERROR_INVALID_PARAM;
+        _report_mr1(KHERR_WARNING, MSG_RMI_NO_TRANS, _dupstr(m->path));
+        _location(L"VerQueryValue");
+        goto _cleanup;
+    }
+
+    n_languages = (int) (cb / sizeof(*languages));
+
+    /* Try searching for the user's default language first */
+    lang = GetUserDefaultLangID();
+    for (i = 0; i < n_languages; i++) {
+        if(languages[i].language == lang)
+            break;
+    }
+
+    /* If not, try the system default */
+    if (i >= n_languages) {
+        lang = GetSystemDefaultLangID();
+        for (i=0; i<n_languages; i++)
+            if (languages[i].language == lang)
+                break;
+    }
+
+    /* Then try EN_US */
+    if (i >= n_languages) {
+        lang = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
+        for (i=0; i<n_languages; i++)
+            if (languages[i].language == lang)
+                break;
+    }
+
+    /* Language neutral? */
+    if (i >= n_languages) {
+        lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
+        for (i=0; i<n_languages; i++)
+            if (languages[i].language == lang)
+                break;
+    }
+
+    /* Just use the first one? */
+    if (i >= n_languages) {
+        i = 0;
+    }
+
+    if (i >= n_languages) {
+        rv = KHM_ERROR_INVALID_PARAM;
+        _report_mr0(KHERR_WARNING, MSG_RMI_NO_LOCAL);
+        goto _cleanup;
+    }
+
+    /* check module name */
+    StringCbPrintf(resname, sizeof(resname),
+                   L"\\StringFileInfo\\%04x%04x\\" TEXT(NIMV_MODULE),
+                   languages[i].language,
+                   languages[i].codepage);
+
+    if (!VerQueryValue(m->version_info,
+                       resname, (LPVOID *) &r, &cb)) {
+        rv = KHM_ERROR_INVALID_PARAM;
+        _report_mr1(KHERR_WARNING, MSG_RMI_RES_MISSING, 
+                    _cstr(TEXT(NIMV_MODULE)));
+        goto _cleanup;
+    }
+
+    if (cb > KMM_MAXCB_NAME ||
+        FAILED(StringCbLength(r, KMM_MAXCB_NAME, &cb))) {
+        rv = KHM_ERROR_INVALID_PARAM;
+        _report_mr1(KHERR_WARNING, MSG_RMI_RES_TOO_LONG,
+                    _cstr(TEXT(NIMV_MODULE)));
+        goto _cleanup;
+    }
+
+    if (wcscmp(r, m->name)) {
+        rv = KHM_ERROR_INVALID_PARAM;
+        _report_mr2(KHERR_WARNING, MSG_RMI_MOD_MISMATCH,
+                    _dupstr(r), _dupstr(m->name));
+        goto _cleanup;
+    }
+
+    /* check API version */
+    StringCbPrintf(resname, sizeof(resname),
+                   L"\\StringFileInfo\\%04x%04x\\" TEXT(NIMV_APIVER),
+                   languages[i].language,
+                   languages[i].codepage);
+
+    if (!VerQueryValue(m->version_info,
+                       resname, (LPVOID *) &r, &cb)) {
+        rv = KHM_ERROR_INVALID_PARAM;
+        _report_mr1(KHERR_WARNING, MSG_RMI_RES_MISSING, 
+                    _cstr(TEXT(NIMV_APIVER)));
+        goto _cleanup;
+    }
+
+    if (cb > KMM_MAXCB_NAME ||
+        FAILED(StringCbLength(r, KMM_MAXCB_NAME, &cb))) {
+        rv = KHM_ERROR_INVALID_PARAM;
+        _report_mr1(KHERR_WARNING, MSG_RMI_RES_TOO_LONG,
+                    _cstr(TEXT(NIMV_APIVER)));
+        goto _cleanup;
+    }
+
+    t = wcstol(r, NULL, 10);
+
+    rv = kmmint_check_api_version(t);
+
+    if (KHM_FAILED(rv)) {
+        _report_mr2(KHERR_WARNING, MSG_RMI_API_MISMATCH,
+                    _int32(t), _int32(KH_VERSION_API));
+        goto _cleanup;
+    }
+
+    /* Looks good.  Now load the description, copyright, support URI
+       and file versions */
+    if (m->description) {
+        PFREE(m->description);
+        m->description = NULL;
+    }
+
+    StringCbPrintf(resname, sizeof(resname),
+                   L"\\StringFileInfo\\%04x%04x\\FileDescription",
+                   languages[i].language,
+                   languages[i].codepage);
+
+    if (!VerQueryValue(m->version_info,
+                       resname, (LPVOID *) &r, &cb)) {
+        rv = KHM_ERROR_INVALID_PARAM;
+        _report_mr1(KHERR_WARNING, MSG_RMI_RES_MISSING, 
+                    _cstr(L"FileDescription"));
+        goto _cleanup;
+    }
+
+    if (cb > KMM_MAXCB_DESC ||
+        FAILED(StringCbLength(r, KMM_MAXCB_DESC, &cb))) {
+        rv = KHM_ERROR_INVALID_PARAM;
+        _report_mr1(KHERR_WARNING, MSG_RMI_RES_TOO_LONG,
+                    _cstr(L"FileDescription"));
+        goto _cleanup;
+    }
+
+    cb += sizeof(wchar_t);
+
+    m->description = PMALLOC(cb);
+#ifdef DEBUG
+    assert(m->description);
+#endif
+    StringCbCopy(m->description, cb, r);
+
+    /* on to the support URI */
+    if (m->support) {
+        PFREE(m->support);
+        m->support = NULL;
+    }
+
+    StringCbPrintf(resname, sizeof(resname),
+                   L"\\StringFileInfo\\%04x%04x\\" TEXT(NIMV_SUPPORT),
+                   languages[i].language,
+                   languages[i].codepage);
+
+    if (!VerQueryValue(m->version_info,
+                       resname, (LPVOID *) &r, &cb)) {
+        rv = KHM_ERROR_INVALID_PARAM;
+        _report_mr1(KHERR_WARNING, MSG_RMI_RES_MISSING,
+                    _cstr(TEXT(NIMV_SUPPORT)));
+        goto _cleanup;
+    }
+
+    if (cb > KMM_MAXCB_SUPPORT ||
+        FAILED(StringCbLength(r, KMM_MAXCB_SUPPORT, &cb))) {
+        rv = KHM_ERROR_INVALID_PARAM;
+        _report_mr1(KHERR_WARNING, MSG_RMI_RES_TOO_LONG,
+                    _cstr(TEXT(NIMV_SUPPORT)));
+        goto _cleanup;
+    }
+
+    cb += sizeof(wchar_t);
+
+    m->support = PMALLOC(cb);
+#ifdef DEBUG
+    assert(m->support);
+#endif
+    StringCbCopy(m->support, cb, r);
+
+    /* the vendor/copyright */
+    if (m->vendor) {
+        PFREE(m->vendor);
+        m->vendor = NULL;
+    }
+
+    StringCbPrintf(resname, sizeof(resname),
+                   L"\\StringFileInfo\\%04x%04x\\LegalCopyright",
+                   languages[i].language,
+                   languages[i].codepage);
+
+    if (!VerQueryValue(m->version_info,
+                       resname, (LPVOID *) &r, &cb)) {
+        rv = KHM_ERROR_INVALID_PARAM;
+        _report_mr1(KHERR_WARNING, MSG_RMI_RES_MISSING, 
+                    _cstr(L"LegalCopyright"));
+        goto _cleanup;
+    }
+
+    if (cb > KMM_MAXCB_SUPPORT ||
+        FAILED(StringCbLength(r, KMM_MAXCB_SUPPORT, &cb))) {
+        rv = KHM_ERROR_INVALID_PARAM;
+        _report_mr1(KHERR_WARNING, MSG_RMI_RES_TOO_LONG,
+                    _cstr(L"LegalCopyright"));
+        goto _cleanup;
+    }
+
+    cb += sizeof(wchar_t);
+
+    m->vendor = PMALLOC(cb);
+#ifdef DEBUG
+    assert(m->vendor);
+#endif
+    StringCbCopy(m->vendor, cb, r);
+
+    if (!VerQueryValue(m->version_info,
+                       L"\\",
+                       (LPVOID *) &vff,
+                       &cb) ||
+        cb != sizeof(*vff)) {
+
+        rv = KHM_ERROR_INVALID_PARAM;
+        _report_mr1(KHERR_WARNING, MSG_RMI_RES_MISSING, 
+                    _cstr(L"Fixed Version Info"));
+        goto _cleanup;
+    }
+
+    m->file_version.major = HIWORD(vff->dwFileVersionMS);
+    m->file_version.minor = LOWORD(vff->dwFileVersionMS);
+    m->file_version.patch = HIWORD(vff->dwFileVersionLS);
+    m->file_version.aux   = LOWORD(vff->dwFileVersionLS);
+
+    m->prod_version.major = HIWORD(vff->dwProductVersionMS);
+    m->prod_version.minor = LOWORD(vff->dwProductVersionMS);
+    m->prod_version.patch = HIWORD(vff->dwProductVersionLS);
+    m->prod_version.aux   = LOWORD(vff->dwProductVersionLS);
+
+    rv = KHM_ERROR_SUCCESS;
+
+ _cleanup:
+    if (KHM_FAILED(rv)) {
+        if (m->version_info) {
+            PFREE(m->version_info);
+            m->version_info = NULL;
+        }
+    }
+
+    return rv;
+}
+
+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_PARAM;
+    cbsize += sizeof(wchar_t);
+
+    EnterCriticalSection(&cs_kmm);
+    mi = kmmint_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 = kmmint_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_PARAM;
+    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;
+
+        info->file_version = m->file_version;
+        info->product_version = m->prod_version;
+        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_PARAM;
+
+    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_handle cs_mod = NULL;
+    khm_int32 rv;
+    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();
+
+    kmmint_add_to_module_queue();
+
+    while(KHM_SUCCEEDED(khc_enum_subspaces(csm, cs_mod, &cs_mod))) {
+
+        s = sizeof(buf);
+        if (KHM_FAILED(khc_get_config_space_name(cs_mod, buf, &s)))
+            continue;
+
+        /* check for schema subspace.  This is not an actual module. */
+        if (!wcscmp(buf, L"_Schema"))
+            continue;
+
+        kmm_load_module(buf, 0, NULL);
+    }
+
+    kmmint_remove_from_module_queue();
+
+    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
index 18a46b7c469122ef8a3c6d8b081f93698094e4f3..89e31ad844225ead90df8dd133004f60c230a788 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kmminternal.h>\r
-\r
-/* Called with no locks held to get a kmm_plugin_i structure\r
-   that matches the name.  First we look in the hash table, and\r
-   if one isn't found, we create an empty one.\r
-*/\r
-\r
-kmm_plugin_i * \r
-kmmint_get_plugin_i(wchar_t * name)\r
-{\r
-    kmm_plugin_i * p;\r
-    size_t cb;\r
-\r
-    if(FAILED(StringCbLength(name, KMM_MAXCB_NAME, &cb)))\r
-        return NULL;\r
-    cb += sizeof(wchar_t);\r
-\r
-    EnterCriticalSection(&cs_kmm);\r
-    p = (kmm_plugin_i *) hash_lookup(hash_plugins, (void *) name);\r
-\r
-    if(p == NULL) {\r
-        p = PMALLOC(sizeof(kmm_plugin_i));\r
-        ZeroMemory(p, sizeof(kmm_plugin_i));\r
-        p->magic = KMM_PLUGIN_MAGIC;\r
-        p->p.name = PMALLOC(cb);\r
-        StringCbCopy(p->p.name, cb, name);\r
-        p->state = KMM_PLUGIN_STATE_NONE;\r
-\r
-        hash_add(hash_plugins, (void *) p->p.name, (void *) p);\r
-        kmmint_list_plugin(p);\r
-    }\r
-    LeaveCriticalSection(&cs_kmm);\r
-\r
-    return p;\r
-}\r
-\r
-kmm_plugin_i * \r
-kmmint_find_plugin_i(wchar_t * name)\r
-{\r
-    kmm_plugin_i * p;\r
-    size_t cb;\r
-\r
-    if(FAILED(StringCbLength(name, KMM_MAXCB_NAME, &cb)))\r
-        return NULL;\r
-\r
-    EnterCriticalSection(&cs_kmm);\r
-    p = (kmm_plugin_i *) hash_lookup(hash_plugins, (void *) name);\r
-    LeaveCriticalSection(&cs_kmm);\r
-\r
-    return p;\r
-}\r
-\r
-/* the plugin must be delisted before calling this */\r
-void \r
-kmmint_list_plugin(kmm_plugin_i * p)\r
-{\r
-    EnterCriticalSection(&cs_kmm);\r
-    if((p->flags & KMM_PLUGIN_FLAG_IN_MODLIST) ||\r
-        (p->flags & KMM_PLUGIN_FLAG_IN_LIST)) \r
-    {\r
-        RaiseException(2, EXCEPTION_NONCONTINUABLE, 0, NULL);\r
-    }\r
-    p->flags |= KMM_PLUGIN_FLAG_IN_LIST;\r
-    LPUSH(&kmm_listed_plugins, p);\r
-    LeaveCriticalSection(&cs_kmm);\r
-}\r
-\r
-void \r
-kmmint_delist_plugin(kmm_plugin_i * p)\r
-{\r
-    EnterCriticalSection(&cs_kmm);\r
-    if(p->flags & KMM_PLUGIN_FLAG_IN_LIST) {\r
-        p->flags &= ~KMM_PLUGIN_FLAG_IN_LIST;\r
-        LDELETE(&kmm_listed_plugins, p);\r
-    }\r
-    if(p->flags & KMM_PLUGIN_FLAG_IN_MODLIST) {\r
-        p->flags &= ~KMM_PLUGIN_FLAG_IN_MODLIST;\r
-        LDELETE(&(p->module->plugins), p);\r
-    }\r
-    LeaveCriticalSection(&cs_kmm);\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_hold_plugin(kmm_plugin p)\r
-{\r
-    kmm_plugin_i * pi;\r
-\r
-    if(!kmm_is_plugin(p))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_kmm);\r
-    pi = kmm_plugin_from_handle(p);\r
-    pi->refcount++;\r
-    LeaveCriticalSection(&cs_kmm);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* called with cs_kmm held */\r
-void \r
-kmmint_free_plugin(kmm_plugin_i * pi)\r
-{\r
-    int i;\r
-    pi->magic = 0;\r
-\r
-    hash_del(hash_plugins, (void *) pi->p.name);\r
-\r
-    kmmint_delist_plugin(pi);\r
-\r
-    for(i=0; i<pi->n_dependants; i++) {\r
-        kmm_release_plugin(kmm_handle_from_plugin(pi->dependants[i]));\r
-        pi->dependants[i] = NULL;\r
-    }\r
-\r
-    if(pi->module) {\r
-        kmm_release_module(kmm_handle_from_module(pi->module));\r
-    }\r
-\r
-    pi->module = NULL;\r
-    pi->p.module = NULL;\r
-\r
-    if(pi->p.name)\r
-        PFREE(pi->p.name);\r
-    pi->p.name = NULL;\r
-\r
-    if(pi->p.description)\r
-        PFREE(pi->p.description);\r
-    pi->p.description = NULL;\r
-\r
-    if(pi->p.dependencies)\r
-        PFREE(pi->p.dependencies);\r
-    pi->p.dependencies = NULL;\r
-\r
-    PFREE(pi);\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI\r
-kmm_enable_plugin(kmm_plugin p, khm_boolean enable) {\r
-    kmm_plugin_i * pi;\r
-    khm_int32 rv = KHM_ERROR_NOT_FOUND; /* default to error */\r
-    khm_handle csp_plugin = NULL;\r
-\r
-    EnterCriticalSection(&cs_kmm);\r
-    if (!kmm_is_plugin(p)) {\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        goto _cleanup;\r
-    }\r
-\r
-    pi = kmm_plugin_from_handle(p);\r
-\r
-    if (KHM_FAILED(rv = kmm_get_plugin_config(pi->p.name, 0, &csp_plugin))) {\r
-        goto _cleanup;\r
-    }\r
-\r
-    if (KHM_FAILED(rv = khc_write_int32(csp_plugin, L"Disabled", !enable))) {\r
-        goto _cleanup;\r
-    }\r
-\r
-    rv = KHM_ERROR_SUCCESS;\r
-\r
- _cleanup:\r
-    LeaveCriticalSection(&cs_kmm);\r
-\r
-    if (csp_plugin)\r
-        khc_close_space(csp_plugin);\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI\r
-kmm_get_plugin_info_i(kmm_plugin p, kmm_plugin_info * info) {\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    kmm_plugin_i * pi;\r
-    khm_handle csp_plugin;\r
-\r
-    if (!info)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_kmm);\r
-    if (!kmm_is_plugin(p)) {\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-        goto _cleanup;\r
-    }\r
-\r
-    pi = kmm_plugin_from_handle(p);\r
-\r
-    ZeroMemory(info, sizeof(*info));\r
-\r
-    info->reg = pi->p;\r
-    info->reg.msg_proc = NULL;\r
-\r
-    if (KHM_FAILED(kmm_get_plugin_config(pi->p.name, KHM_PERM_READ,\r
-                                         &csp_plugin))) {\r
-        info->failure_count = 0;\r
-        *((khm_int64 *)&info->failure_time) = 0;\r
-        info->failure_reason = 0;\r
-    } else {\r
-        if (KHM_FAILED(khc_read_int32(csp_plugin, L"FailureCount",\r
-                                      &info->failure_count)))\r
-            info->failure_count = 0;\r
-        if (KHM_FAILED(khc_read_int64(csp_plugin, L"FailureTime",\r
-                                      (khm_int64 *) &info->failure_time)))\r
-            *((khm_int64 *) &info->failure_time) = 0;\r
-        if (KHM_FAILED(khc_read_int32(csp_plugin, L"FailureReason",\r
-                                      &info->failure_reason)))\r
-            info->failure_reason = 0;\r
-\r
-        khc_close_space(csp_plugin);\r
-    }\r
-\r
-    info->state = pi->state;\r
-\r
-    kmm_hold_plugin(p);\r
-    info->h_plugin = p;\r
-\r
-    info->flags = (pi->flags & KMM_PLUGIN_FLAG_DISABLED);\r
-\r
- _cleanup:\r
-    LeaveCriticalSection(&cs_kmm);\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI\r
-kmm_release_plugin_info_i(kmm_plugin_info * info) {\r
-    khm_int32 rv;\r
-\r
-    if (!info || !info->h_plugin)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    rv = kmm_release_plugin(info->h_plugin);\r
-\r
-    ZeroMemory(info, sizeof(info));\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI\r
-kmm_get_next_plugin(kmm_plugin p, kmm_plugin * p_next) {\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    kmm_plugin_i * pi;\r
-    kmm_plugin_i * pi_next = NULL;\r
-    kmm_module_i * m;\r
-\r
-    EnterCriticalSection(&cs_kmm);\r
-    if (p == NULL) {\r
-        if (kmm_listed_plugins)\r
-            pi_next = kmm_listed_plugins;\r
-        else {\r
-            for (m = kmm_all_modules; m; m = LNEXT(m)) {\r
-                if (m->plugins) {\r
-                    pi_next = m->plugins;\r
-                    break;\r
-                }\r
-            }\r
-        }\r
-    } else if (kmm_is_plugin(p)) {\r
-        pi = kmm_plugin_from_handle(p);\r
-        pi_next = LNEXT(pi);\r
-\r
-        if (!pi_next) {\r
-            /* we have either exhausted the listed plugins or we are\r
-               at the end of the module's plugin list */\r
-            if (pi->module) {\r
-                m = LNEXT(pi->module);\r
-            } else {\r
-                m = kmm_all_modules;\r
-            }\r
-\r
-            for(; m; m = LNEXT(m)) {\r
-                if (m->plugins) {\r
-                    pi_next = m->plugins;\r
-                    break;\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-    if (pi_next) {\r
-        *p_next = kmm_handle_from_plugin(pi_next);\r
-        kmm_hold_plugin(*p_next);\r
-    } else {\r
-        *p_next = NULL;\r
-        rv = KHM_ERROR_NOT_FOUND;\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_kmm);\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_release_plugin(kmm_plugin p)\r
-{\r
-    kmm_plugin_i * pi;\r
-\r
-    if(!kmm_is_plugin(p))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_kmm);\r
-    pi = kmm_plugin_from_handle(p);\r
-    pi->refcount--;\r
-    if(pi->refcount == 0) {\r
-        kmmint_free_plugin(pi);\r
-    }\r
-    LeaveCriticalSection(&cs_kmm);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_provide_plugin(kmm_module module, kmm_plugin_reg * plugin)\r
-{\r
-    kmm_module_i * m;\r
-    kmm_plugin_i * p;\r
-    size_t cb_name = 0;\r
-    size_t cb_desc = 0;\r
-    size_t cb_dep = 0;\r
-\r
-    m = kmm_module_from_handle(module);\r
-\r
-    /* can only called when handing init_module() */\r
-    if(m->state != KMM_MODULE_STATE_INIT)\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-\r
-    if(!plugin || \r
-       FAILED(StringCbLength(plugin->name, KMM_MAXCB_NAME - sizeof(wchar_t), \r
-                             &cb_name)) ||\r
-       (plugin->description && \r
-        FAILED(StringCbLength(plugin->description, \r
-                              KMM_MAXCB_DESC - sizeof(wchar_t), \r
-                              &cb_desc))) ||\r
-       (plugin->dependencies && \r
-        KHM_FAILED(multi_string_length_cb(plugin->dependencies, \r
-                                          KMM_MAXCB_DEPS, &cb_dep)))) {\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    cb_name += sizeof(wchar_t);\r
-    cb_desc += sizeof(wchar_t);\r
-\r
-    p = kmmint_get_plugin_i(plugin->name);\r
-\r
-    /* released below or in kmmint_init_module() */\r
-    kmm_hold_plugin(kmm_handle_from_plugin(p));\r
-\r
-    if(p->state != KMM_PLUGIN_STATE_NONE &&\r
-        p->state != KMM_PLUGIN_STATE_PLACEHOLDER)\r
-    {\r
-        kmm_release_plugin(kmm_handle_from_plugin(p));\r
-        return KHM_ERROR_DUPLICATE;\r
-    }\r
-\r
-    /* released when the plugin quits */\r
-    kmm_hold_module(module);\r
-\r
-    p->module = m;\r
-    p->p.flags = plugin->flags;\r
-    p->p.msg_proc = plugin->msg_proc;\r
-    p->p.type = plugin->type;\r
-\r
-    if(plugin->description) {\r
-        p->p.description = PMALLOC(cb_desc);\r
-        StringCbCopy(p->p.description, cb_desc, plugin->description);\r
-    } else\r
-        p->p.description = NULL;\r
-\r
-    if(plugin->dependencies) {\r
-        p->p.dependencies = PMALLOC(cb_dep);\r
-        multi_string_copy_cb(p->p.dependencies, cb_dep, plugin->dependencies);\r
-    } else\r
-        p->p.dependencies = NULL;\r
-\r
-    p->p.module = p->module->name;\r
-\r
-    p->p.icon = plugin->icon;\r
-\r
-    p->state = KMM_PLUGIN_STATE_REG;\r
-\r
-    kmmint_delist_plugin(p);\r
-    EnterCriticalSection(&cs_kmm);\r
-    LPUSH(&(m->plugins), p);\r
-    p->flags |= KMM_PLUGIN_FLAG_IN_MODLIST;\r
-    LeaveCriticalSection(&cs_kmm);\r
-\r
-    /* leave the plugin held because it is in the module's plugin list */\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 * 
+kmmint_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 = PMALLOC(sizeof(kmm_plugin_i));
+        ZeroMemory(p, sizeof(kmm_plugin_i));
+        p->magic = KMM_PLUGIN_MAGIC;
+        p->p.name = PMALLOC(cb);
+        StringCbCopy(p->p.name, cb, name);
+        p->state = KMM_PLUGIN_STATE_NONE;
+
+        hash_add(hash_plugins, (void *) p->p.name, (void *) p);
+        kmmint_list_plugin(p);
+    }
+    LeaveCriticalSection(&cs_kmm);
+
+    return p;
+}
+
+kmm_plugin_i * 
+kmmint_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 
+kmmint_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 
+kmmint_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_PARAM;
+
+    EnterCriticalSection(&cs_kmm);
+    pi = kmm_plugin_from_handle(p);
+    pi->refcount++;
+    LeaveCriticalSection(&cs_kmm);
+
+    return KHM_ERROR_SUCCESS;
+}
+
+/* called with cs_kmm held */
+void 
+kmmint_free_plugin(kmm_plugin_i * pi)
+{
+    int i;
+    pi->magic = 0;
+
+    hash_del(hash_plugins, (void *) pi->p.name);
+
+    kmmint_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)
+        PFREE(pi->p.name);
+    pi->p.name = NULL;
+
+    if(pi->p.description)
+        PFREE(pi->p.description);
+    pi->p.description = NULL;
+
+    if(pi->p.dependencies)
+        PFREE(pi->p.dependencies);
+    pi->p.dependencies = NULL;
+
+    PFREE(pi);
+}
+
+KHMEXP khm_int32   KHMAPI
+kmm_enable_plugin(kmm_plugin p, khm_boolean enable) {
+    kmm_plugin_i * pi;
+    khm_int32 rv = KHM_ERROR_NOT_FOUND; /* default to error */
+    khm_handle csp_plugin = NULL;
+
+    EnterCriticalSection(&cs_kmm);
+    if (!kmm_is_plugin(p)) {
+        rv = KHM_ERROR_INVALID_PARAM;
+        goto _cleanup;
+    }
+
+    pi = kmm_plugin_from_handle(p);
+
+    if (KHM_FAILED(rv = kmm_get_plugin_config(pi->p.name, 0, &csp_plugin))) {
+        goto _cleanup;
+    }
+
+    if (KHM_FAILED(rv = khc_write_int32(csp_plugin, L"Disabled", !enable))) {
+        goto _cleanup;
+    }
+
+    rv = KHM_ERROR_SUCCESS;
+
+ _cleanup:
+    LeaveCriticalSection(&cs_kmm);
+
+    if (csp_plugin)
+        khc_close_space(csp_plugin);
+
+    return rv;
+}
+
+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_PARAM;
+
+    EnterCriticalSection(&cs_kmm);
+    if (!kmm_is_plugin(p)) {
+        rv = KHM_ERROR_INVALID_PARAM;
+        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;
+
+    kmm_hold_plugin(p);
+    info->h_plugin = p;
+
+    info->flags = (pi->flags & KMM_PLUGIN_FLAG_DISABLED);
+
+ _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_PARAM;
+
+    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_PARAM;
+
+    EnterCriticalSection(&cs_kmm);
+    pi = kmm_plugin_from_handle(p);
+    pi->refcount--;
+    if(pi->refcount == 0) {
+        kmmint_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_PARAM;
+    }
+
+    cb_name += sizeof(wchar_t);
+    cb_desc += sizeof(wchar_t);
+
+    p = kmmint_get_plugin_i(plugin->name);
+
+    /* released below or in kmmint_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 = PMALLOC(cb_desc);
+        StringCbCopy(p->p.description, cb_desc, plugin->description);
+    } else
+        p->p.description = NULL;
+
+    if(plugin->dependencies) {
+        p->p.dependencies = PMALLOC(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;
+
+    kmmint_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;
+}
+
index 60159df37c9e8a06b926f34acfae87968c72fde7..66354229fb55a8033b8ec28c51839a20d2ccfca3 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2006 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kmminternal.h>\r
-\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_get_module_info(wchar_t * module_name, khm_int32 flags, \r
-                    kmm_module_info * buffer, khm_size * cb_buffer)\r
-{\r
-    /*TODO:Implement this */\r
-    return KHM_ERROR_NOT_IMPLEMENTED;\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_get_plugin_info(wchar_t * plugin_name, \r
-                    kmm_plugin_info * buffer, khm_size * cb_buffer)\r
-{\r
-    /*TODO:Implement this */\r
-    return KHM_ERROR_NOT_IMPLEMENTED;\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_get_plugins_config(khm_int32 flags, khm_handle * result) {\r
-    khm_handle csp_root;\r
-    khm_handle csp_plugins;\r
-    khm_int32 rv;\r
-\r
-    rv = khc_open_space(KHM_INVALID_HANDLE, KMM_CSNAME_ROOT, flags, &csp_root);\r
-\r
-    if(KHM_FAILED(rv))\r
-        return rv;\r
-\r
-    rv = khc_open_space(csp_root, KMM_CSNAME_PLUGINS, flags, &csp_plugins);\r
-    khc_close_space(csp_root);\r
-\r
-    if(KHM_SUCCEEDED(rv))\r
-        *result = csp_plugins;\r
-    else\r
-        *result = NULL;\r
-\r
-    return rv;\r
-}\r
-\r
-\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_get_modules_config(khm_int32 flags, khm_handle * result) {\r
-    khm_handle croot;\r
-    khm_handle kmm_all_modules;\r
-    khm_int32 rv;\r
-\r
-    rv = khc_open_space(NULL, KMM_CSNAME_ROOT, flags, &croot);\r
-\r
-    if(KHM_FAILED(rv))\r
-        return rv;\r
-\r
-    rv = khc_open_space(croot, KMM_CSNAME_MODULES, flags, &kmm_all_modules);\r
-    khc_close_space(croot);\r
-\r
-    if(KHM_SUCCEEDED(rv))\r
-        *result = kmm_all_modules;\r
-    else\r
-        *result = NULL;\r
-\r
-    return rv;\r
-}\r
-\r
-\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_get_plugin_config(wchar_t * plugin, khm_int32 flags, khm_handle * result)\r
-{\r
-    khm_handle csplugins;\r
-    khm_handle csplugin;\r
-    khm_int32 rv;\r
-\r
-    if(!plugin || wcschr(plugin, L'/') || wcschr(plugin, L'\\'))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(KHM_FAILED(kmm_get_plugins_config(flags, &csplugins)))\r
-        return KHM_ERROR_UNKNOWN;\r
-\r
-    rv = khc_open_space(csplugins, plugin, flags, &csplugin);\r
-    *result = csplugin;\r
-\r
-    khc_close_space(csplugins);\r
-\r
-    return rv;\r
-}\r
-\r
-\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_get_module_config(wchar_t * module, khm_int32 flags, khm_handle * result)\r
-{\r
-    khm_handle csmodules;\r
-    khm_handle csmodule;\r
-    khm_int32 rv;\r
-\r
-    if(!module || wcschr(module, L'/') || wcschr(module, L'\\'))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(KHM_FAILED(kmm_get_modules_config(flags, &csmodules)))\r
-        return KHM_ERROR_UNKNOWN;\r
-\r
-    rv = khc_open_space(csmodules, module, flags, &csmodule);\r
-    *result = csmodule;\r
-\r
-    khc_close_space(csmodules);\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_register_plugin(kmm_plugin_reg * plugin, khm_int32 config_flags)\r
-{\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    khm_handle csp_plugin = NULL;\r
-    khm_handle csp_module = NULL;\r
-    size_t cch;\r
-\r
-    /* avoid accidently creating the module key if it doesn't exist */\r
-    config_flags &= ~KHM_FLAG_CREATE;\r
-\r
-    if((plugin == NULL) ||\r
-       (plugin->dependencies && \r
-        KHM_FAILED(multi_string_length_cch(plugin->dependencies, \r
-                                           KMM_MAXCCH_DEPS, &cch))) ||\r
-       FAILED(StringCchLength(plugin->module, KMM_MAXCCH_NAME, &cch)) ||\r
-       (plugin->description &&\r
-        FAILED(StringCchLength(plugin->description,\r
-                               KMM_MAXCCH_DESC, &cch))) ||\r
-       FAILED(StringCchLength(plugin->name, KMM_MAXCCH_NAME, &cch)))\r
-    {\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    /* note that we are retaining the length of the plugin name in\r
-       chars in cch */\r
-    cch ++;\r
-\r
-#define CKRV if(KHM_FAILED(rv)) goto _exit\r
-\r
-    rv = kmm_get_plugin_config(plugin->name, \r
-                               config_flags | KHM_FLAG_CREATE, &csp_plugin);\r
-    CKRV;\r
-\r
-    /* should fail if the module key doesn't exist */\r
-    rv = kmm_get_module_config(plugin->module, config_flags, &csp_module);\r
-    CKRV;\r
-\r
-    /*TODO: Make sure that the module registration is in the same\r
-      config store as the one in which the plugin is going to be\r
-      registered */\r
-\r
-    rv = khc_write_string(csp_plugin, L"Module", plugin->module);\r
-    CKRV;\r
-    if(plugin->description) {\r
-        rv = khc_write_string(csp_plugin, L"Description", plugin->description);\r
-        CKRV;\r
-    }\r
-\r
-    if(plugin->dependencies) {\r
-        rv = khc_write_multi_string(csp_plugin, L"Dependencies", \r
-                                    plugin->dependencies);\r
-        CKRV;\r
-    }\r
-\r
-    rv = khc_write_int32(csp_plugin, L"Type", plugin->type);\r
-    CKRV;\r
-    rv = khc_write_int32(csp_plugin, L"Disabled",\r
-                         !!(plugin->flags & KMM_PLUGIN_FLAG_DISABLED));\r
-    CKRV;\r
-\r
-    {\r
-        khm_size cb = 0;\r
-        wchar_t * pl = NULL;\r
-        size_t scb = 0;\r
-\r
-        rv = khc_read_multi_string(csp_module, L"PluginList", NULL, &cb);\r
-        if(rv != KHM_ERROR_TOO_LONG) {\r
-            if (rv == KHM_ERROR_NOT_FOUND) {\r
-\r
-                scb = cb = cch * sizeof(wchar_t);\r
-                pl = PMALLOC(cb);\r
-                multi_string_init(pl, cb);\r
-                rv = KHM_ERROR_SUCCESS;\r
-\r
-                goto add_plugin_to_list;\r
-\r
-            } else {\r
-                goto _exit;\r
-            }\r
-        }\r
-\r
-        cb += cch * sizeof(wchar_t);\r
-        scb = cb;\r
-\r
-        pl = PMALLOC(cb);\r
-\r
-        rv = khc_read_multi_string(csp_module, L"PluginList", pl, &cb);\r
-        if(KHM_FAILED(rv)) {\r
-            if(pl)\r
-                PFREE(pl);\r
-            goto _exit;\r
-        }\r
-\r
-    add_plugin_to_list:\r
-\r
-        if(!multi_string_find(pl, plugin->name, 0)) {\r
-            multi_string_append(pl, &scb, plugin->name);\r
-            rv = khc_write_multi_string(csp_module, L"PluginList", pl);\r
-        }\r
-\r
-        PFREE(pl);\r
-        CKRV;\r
-    }\r
-\r
-#undef CKRV\r
-\r
-_exit:\r
-    if(csp_plugin)\r
-        khc_close_space(csp_plugin);\r
-    if(csp_module)\r
-        khc_close_space(csp_module);\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_register_module(kmm_module_reg * module, khm_int32 config_flags)\r
-{\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    khm_handle csp_module = NULL;\r
-    size_t cch;\r
-    int i;\r
-\r
-    if((module == NULL) ||\r
-        FAILED(StringCchLength(module->name, KMM_MAXCCH_NAME, &cch)) ||\r
-        (module->description && \r
-            FAILED(StringCchLength(module->description, \r
-                                   KMM_MAXCCH_DESC, &cch))) ||\r
-        FAILED(StringCchLength(module->path, MAX_PATH, &cch)) ||\r
-        (module->n_plugins > 0 && module->plugin_reg_info == NULL)) {\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-#define CKRV if(KHM_FAILED(rv)) goto _exit\r
-\r
-    rv = kmm_get_module_config(module->name, config_flags | KHM_FLAG_CREATE, \r
-                               &csp_module);\r
-    CKRV;\r
-\r
-    rv = khc_write_string(csp_module, L"ImagePath", module->path);\r
-    CKRV;\r
-\r
-    rv = khc_write_int32(csp_module, L"Disabled", 0);\r
-    CKRV;\r
-\r
-    /* FileVersion and ProductVersion will be set when the module\r
-       is loaded for the first time */\r
-\r
-    for(i=0; i<module->n_plugins; i++) {\r
-        rv = kmm_register_plugin(&(module->plugin_reg_info[i]), config_flags);\r
-        CKRV;\r
-    }\r
-\r
-#undef CKRV\r
-_exit:\r
-    if(csp_module)\r
-        khc_close_space(csp_module);\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_unregister_plugin(wchar_t * plugin, khm_int32 config_flags)\r
-{\r
-    khm_handle csp_plugin = NULL;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    rv = kmm_get_plugin_config(plugin, config_flags, &csp_plugin);\r
-\r
-    if (KHM_FAILED(rv))\r
-        goto _cleanup;\r
-\r
-    rv = khc_remove_space(csp_plugin);\r
-\r
- _cleanup:\r
-\r
-    if (csp_plugin)\r
-        khc_close_space(csp_plugin);\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32   KHMAPI \r
-kmm_unregister_module(wchar_t * module, khm_int32 config_flags)\r
-{\r
-    khm_handle csp_module = NULL;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    rv = kmm_get_module_config(module, config_flags, &csp_module);\r
-\r
-    if (KHM_FAILED(rv))\r
-        goto _cleanup;\r
-\r
-    rv = khc_remove_space(csp_module);\r
-\r
- _cleanup:\r
-    if (csp_module)\r
-        khc_close_space(csp_module);\r
-\r
-    return rv;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2006 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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_PARAM;
+
+    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_PARAM;
+
+    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, &cch)) ||
+       (plugin->description &&
+        FAILED(StringCchLength(plugin->description,
+                               KMM_MAXCCH_DESC, &cch))) ||
+       FAILED(StringCchLength(plugin->name, KMM_MAXCCH_NAME, &cch)))
+    {
+        return KHM_ERROR_INVALID_PARAM;
+    }
+
+    /* 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"Disabled",
+                         !!(plugin->flags & KMM_PLUGIN_FLAG_DISABLED));
+    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) {
+            if (rv == KHM_ERROR_NOT_FOUND) {
+
+                scb = cb = cch * sizeof(wchar_t);
+                pl = PMALLOC(cb);
+                multi_string_init(pl, cb);
+                rv = KHM_ERROR_SUCCESS;
+
+                goto add_plugin_to_list;
+
+            } else {
+                goto _exit;
+            }
+        }
+
+        cb += cch * sizeof(wchar_t);
+        scb = cb;
+
+        pl = PMALLOC(cb);
+
+        rv = khc_read_multi_string(csp_module, L"PluginList", pl, &cb);
+        if(KHM_FAILED(rv)) {
+            if(pl)
+                PFREE(pl);
+            goto _exit;
+        }
+
+    add_plugin_to_list:
+
+        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);
+        }
+
+        PFREE(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, &cch)) ||
+        (module->description && 
+            FAILED(StringCchLength(module->description, 
+                                   KMM_MAXCCH_DESC, &cch))) ||
+        FAILED(StringCchLength(module->path, MAX_PATH, &cch)) ||
+        (module->n_plugins > 0 && module->plugin_reg_info == NULL)) {
+        return KHM_ERROR_INVALID_PARAM;
+    }
+
+#define CKRV if(KHM_FAILED(rv)) goto _exit
+
+    rv = kmm_get_module_config(module->name, config_flags | KHM_FLAG_CREATE, 
+                               &csp_module);
+    CKRV;
+
+    rv = khc_write_string(csp_module, L"ImagePath", module->path);
+    CKRV;
+
+    rv = khc_write_int32(csp_module, L"Disabled", 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)
+{
+    khm_handle csp_plugin = NULL;
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+
+    rv = kmm_get_plugin_config(plugin, config_flags, &csp_plugin);
+
+    if (KHM_FAILED(rv))
+        goto _cleanup;
+
+    rv = khc_remove_space(csp_plugin);
+
+ _cleanup:
+
+    if (csp_plugin)
+        khc_close_space(csp_plugin);
+
+    return rv;
+}
+
+KHMEXP khm_int32   KHMAPI 
+kmm_unregister_module(wchar_t * module, khm_int32 config_flags)
+{
+    khm_handle csp_module = NULL;
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+
+    rv = kmm_get_module_config(module, config_flags, &csp_module);
+
+    if (KHM_FAILED(rv))
+        goto _cleanup;
+
+    rv = khc_remove_space(csp_module);
+
+ _cleanup:
+    if (csp_module)
+        khc_close_space(csp_module);
+
+    return rv;
+}
index 636e8579f634d7b74f00b99c2c81d77118677179..0aa4b425f121b65a85a8436a0202493275a27516 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kmminternal.h>\r
-#ifdef DEBUG\r
-#include<assert.h>\r
-#endif\r
-\r
-static LONG pending_modules = 0;\r
-static LONG pending_plugins = 0;\r
-static LONG startup_signal = 0;\r
-static BOOL load_done = FALSE;\r
-\r
-void\r
-kmmint_check_completion(void) {\r
-    if (pending_modules == 0 &&\r
-        pending_plugins == 0 &&\r
-        InterlockedIncrement(&startup_signal) == 1) {\r
-\r
-        load_done = TRUE;\r
-\r
-        /* TODO: check for orphaned plugins */\r
-\r
-        kmq_post_message(KMSG_KMM, KMSG_KMM_I_DONE, 0, 0);\r
-    }\r
-}\r
-\r
-void\r
-kmmint_add_to_module_queue(void) {\r
-    InterlockedIncrement(&pending_modules);\r
-}\r
-\r
-void\r
-kmmint_remove_from_module_queue(void) {\r
-\r
-    InterlockedDecrement(&pending_modules);\r
-\r
-    kmmint_check_completion();\r
-}\r
-\r
-void\r
-kmmint_add_to_plugin_queue(kmm_plugin_i * plugin) {\r
-    EnterCriticalSection(&cs_kmm);\r
-    if (!(plugin->flags & KMM_PLUGIN_FLAG_IN_QUEUE)) {\r
-        InterlockedIncrement(&pending_plugins);\r
-        plugin->flags |= KMM_PLUGIN_FLAG_IN_QUEUE;\r
-    }\r
-    LeaveCriticalSection(&cs_kmm);\r
-}\r
-\r
-void\r
-kmmint_remove_from_plugin_queue(kmm_plugin_i * plugin) {\r
-    EnterCriticalSection(&cs_kmm);\r
-\r
-    if (plugin->flags & KMM_PLUGIN_FLAG_IN_QUEUE) {\r
-        InterlockedDecrement(&pending_plugins);\r
-        plugin->flags &= ~KMM_PLUGIN_FLAG_IN_QUEUE;\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_kmm);\r
-    kmmint_check_completion();\r
-}\r
-\r
-KHMEXP khm_boolean  KHMAPI\r
-kmm_load_pending(void) {\r
-    return !load_done;\r
-}\r
-\r
-/*! \internal\r
-  \brief Message handler for the registrar thread. */\r
-khm_boolean KHMAPI kmmint_reg_cb(khm_int32 msg_type, \r
-                                 khm_int32 msg_sub_type, \r
-                                 khm_ui_4 uparam,\r
-                                 void *vparam)\r
-{\r
-    /* we should only be getting <KMSG_KMM,KMSG_KMM_I_REG> anyway */\r
-    if(msg_type != KMSG_KMM || msg_sub_type != KMSG_KMM_I_REG)\r
-        return FALSE;\r
-\r
-    switch(uparam) {\r
-        case KMM_REG_INIT_MODULE:\r
-            kmmint_init_module((kmm_module_i *) vparam);\r
-            kmm_release_module(kmm_handle_from_module((kmm_module_i *) vparam));\r
-            break;\r
-\r
-        case KMM_REG_EXIT_MODULE:\r
-            kmmint_exit_module((kmm_module_i *) vparam);\r
-            kmm_release_module(kmm_handle_from_module((kmm_module_i *) vparam));\r
-            break;\r
-\r
-        case KMM_REG_INIT_PLUGIN:\r
-            kmmint_init_plugin((kmm_plugin_i *) vparam);\r
-            kmm_release_plugin(kmm_handle_from_plugin((kmm_plugin_i *) vparam));\r
-            break;\r
-\r
-        case KMM_REG_EXIT_PLUGIN:\r
-            kmmint_exit_plugin((kmm_plugin_i *) vparam);\r
-            kmm_release_plugin(kmm_handle_from_plugin((kmm_plugin_i *) vparam));\r
-            break;\r
-    }\r
-    return TRUE;\r
-}\r
-\r
-/*! \internal\r
-  \brief The registrar thread.\r
-\r
-  The only thing this function does is to dispatch messages to the\r
-  callback routine ( kmmint_reg_cb() ) */\r
-DWORD WINAPI kmmint_registrar(LPVOID lpParameter)\r
-{\r
-\r
-    PDESCTHREAD(L"KMM Registrar", L"KMM");\r
-\r
-    tid_registrar = GetCurrentThreadId();\r
-\r
-    kmq_subscribe(KMSG_KMM, kmmint_reg_cb);\r
-    kmq_subscribe(KMSG_SYSTEM, kmmint_reg_cb);\r
-\r
-    SetEvent(evt_startup);\r
-\r
-    while(KHM_SUCCEEDED(kmq_dispatch(INFINITE)));\r
-\r
-    kmq_unsubscribe(KMSG_KMM, kmmint_reg_cb);\r
-    kmq_unsubscribe(KMSG_SYSTEM, kmmint_reg_cb);\r
-\r
-    ExitThread(0);\r
-    /* not reached */\r
-    return 0;\r
-}\r
-\r
-/*! \internal\r
-  \brief Manages a plugin message thread.\r
-\r
-  Each plugin gets its own plugin thread which is used to dispatch\r
-  messages to the plugin.  This acts as the thread function for the\r
-  plugin thread.*/\r
-DWORD WINAPI kmmint_plugin_broker(LPVOID lpParameter)\r
-{\r
-    DWORD rv = 0;\r
-    kmm_plugin_i * p = (kmm_plugin_i *) lpParameter;\r
-\r
-    PDESCTHREAD(p->p.name, L"KMM");\r
-\r
-    _begin_task(0);\r
-    _report_mr1(KHERR_NONE, MSG_PB_START, _cstr(p->p.name));\r
-    _describe();\r
-\r
-    TlsSetValue(tls_kmm, (LPVOID) p);\r
-\r
-    kmm_hold_plugin(kmm_handle_from_plugin(p));\r
-\r
-    p->tid_thread = GetCurrentThreadId();\r
-\r
-    if (IsBadCodePtr(p->p.msg_proc)) {\r
-        _report_mr0(KHERR_WARNING, MSG_PB_INVALID_CODE_PTR);\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-    } else {\r
-        rv = (*p->p.msg_proc)(KMSG_SYSTEM, KMSG_SYSTEM_INIT, \r
-                              0, (void *) &(p->p));\r
-        _report_mr1(KHERR_INFO, MSG_PB_INIT_RV, _int32(rv));\r
-    }\r
-\r
-    /* if it fails to initialize, we exit the plugin */\r
-    if(KHM_FAILED(rv)) {\r
-\r
-        kherr_report(KHERR_ERROR,\r
-                     (wchar_t *) MSG_PB_INIT_FAIL_S,\r
-                     (wchar_t *) KHERR_FACILITY,\r
-                     NULL,\r
-                     (wchar_t *) MSG_PB_INIT_FAIL,\r
-                     (wchar_t *) MSG_PB_INIT_FAIL_G,\r
-                     KHERR_FACILITY_ID,\r
-                     KHERR_SUGGEST_NONE,\r
-                     _cstr(p->p.name),\r
-                     _cstr(p->p.description),\r
-                     _cstr(p->module->path),\r
-                     _cstr(p->module->support),\r
-                     KHERR_RF_MSG_SHORT_DESC |\r
-                     KHERR_RF_MSG_LONG_DESC |\r
-                     KHERR_RF_MSG_SUGGEST\r
-#ifdef _WIN32\r
-                     ,KHERR_HMODULE\r
-#endif\r
-                     );\r
-        _resolve();\r
-\r
-        /* exit the plugin first.  Otherwise it may not uninitialize correctly */\r
-        (*p->p.msg_proc)(KMSG_SYSTEM, KMSG_SYSTEM_EXIT, 0, (void *) &(p->p));\r
-\r
-        kmmint_remove_from_plugin_queue(p);\r
-        rv = 1;\r
-        _end_task();\r
-\r
-        p->state = KMM_PLUGIN_STATE_FAIL_INIT;\r
-        goto _exit;\r
-    }\r
-\r
-    /* subscribe to default message classes by plugin type */\r
-    if(p->p.type == KHM_PITYPE_CRED) {\r
-        kmq_subscribe(KMSG_SYSTEM, p->p.msg_proc);\r
-        kmq_subscribe(KMSG_KCDB, p->p.msg_proc);\r
-        kmq_subscribe(KMSG_CRED, p->p.msg_proc);\r
-    } else if(p->p.type == KHM_PITYPE_IDENT) {\r
-        khm_handle h = NULL;\r
-\r
-        kmq_subscribe(KMSG_SYSTEM, p->p.msg_proc);\r
-        kmq_subscribe(KMSG_KCDB, p->p.msg_proc);\r
-\r
-        kmq_create_subscription(p->p.msg_proc, &h);\r
-        kcdb_identity_set_provider(h);\r
-        /* kcdb deletes the subscription when it's done with it */\r
-    } else if(p->p.type == KHM_PITYPE_CONFIG) {\r
-        /*TODO: subscribe to configuration provider messages here */\r
-    }\r
-\r
-    p->state = KMM_PLUGIN_STATE_RUNNING;\r
-\r
-    _report_mr0(KHERR_INFO, MSG_PB_INIT_DONE);\r
-\r
-    _end_task();\r
-\r
-    /* if there were any plugins that were waiting for this one to\r
-       start, we should start them too */\r
-    EnterCriticalSection(&cs_kmm);\r
-    do {\r
-        kmm_plugin_i * pd;\r
-        int i;\r
-\r
-        for(i=0; i < p->n_dependants; i++) {\r
-            pd = p->dependants[i];\r
-\r
-            pd->n_unresolved--;\r
-\r
-            if(pd->n_unresolved == 0) {\r
-                kmmint_add_to_plugin_queue(pd);\r
-                kmm_hold_plugin(kmm_handle_from_plugin(pd));\r
-                kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, KMM_REG_INIT_PLUGIN, (void *) pd);\r
-            }\r
-        }\r
-    } while(FALSE);\r
-    LeaveCriticalSection(&cs_kmm);\r
-\r
-    kmmint_remove_from_plugin_queue(p);\r
-\r
-    /* main message loop */\r
-    while(KHM_SUCCEEDED(kmq_dispatch(INFINITE)));\r
-\r
-    /* unsubscribe from default message classes by plugin type */\r
-    if(p->p.type == KHM_PITYPE_CRED) {\r
-        kmq_unsubscribe(KMSG_SYSTEM, p->p.msg_proc);\r
-        kmq_unsubscribe(KMSG_KCDB, p->p.msg_proc);\r
-        kmq_unsubscribe(KMSG_CRED, p->p.msg_proc);\r
-    } else if (p->p.type == KHM_PITYPE_IDENT) {\r
-        kmq_unsubscribe(KMSG_KCDB, p->p.msg_proc);\r
-        kmq_unsubscribe(KMSG_SYSTEM, p->p.msg_proc);\r
-        kcdb_identity_set_provider(NULL);\r
-    } else if(p->p.type == KHM_PITYPE_CONFIG) {\r
-        /*TODO: unsubscribe from configuration provider messages here */\r
-    }\r
-\r
-    p->p.msg_proc(KMSG_SYSTEM, KMSG_SYSTEM_EXIT, 0, (void *) &(p->p));\r
-\r
- _exit:\r
-    if (p->state >= 0)\r
-        p->state = KMM_PLUGIN_STATE_EXITED;\r
-\r
-    /* the following call will automatically release the plugin */\r
-    kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, \r
-                     KMM_REG_EXIT_PLUGIN, (void *) p);\r
-\r
-    TlsSetValue(tls_kmm, (LPVOID) 0);\r
-\r
-    ExitThread(rv);\r
-\r
-    /* not reached */\r
-    return rv;\r
-}\r
-\r
-/*! \internal\r
-  \brief Initialize a plugin\r
-\r
-  \note If kmmint_init_plugin() is called on a plugin, then kmmint_exit_plugin()\r
-      \b must be called for the plugin.\r
-\r
-  \note Should only be called from the context of the registrar thread */\r
-void kmmint_init_plugin(kmm_plugin_i * p) {\r
-    DWORD dummy;\r
-    khm_handle csp_plugin   = NULL;\r
-    khm_handle csp_plugins  = NULL;\r
-    khm_int32 t;\r
-\r
-    /* the following will be undone in kmmint_exit_plugin() */\r
-    kmm_hold_plugin(kmm_handle_from_plugin(p));\r
-\r
-    EnterCriticalSection(&cs_kmm);\r
-    if(p->state != KMM_PLUGIN_STATE_REG &&\r
-        p->state != KMM_PLUGIN_STATE_HOLD)\r
-    {\r
-        LeaveCriticalSection(&cs_kmm);\r
-        goto _exit;\r
-    }\r
-\r
-    _begin_task(0);\r
-    _report_mr1(KHERR_NONE, MSG_IP_TASK_DESC, _cstr(p->p.name));\r
-    _describe();\r
-\r
-    if(p->state == KMM_PLUGIN_STATE_HOLD) {\r
-        /* if this plugin was held, then we already had a hold\r
-           from the initial attempt to start the plugin.  Undo\r
-           the hold we did a few lines earlier. */\r
-        kmm_release_plugin(kmm_handle_from_plugin(p));\r
-\r
-        /* same for the plugin count for the module. */\r
-#ifdef DEBUG\r
-        assert(p->flags & KMM_PLUGIN_FLAG_IN_MODCOUNT);\r
-#endif\r
-        p->module->plugin_count--;\r
-        p->flags &= ~KMM_PLUGIN_FLAG_IN_MODCOUNT;\r
-    }\r
-\r
-    p->state = KMM_PLUGIN_STATE_PREINIT;\r
-\r
-    kmmint_delist_plugin(p);\r
-    kmmint_list_plugin(p);\r
-\r
-    LeaveCriticalSection(&cs_kmm);\r
-\r
-    if(KHM_FAILED(kmm_get_plugins_config(0, &csp_plugins))) {\r
-        _report_mr0(KHERR_ERROR, MSG_IP_GET_CONFIG);\r
-\r
-        p->state = KMM_PLUGIN_STATE_FAIL_UNKNOWN;\r
-        goto _exit;\r
-    }\r
-\r
-    if(KHM_FAILED(kmm_get_plugin_config(p->p.name, 0, &csp_plugin))) {\r
-        if(KHM_FAILED(kmm_register_plugin(&(p->p), 0))) {\r
-            _report_mr0(KHERR_ERROR, MSG_IP_NOT_REGISTERED);\r
-\r
-            p->state = KMM_PLUGIN_STATE_FAIL_NOT_REGISTERED;\r
-            goto _exit;\r
-        }\r
-        \r
-        if(KHM_FAILED(kmm_get_plugin_config(p->p.name, 0, &csp_plugin))) {\r
-            _report_mr0(KHERR_ERROR, MSG_IP_NOT_REGISTERED);\r
-\r
-            p->state = KMM_PLUGIN_STATE_FAIL_NOT_REGISTERED;\r
-            goto _exit;\r
-        }\r
-    }\r
-\r
-    if (KHM_SUCCEEDED(khc_read_int32(csp_plugin, L"Disabled", &t)) && t) {\r
-        p->flags |= KMM_PLUGIN_FLAG_DISABLED;\r
-        p->state = KMM_PLUGIN_STATE_FAIL_DISABLED;\r
-        goto _exit;\r
-    }\r
-\r
-#if 0\r
-    /*TODO: check the failure count and act accordingly */\r
-    if(KHM_SUCCEEDED(khc_read_int32(csp_plugin, L"FailureCount", &t)) && (t > 0)) {\r
-    }\r
-#endif\r
-\r
-    EnterCriticalSection(&cs_kmm);\r
-\r
-    p->n_depends = 0;\r
-    p->n_unresolved = 0;\r
-    \r
-    do {\r
-        wchar_t * deps = NULL;\r
-        wchar_t * d;\r
-        khm_size sz = 0;\r
-\r
-        if(khc_read_multi_string(csp_plugin, L"Dependencies", \r
-                                 NULL, &sz) != KHM_ERROR_TOO_LONG)\r
-            break;\r
-\r
-        deps = PMALLOC(sz);\r
-        if(KHM_FAILED(khc_read_multi_string(csp_plugin, L"Dependencies", \r
-                                            deps, &sz))) {\r
-            if(deps)\r
-                PFREE(deps);\r
-            break;\r
-        }\r
-\r
-        for(d = deps; d && *d; d = multi_string_next(d)) {\r
-            kmm_plugin_i * pd;\r
-            int i;\r
-\r
-            pd = kmmint_get_plugin_i(d);\r
-\r
-            if(pd->state == KMM_PLUGIN_STATE_NONE) {\r
-                /* the dependant was not previously known */\r
-                pd->state = KMM_PLUGIN_STATE_PLACEHOLDER;\r
-            }\r
-\r
-            for(i=0; i < pd->n_dependants; i++) {\r
-                if(pd->dependants[i] == p)\r
-                    break;\r
-            }\r
-\r
-            if(i >= pd->n_dependants) {\r
-                if( pd->n_dependants >= KMM_MAX_DEPENDANTS ) {\r
-                    /*TODO: handle this gracefully */\r
-                    RaiseException(1, EXCEPTION_NONCONTINUABLE, 0, NULL);\r
-                }\r
-\r
-                /* released in kmmint_free_plugin() */\r
-                kmm_hold_plugin(kmm_handle_from_plugin(p));\r
-                pd->dependants[pd->n_dependants] = p;\r
-                pd->n_dependants++;\r
-            }\r
-\r
-            p->n_depends++;\r
-\r
-            if(pd->state != KMM_PLUGIN_STATE_RUNNING) {\r
-                p->n_unresolved++;\r
-            }\r
-        }\r
-\r
-        if(p->n_unresolved > 0) {\r
-            p->state = KMM_PLUGIN_STATE_HOLD;\r
-        }\r
-\r
-        PFREE(deps);\r
-\r
-    } while(FALSE);\r
-\r
-#ifdef DEBUG\r
-    assert(!(p->flags & KMM_PLUGIN_FLAG_IN_MODCOUNT));\r
-#endif\r
-    p->flags |= KMM_PLUGIN_FLAG_IN_MODCOUNT;\r
-    p->module->plugin_count++;\r
-\r
-    LeaveCriticalSection(&cs_kmm);\r
-\r
-    if(p->state == KMM_PLUGIN_STATE_HOLD) {\r
-        _report_mr1(KHERR_INFO, MSG_IP_HOLD, _dupstr(p->p.name));\r
-\r
-        goto _exit_post;\r
-    }\r
-\r
-    kmmint_add_to_plugin_queue(p);\r
-\r
-    p->ht_thread = CreateThread(NULL,\r
-                                0,\r
-                                kmmint_plugin_broker,\r
-                                (LPVOID) p,\r
-                                CREATE_SUSPENDED,\r
-                                &dummy);\r
-\r
-    p->state = KMM_PLUGIN_STATE_INIT;\r
-\r
-    ResumeThread(p->ht_thread);\r
-\r
-_exit_post:\r
-    if(csp_plugin != NULL)\r
-        khc_close_space(csp_plugin);\r
-\r
-    if(csp_plugins != NULL)\r
-        khc_close_space(csp_plugins);\r
-\r
-    _report_mr2(KHERR_INFO, MSG_IP_STATE, \r
-                _dupstr(p->p.name), _int32(p->state));\r
-\r
-    _end_task();\r
-    \r
-    return;\r
-\r
-    /* jump here if an error condition happens before the plugin\r
-       broker thread starts and the plugin should be unloaded */\r
-\r
-_exit:\r
-    if(csp_plugin != NULL)\r
-        khc_close_space(csp_plugin);\r
-    if(csp_plugins != NULL)\r
-        khc_close_space(csp_plugins);\r
-\r
-    _report_mr2(KHERR_WARNING, MSG_IP_EXITING, \r
-                _dupstr(p->p.name), _int32(p->state));\r
-    _end_task();\r
-\r
-\r
-#ifdef ASYNC_PLUGIN_UNLOAD_ON_FAILURE\r
-\r
-    kmm_hold_plugin(kmm_handle_from_plugin(p));\r
-\r
-    kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, KMM_REG_EXIT_PLUGIN, (void *) p);\r
-\r
-#else\r
-\r
-    kmmint_exit_plugin(p);\r
-\r
-#endif\r
-}\r
-\r
-/*! \internal\r
-  \brief Uninitialize a plugin\r
-\r
-  In addition to terminating the thread, and removing p from the\r
-  linked list and hashtable, it also frees up p.\r
-   \r
-  \note Should only be called from the context of the registrar thread. */\r
-void kmmint_exit_plugin(kmm_plugin_i * p) {\r
-    int np;\r
-    khm_boolean release_plugin = TRUE;\r
-\r
-    if(p->state == KMM_PLUGIN_STATE_RUNNING ||\r
-       p->state == KMM_PLUGIN_STATE_INIT) {\r
-\r
-        kmq_post_thread_quit_message(p->tid_thread, 0, NULL);\r
-        /* when we post the quit message to the plugin thread, the plugin\r
-           broker terminates the plugin and posts a EXIT_PLUGIN message,\r
-           which calls this function again.  We just exit here because\r
-           the EXIT_PLUGIN message will end up calling us again momentarily */\r
-        return;\r
-\r
-    }\r
-\r
-    if(p->ht_thread) {\r
-        /* wait for the thread to terminate */\r
-        WaitForSingleObject(p->ht_thread, INFINITE);\r
-        p->ht_thread = NULL;\r
-    }\r
-\r
-    EnterCriticalSection(&cs_kmm);\r
-\r
-    /* undo reference count done in kmmint_init_plugin() */\r
-    if(p->flags & KMM_PLUGIN_FLAG_IN_MODCOUNT) {\r
-\r
-        np = --(p->module->plugin_count);\r
-        p->flags &= ~KMM_PLUGIN_FLAG_IN_MODCOUNT;\r
-\r
-    } else {\r
-        /* the plugin was not included in the module's plugin_count.\r
-           We can't base a decision to unload the module based on this\r
-           plugin exiting. */\r
-        np = TRUE;\r
-    }\r
-\r
-    /* The plugin is in an error state.  We need to keep the plugin\r
-       record in tact so that the failure information is kept\r
-       around. */\r
-    if (p->state < KMM_PLUGIN_STATE_NONE) {\r
-        release_plugin = FALSE;\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_kmm);\r
-\r
-    if(!np) {\r
-        /*  if this is the last plugin to exit, then notify the\r
-            registrar that the module should be removed as well */\r
-        kmm_hold_module(kmm_handle_from_module(p->module));\r
-        kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, KMM_REG_EXIT_MODULE, (void *) p->module);\r
-    }\r
-\r
-    /* release the hold obtained in kmmint_init_plugin() */\r
-    if (release_plugin)\r
-        kmm_release_plugin(kmm_handle_from_plugin(p));\r
-}\r
-\r
-/*! \internal\r
-  \brief Initialize a module\r
-\r
-  \a m is not in the linked list yet.\r
-\r
-  \note Should only be called from the context of the registrar thread. */\r
-void kmmint_init_module(kmm_module_i * m) {\r
-    HMODULE hm;\r
-    init_module_t p_init_module;\r
-    kmm_plugin_i * pi;\r
-    khm_int32 rv;\r
-    khm_handle csp_mod = NULL;\r
-    khm_handle csp_mods = NULL;\r
-    khm_size sz;\r
-    khm_int32 i;\r
-\r
-    /* error condition handling */\r
-    BOOL exit_module = FALSE;\r
-    BOOL release_module = TRUE;\r
-    BOOL record_failure = FALSE;\r
-\r
-    /* failure handling */\r
-    khm_int32 max_fail_count = 0;\r
-    khm_int64 fail_reset_time = 0;\r
-\r
-    _begin_task(0);\r
-    _report_mr1(KHERR_NONE, MSG_INIT_MODULE, _cstr(m->name));\r
-    _describe();\r
-\r
-    kmm_hold_module(kmm_handle_from_module(m));\r
-\r
-    if(KHM_FAILED(kmm_get_modules_config(0, &csp_mods))) {\r
-        _report_mr0(KHERR_ERROR, MSG_IM_GET_CONFIG);\r
-        _location(L"kmm_get_modules_config()");\r
-\r
-        m->state = KMM_MODULE_STATE_FAIL_UNKNOWN;\r
-        goto _exit;\r
-    }\r
-\r
-    khc_read_int32(csp_mods, L"ModuleMaxFailureCount", &max_fail_count);\r
-    khc_read_int64(csp_mods, L"ModuleFailureCountResetTime", &fail_reset_time);\r
-\r
-    /* If the module is not in the pre-init state, we can't\r
-       initialize it. */\r
-    if(m->state != KMM_MODULE_STATE_PREINIT) {\r
-        _report_mr1(KHERR_INFO, MSG_IM_NOT_PREINIT, _int32(m->state));\r
-        goto _exit;\r
-    }\r
-\r
-    if(KHM_FAILED(kmm_get_module_config(m->name, 0, &csp_mod))) {\r
-        _report_mr0(KHERR_ERROR, MSG_IM_NOT_REGISTERED);\r
-\r
-        m->state = KMM_MODULE_STATE_FAIL_NOT_REGISTERED;\r
-        goto _exit;\r
-    }\r
-\r
-    if(KHM_SUCCEEDED(khc_read_int32(csp_mod, L"Disabled", &i)) && i) {\r
-        _report_mr0(KHERR_INFO, MSG_IM_DISABLED);\r
-\r
-        m->state = KMM_MODULE_STATE_FAIL_DISABLED;\r
-        goto _exit;\r
-    }\r
-\r
-    if(KHM_SUCCEEDED(khc_read_int32(csp_mod, L"NoUnload", &i)) && i) {\r
-        m->flags |= KMM_MODULE_FLAG_NOUNLOAD;\r
-    }\r
-\r
-    if(KHM_SUCCEEDED(khc_read_int32(csp_mod, L"FailureCount", &i))) {\r
-        khm_int64 tm;\r
-        khm_int64 ct;\r
-        FILETIME fct;\r
-        khm_int32 last_reason = 0;\r
-\r
-        /* reset the failure count if the failure count reset time\r
-           period has elapsed */\r
-        tm = 0;\r
-        khc_read_int64(csp_mod, L"FailureTime", &tm);\r
-        GetSystemTimeAsFileTime(&fct);\r
-\r
-        ct = (FtToInt(&fct) - tm) / 10000000i64;\r
-\r
-        if(tm > 0 && \r
-           ct > fail_reset_time) {\r
-            i = 0;\r
-            khc_write_int32(csp_mod, L"FailureCount", 0);\r
-            khc_write_int64(csp_mod, L"FailureTime", 0);\r
-        }\r
-\r
-        khc_read_int32(csp_mod, L"FailureReason", &last_reason);\r
-\r
-        /* did we exceed the max failure count?  However, we ignore\r
-           the max failure count if the reason why it didn't load the\r
-           last time was because the module wasn't found. */\r
-        if(i > max_fail_count && \r
-           last_reason != KMM_MODULE_STATE_FAIL_NOT_FOUND) {\r
-            /* failed too many times */\r
-            _report_mr0(KHERR_INFO, MSG_IM_MAX_FAIL);\r
-\r
-            m->state = KMM_MODULE_STATE_FAIL_MAX_FAILURE;\r
-            goto _exit;\r
-        }\r
-    }\r
-\r
-    if(khc_read_string(csp_mod, L"ImagePath", NULL, &sz) == \r
-       KHM_ERROR_TOO_LONG) {\r
-        if(m->path)\r
-            PFREE(m->path);\r
-        m->path = PMALLOC(sz);\r
-        khc_read_string(csp_mod, L"ImagePath", m->path, &sz);\r
-    } else {\r
-        _report_mr0(KHERR_ERROR, MSG_IM_NOT_REGISTERED);\r
-\r
-        m->state = KMM_MODULE_STATE_FAIL_NOT_REGISTERED;\r
-        goto _exit;\r
-    }\r
-\r
-    rv = kmmint_read_module_info(m);\r
-\r
-    if (KHM_FAILED(rv)) {\r
-        if (rv == KHM_ERROR_INCOMPATIBLE) {\r
-            _report_mr0(KHERR_ERROR, MSG_IM_INCOMPATIBLE);\r
-\r
-            m->state = KMM_MODULE_STATE_FAIL_INCOMPAT;\r
-        } else if (rv == KHM_ERROR_NOT_FOUND) {\r
-            _report_mr1(KHERR_ERROR, MSG_IM_NOT_FOUND, _dupstr(m->path));\r
-\r
-            m->state = KMM_MODULE_STATE_FAIL_NOT_FOUND;\r
-        } else {\r
-            _report_mr0(KHERR_ERROR, MSG_IM_INVALID_MODULE);\r
-\r
-            m->state = KMM_MODULE_STATE_FAIL_INV_MODULE;\r
-        }\r
-        goto _exit;\r
-    }\r
-\r
-    /* check again */\r
-    if(m->state != KMM_MODULE_STATE_PREINIT) {\r
-        _report_mr0(KHERR_ERROR, MSG_IM_NOT_PREINIT);\r
-\r
-        goto _exit;\r
-    }\r
-\r
-    /* from this point on, we must record any failure codes */\r
-    record_failure = TRUE;\r
-\r
-    hm = LoadLibrary(m->path);\r
-    if(!hm) {\r
-        m->h_module = NULL;\r
-        m->state = KMM_MODULE_STATE_FAIL_NOT_FOUND;\r
-\r
-        _report_mr1(KHERR_ERROR, MSG_IM_NOT_FOUND, _dupstr(m->path));\r
-\r
-        goto _exit;\r
-    }\r
-\r
-    /* from this point on, we need to discard the module through\r
-       exit_module */\r
-    ResetEvent(evt_exit);\r
-\r
-    kmm_active_modules++;\r
-\r
-    release_module = FALSE;\r
-    exit_module = TRUE;\r
-\r
-    m->flags |= KMM_MODULE_FLAG_LOADED;\r
-    m->h_module = hm;\r
-\r
-    /* TODO: check signatures */\r
-\r
-    p_init_module = (init_module_t) GetProcAddress(hm, EXP_INIT_MODULE);\r
-\r
-    if(!p_init_module) {\r
-        _report_mr1(KHERR_ERROR, MSG_IM_NO_ENTRY, _cstr(EXP_INIT_MODULE));\r
-\r
-        m->state = KMM_MODULE_STATE_FAIL_INVALID;\r
-        goto _exit;\r
-    }\r
-\r
-    m->state = KMM_MODULE_STATE_INIT;\r
-\r
-    /* call init_module() */\r
-    rv = (*p_init_module)(kmm_handle_from_module(m));\r
-\r
-    m->flags |= KMM_MODULE_FLAG_INITP;\r
-\r
-    if(KHM_FAILED(rv)) {\r
-        _report_mr1(KHERR_ERROR, MSG_IM_INIT_FAIL, _int32(rv));\r
-\r
-        m->state = KMM_MODULE_STATE_FAIL_LOAD;\r
-        goto _exit;\r
-    }\r
-\r
-    if(!m->plugins) {\r
-        _report_mr0(KHERR_ERROR, MSG_IM_NO_PLUGINS);\r
-\r
-        m->state = KMM_MODULE_STATE_FAIL_NO_PLUGINS;\r
-        record_failure = FALSE;\r
-        goto _exit;\r
-    }\r
-\r
-    m->state = KMM_MODULE_STATE_INITPLUG;\r
-\r
-    do {\r
-        LPOP(&(m->plugins), &pi);\r
-        if(pi) {\r
-            pi->flags &= ~KMM_PLUGIN_FLAG_IN_MODLIST;\r
-            kmmint_init_plugin(pi);\r
-\r
-            /* release the hold obtained in kmm_provide_plugin() */\r
-            kmm_release_plugin(kmm_handle_from_plugin(pi));\r
-        }\r
-    } while(pi);\r
-\r
-    if(!m->plugin_count) {\r
-        /* We don't want to report this case.  This usually means that\r
-           the plugins that were provided by the module were\r
-           disabled. */\r
-#ifdef REPORT_EMPTY_MODULES\r
-        _report_mr0(KHERR_ERROR, MSG_IM_NO_PLUGINS);\r
-\r
-        m->state = KMM_MODULE_STATE_FAIL_NO_PLUGINS;\r
-#endif\r
-        record_failure = FALSE;\r
-        goto _exit;\r
-    }\r
-\r
-    m->state = KMM_MODULE_STATE_RUNNING;\r
-\r
-    exit_module = FALSE;\r
-    record_failure = FALSE;\r
-\r
- _exit:\r
-    if(csp_mod) {\r
-        if(record_failure) {\r
-            FILETIME fct;\r
-\r
-            i = 0;\r
-            khc_read_int32(csp_mod, L"FailureCount", &i);\r
-            i++;\r
-            khc_write_int32(csp_mod, L"FailureCount", i);\r
-\r
-            if(i==1) { /* first fault */\r
-                GetSystemTimeAsFileTime(&fct);\r
-                khc_write_int64(csp_mod, L"FailureTime", FtToInt(&fct));\r
-            }\r
-\r
-            khc_write_int32(csp_mod, L"FailureReason", m->state);\r
-        }\r
-        khc_close_space(csp_mod);\r
-    }\r
-\r
-    if(csp_mods)\r
-        khc_close_space(csp_mods);\r
-\r
-    _report_mr2(KHERR_INFO, MSG_IM_MOD_STATE, \r
-                _dupstr(m->name), _int32(m->state));\r
-\r
-    kmmint_remove_from_module_queue();\r
-\r
-    /* if something went wrong after init_module was called on the\r
-       module code, we need to call exit_module */\r
-    if(exit_module)\r
-        kmmint_exit_module(m);\r
-\r
-    if(release_module)\r
-        kmm_release_module(kmm_handle_from_module(m));\r
-\r
-    if (kherr_is_error()) {\r
-        kherr_context * c;\r
-        kherr_event * err_e = NULL;\r
-        kherr_event * warn_e = NULL;\r
-        kherr_event * e;\r
-\r
-        c = kherr_peek_context();\r
-        err_e = kherr_get_err_event(c);\r
-        for(e = kherr_get_first_event(c);\r
-            e;\r
-            e = kherr_get_next_event(e)) {\r
-            if (e != err_e &&\r
-                e->severity == KHERR_WARNING) {\r
-                warn_e = e;\r
-                break;\r
-            }\r
-        }\r
-\r
-        kherr_evaluate_event(err_e);\r
-        if (warn_e)\r
-            kherr_evaluate_event(warn_e);\r
-\r
-        kherr_clear_error();\r
-\r
-        e = kherr_report(KHERR_ERROR,\r
-                         (wchar_t *) MSG_IMERR_TITLE,\r
-                         KHERR_FACILITY,\r
-                         NULL,\r
-                         err_e->long_desc,\r
-                         ((warn_e)? (wchar_t *)MSG_IMERR_SUGGEST: NULL),\r
-                         KHERR_FACILITY_ID,\r
-                         KHERR_SUGGEST_NONE,\r
-                         _cstr(m->name),\r
-                         ((warn_e)? _cstr(warn_e->long_desc):_vnull()),\r
-                         _vnull(),_vnull(),\r
-                         KHERR_RF_MSG_SHORT_DESC |\r
-                         ((warn_e)? KHERR_RF_MSG_SUGGEST: 0),\r
-                         KHERR_HMODULE);\r
-\r
-        kherr_evaluate_event(e);\r
-\r
-        kherr_release_context(c);\r
-    }\r
-\r
-    _end_task();\r
-}\r
-\r
-\r
-/*! \internal\r
-  \brief Uninitializes a module\r
-\r
-  \note Should only be called from the context of the registrar\r
-  thread */\r
-void kmmint_exit_module(kmm_module_i * m) {\r
-    kmm_plugin_i * p;\r
-\r
-    /*  Exiting a module happens in two stages.  \r
-\r
-        If the module state is running (there are active plugins) then\r
-        those plugins must be exited.  This has to be done from the\r
-        plugin threads.  The signal for the plugins to exit must be\r
-        issued from the registrar.  Therefore, we post messages to the\r
-        registrar for each plugin we want to remove and exit\r
-        kmmint_exit_module().\r
-\r
-        When the last plugin is exited, the plugin management code\r
-        automatically signalls the registrar to remove the module.\r
-        kmmint_exit_module() gets called again.  This is the second\r
-        stage, where we call exit_module() for the module and start\r
-        unloading everything.\r
-    */\r
-\r
-    EnterCriticalSection(&cs_kmm);\r
-\r
-    /* get rid of any dangling uninitialized plugins */\r
-    LPOP(&(m->plugins), &p);\r
-    while(p) {\r
-        p->flags &= ~KMM_PLUGIN_FLAG_IN_MODLIST;\r
-        kmmint_exit_plugin(p);\r
-\r
-        /* release hold from kmm_provide_plugin() */\r
-        kmm_release_plugin(kmm_handle_from_plugin(p));\r
-\r
-        LPOP(&(m->plugins), &p);\r
-    }\r
-\r
-    if(m->state == KMM_MODULE_STATE_RUNNING) {\r
-        int np = 0;\r
-\r
-        m->state = KMM_MODULE_STATE_EXITPLUG;\r
-\r
-        p = kmm_listed_plugins;\r
-\r
-        while(p) {\r
-            if(p->module == m &&\r
-               (p->flags & KMM_PLUGIN_FLAG_IN_MODCOUNT)) {\r
-\r
-                kmm_hold_plugin(kmm_handle_from_plugin(p));\r
-                kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, \r
-                                 KMM_REG_EXIT_PLUGIN, (void *) p);\r
-                np++;\r
-\r
-            }\r
-\r
-            p = LNEXT(p);\r
-        }\r
-\r
-#ifdef DEBUG\r
-        assert(np == m->plugin_count);\r
-#endif\r
-\r
-        if(np > 0) {\r
-            /*  we have to go back and wait for the plugins to exit.\r
-                when the last plugin exits, it automatically posts\r
-                EXIT_MODULE. We can pick up from there when this\r
-                happens. */\r
-            LeaveCriticalSection(&cs_kmm);\r
-            return;\r
-        }\r
-\r
-    } else {\r
-\r
-#ifdef DEBUG\r
-        assert(m->plugin_count == 0 ||\r
-               m->state == KMM_MODULE_STATE_EXITPLUG);\r
-#endif\r
-\r
-        /* if there are still plug-ins waiting to be unloaded, then we\r
-           have to go back and wait for them to finish.  Once they are\r
-           done, kmmint_exit_module() will get called again. */\r
-        if (m->plugin_count > 0) {\r
-            LeaveCriticalSection(&cs_kmm);\r
-            return;\r
-        }\r
-    }\r
-\r
-    if(m->flags & KMM_MODULE_FLAG_INITP) {\r
-        exit_module_t p_exit_module;\r
-\r
-        if(m->state > 0)\r
-            m->state = KMM_MODULE_STATE_EXIT;\r
-\r
-        p_exit_module = \r
-            (exit_module_t) GetProcAddress(m->h_module, \r
-                                           EXP_EXIT_MODULE);\r
-        if(p_exit_module) {\r
-            LeaveCriticalSection(&cs_kmm);\r
-            (*p_exit_module)(kmm_handle_from_module(m));\r
-            EnterCriticalSection(&cs_kmm);\r
-        }\r
-    }\r
-\r
-    if(m->state > 0)\r
-        m->state = KMM_MODULE_STATE_EXITED;\r
-\r
-    LeaveCriticalSection(&cs_kmm);\r
-\r
-    if(!(m->flags & KMM_MODULE_FLAG_NOUNLOAD) &&\r
-       m->h_module) {\r
-        FreeLibrary(m->h_module);\r
-    }\r
-\r
-    if(!(m->flags & KMM_MODULE_FLAG_NOUNLOAD) &&\r
-       m->h_resource && (m->h_resource != m->h_module)) {\r
-        FreeLibrary(m->h_resource);\r
-    }\r
-\r
-    m->h_module = NULL;\r
-    m->h_resource = NULL;\r
-\r
-    if (m->flags & KMM_MODULE_FLAG_LOADED) {\r
-#ifdef DEBUG\r
-        assert(kmm_active_modules > 0);\r
-#endif\r
-        kmm_active_modules--;\r
-    }\r
-\r
-    m->flags = 0;\r
-\r
-    /* release the hold obtained in kmmint_init_module() */\r
-    kmm_release_module(kmm_handle_from_module(m));\r
-\r
-    /* Last but not least, now see if there are any modules left that\r
-       are running. If not, we can safely signal an exit. */\r
-    if (kmm_active_modules == 0) {\r
-        SetEvent(evt_exit);\r
-    }\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+#ifdef DEBUG
+#include<assert.h>
+#endif
+
+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;
+
+        /* TODO: check for orphaned plugins */
+
+        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(kmm_plugin_i * plugin) {
+    EnterCriticalSection(&cs_kmm);
+    if (!(plugin->flags & KMM_PLUGIN_FLAG_IN_QUEUE)) {
+        InterlockedIncrement(&pending_plugins);
+        plugin->flags |= KMM_PLUGIN_FLAG_IN_QUEUE;
+    }
+    LeaveCriticalSection(&cs_kmm);
+}
+
+void
+kmmint_remove_from_plugin_queue(kmm_plugin_i * plugin) {
+    EnterCriticalSection(&cs_kmm);
+
+    if (plugin->flags & KMM_PLUGIN_FLAG_IN_QUEUE) {
+        InterlockedDecrement(&pending_plugins);
+        plugin->flags &= ~KMM_PLUGIN_FLAG_IN_QUEUE;
+    }
+
+    LeaveCriticalSection(&cs_kmm);
+    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 kmmint_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:
+            kmmint_init_module((kmm_module_i *) vparam);
+            kmm_release_module(kmm_handle_from_module((kmm_module_i *) vparam));
+            break;
+
+        case KMM_REG_EXIT_MODULE:
+            kmmint_exit_module((kmm_module_i *) vparam);
+            kmm_release_module(kmm_handle_from_module((kmm_module_i *) vparam));
+            break;
+
+        case KMM_REG_INIT_PLUGIN:
+            kmmint_init_plugin((kmm_plugin_i *) vparam);
+            kmm_release_plugin(kmm_handle_from_plugin((kmm_plugin_i *) vparam));
+            break;
+
+        case KMM_REG_EXIT_PLUGIN:
+            kmmint_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 ( kmmint_reg_cb() ) */
+DWORD WINAPI kmmint_registrar(LPVOID lpParameter)
+{
+
+    PDESCTHREAD(L"KMM Registrar", L"KMM");
+
+    tid_registrar = GetCurrentThreadId();
+
+    kmq_subscribe(KMSG_KMM, kmmint_reg_cb);
+    kmq_subscribe(KMSG_SYSTEM, kmmint_reg_cb);
+
+    SetEvent(evt_startup);
+
+    while(KHM_SUCCEEDED(kmq_dispatch(INFINITE)));
+
+    kmq_unsubscribe(KMSG_KMM, kmmint_reg_cb);
+    kmq_unsubscribe(KMSG_SYSTEM, kmmint_reg_cb);
+
+    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 kmmint_plugin_broker(LPVOID lpParameter)
+{
+    DWORD rv = 0;
+    kmm_plugin_i * p = (kmm_plugin_i *) lpParameter;
+
+    PDESCTHREAD(p->p.name, L"KMM");
+
+    _begin_task(0);
+    _report_mr1(KHERR_NONE, MSG_PB_START, _cstr(p->p.name));
+    _describe();
+
+    TlsSetValue(tls_kmm, (LPVOID) p);
+
+    kmm_hold_plugin(kmm_handle_from_plugin(p));
+
+    p->tid_thread = GetCurrentThreadId();
+
+    if (IsBadCodePtr(p->p.msg_proc)) {
+        _report_mr0(KHERR_WARNING, MSG_PB_INVALID_CODE_PTR);
+        rv = KHM_ERROR_INVALID_PARAM;
+    } else {
+        rv = (*p->p.msg_proc)(KMSG_SYSTEM, KMSG_SYSTEM_INIT, 
+                              0, (void *) &(p->p));
+        _report_mr1(KHERR_INFO, MSG_PB_INIT_RV, _int32(rv));
+    }
+
+    /* if it fails to initialize, we exit the plugin */
+    if(KHM_FAILED(rv)) {
+
+        kherr_report(KHERR_ERROR,
+                     (wchar_t *) MSG_PB_INIT_FAIL_S,
+                     (wchar_t *) KHERR_FACILITY,
+                     NULL,
+                     (wchar_t *) MSG_PB_INIT_FAIL,
+                     (wchar_t *) MSG_PB_INIT_FAIL_G,
+                     KHERR_FACILITY_ID,
+                     KHERR_SUGGEST_NONE,
+                     _cstr(p->p.name),
+                     _cstr(p->p.description),
+                     _cstr(p->module->path),
+                     _cstr(p->module->support),
+                     KHERR_RF_MSG_SHORT_DESC |
+                     KHERR_RF_MSG_LONG_DESC |
+                     KHERR_RF_MSG_SUGGEST
+#ifdef _WIN32
+                     ,KHERR_HMODULE
+#endif
+                     );
+        _resolve();
+
+        /* exit the plugin first.  Otherwise it may not uninitialize correctly */
+        (*p->p.msg_proc)(KMSG_SYSTEM, KMSG_SYSTEM_EXIT, 0, (void *) &(p->p));
+
+        kmmint_remove_from_plugin_queue(p);
+        rv = 1;
+        _end_task();
+
+        p->state = KMM_PLUGIN_STATE_FAIL_INIT;
+        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);
+    } else if(p->p.type == KHM_PITYPE_IDENT) {
+        khm_handle h = NULL;
+
+        kmq_subscribe(KMSG_SYSTEM, p->p.msg_proc);
+        kmq_subscribe(KMSG_KCDB, p->p.msg_proc);
+
+        kmq_create_subscription(p->p.msg_proc, &h);
+        kcdb_identity_set_provider(h);
+        /* kcdb deletes the subscription when it's done with it */
+    } else if(p->p.type == KHM_PITYPE_CONFIG) {
+        /*TODO: subscribe to configuration provider messages here */
+    }
+
+    p->state = KMM_PLUGIN_STATE_RUNNING;
+
+    _report_mr0(KHERR_INFO, MSG_PB_INIT_DONE);
+
+    _end_task();
+
+    /* 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) {
+                kmmint_add_to_plugin_queue(pd);
+                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(p);
+
+    /* 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);
+    } else if (p->p.type == KHM_PITYPE_IDENT) {
+        kmq_unsubscribe(KMSG_KCDB, p->p.msg_proc);
+        kmq_unsubscribe(KMSG_SYSTEM, p->p.msg_proc);
+        kcdb_identity_set_provider(NULL);
+    } else 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:
+    if (p->state >= 0)
+        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 kmmint_init_plugin() is called on a plugin, then kmmint_exit_plugin()
+      \b must be called for the plugin.
+
+  \note Should only be called from the context of the registrar thread */
+void kmmint_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 kmmint_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. */
+#ifdef DEBUG
+        assert(p->flags & KMM_PLUGIN_FLAG_IN_MODCOUNT);
+#endif
+        p->module->plugin_count--;
+        p->flags &= ~KMM_PLUGIN_FLAG_IN_MODCOUNT;
+    }
+
+    p->state = KMM_PLUGIN_STATE_PREINIT;
+
+    kmmint_delist_plugin(p);
+    kmmint_list_plugin(p);
+
+    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))) {
+        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_SUCCEEDED(khc_read_int32(csp_plugin, L"Disabled", &t)) && t) {
+        p->flags |= KMM_PLUGIN_FLAG_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 = PMALLOC(sz);
+        if(KHM_FAILED(khc_read_multi_string(csp_plugin, L"Dependencies", 
+                                            deps, &sz))) {
+            if(deps)
+                PFREE(deps);
+            break;
+        }
+
+        for(d = deps; d && *d; d = multi_string_next(d)) {
+            kmm_plugin_i * pd;
+            int i;
+
+            pd = kmmint_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 kmmint_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;
+        }
+
+        PFREE(deps);
+
+    } while(FALSE);
+
+#ifdef DEBUG
+    assert(!(p->flags & KMM_PLUGIN_FLAG_IN_MODCOUNT));
+#endif
+    p->flags |= KMM_PLUGIN_FLAG_IN_MODCOUNT;
+    p->module->plugin_count++;
+
+    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);
+
+    p->ht_thread = CreateThread(NULL,
+                                0,
+                                kmmint_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();
+
+
+#ifdef ASYNC_PLUGIN_UNLOAD_ON_FAILURE
+
+    kmm_hold_plugin(kmm_handle_from_plugin(p));
+
+    kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, KMM_REG_EXIT_PLUGIN, (void *) p);
+
+#else
+
+    kmmint_exit_plugin(p);
+
+#endif
+}
+
+/*! \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 kmmint_exit_plugin(kmm_plugin_i * p) {
+    int np;
+    khm_boolean release_plugin = TRUE;
+
+    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 kmmint_init_plugin() */
+    if(p->flags & KMM_PLUGIN_FLAG_IN_MODCOUNT) {
+
+        np = --(p->module->plugin_count);
+        p->flags &= ~KMM_PLUGIN_FLAG_IN_MODCOUNT;
+
+    } else {
+        /* the plugin was not included in the module's plugin_count.
+           We can't base a decision to unload the module based on this
+           plugin exiting. */
+        np = TRUE;
+    }
+
+    /* The plugin is in an error state.  We need to keep the plugin
+       record in tact so that the failure information is kept
+       around. */
+    if (p->state < KMM_PLUGIN_STATE_NONE) {
+        release_plugin = FALSE;
+    }
+
+    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 kmmint_init_plugin() */
+    if (release_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 kmmint_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_INFO, 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"Disabled", &i)) && i) {
+        _report_mr0(KHERR_INFO, MSG_IM_DISABLED);
+
+        m->state = KMM_MODULE_STATE_FAIL_DISABLED;
+        goto _exit;
+    }
+
+    if(KHM_SUCCEEDED(khc_read_int32(csp_mod, L"NoUnload", &i)) && i) {
+        m->flags |= KMM_MODULE_FLAG_NOUNLOAD;
+    }
+
+    if(KHM_SUCCEEDED(khc_read_int32(csp_mod, L"FailureCount", &i))) {
+        khm_int64 tm;
+        khm_int64 ct;
+        FILETIME fct;
+        khm_int32 last_reason = 0;
+
+        /* reset the failure count if the failure count reset time
+           period has elapsed */
+        tm = 0;
+        khc_read_int64(csp_mod, L"FailureTime", &tm);
+        GetSystemTimeAsFileTime(&fct);
+
+        ct = (FtToInt(&fct) - tm) / 10000000i64;
+
+        if(tm > 0 && 
+           ct > fail_reset_time) {
+            i = 0;
+            khc_write_int32(csp_mod, L"FailureCount", 0);
+            khc_write_int64(csp_mod, L"FailureTime", 0);
+        }
+
+        khc_read_int32(csp_mod, L"FailureReason", &last_reason);
+
+        /* did we exceed the max failure count?  However, we ignore
+           the max failure count if the reason why it didn't load the
+           last time was because the module wasn't found. */
+        if(i > max_fail_count && 
+           last_reason != KMM_MODULE_STATE_FAIL_NOT_FOUND) {
+            /* failed too many times */
+            _report_mr0(KHERR_INFO, 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)
+            PFREE(m->path);
+        m->path = PMALLOC(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;
+    }
+
+    rv = kmmint_read_module_info(m);
+
+    if (KHM_FAILED(rv)) {
+        if (rv == KHM_ERROR_INCOMPATIBLE) {
+            _report_mr0(KHERR_ERROR, MSG_IM_INCOMPATIBLE);
+
+            m->state = KMM_MODULE_STATE_FAIL_INCOMPAT;
+        } else if (rv == KHM_ERROR_NOT_FOUND) {
+            _report_mr1(KHERR_ERROR, MSG_IM_NOT_FOUND, _dupstr(m->path));
+
+            m->state = KMM_MODULE_STATE_FAIL_NOT_FOUND;
+        } else {
+            _report_mr0(KHERR_ERROR, MSG_IM_INVALID_MODULE);
+
+            m->state = KMM_MODULE_STATE_FAIL_INV_MODULE;
+        }
+        goto _exit;
+    }
+
+    /* check again */
+    if(m->state != KMM_MODULE_STATE_PREINIT) {
+        _report_mr0(KHERR_ERROR, MSG_IM_NOT_PREINIT);
+
+        goto _exit;
+    }
+
+    /* from this point on, we must record any failure codes */
+    record_failure = TRUE;
+
+    hm = LoadLibrary(m->path);
+    if(!hm) {
+        m->h_module = NULL;
+        m->state = KMM_MODULE_STATE_FAIL_NOT_FOUND;
+
+        _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 */
+    ResetEvent(evt_exit);
+
+    kmm_active_modules++;
+
+    release_module = FALSE;
+    exit_module = 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;
+            kmmint_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) {
+        /* We don't want to report this case.  This usually means that
+           the plugins that were provided by the module were
+           disabled. */
+#ifdef REPORT_EMPTY_MODULES
+        _report_mr0(KHERR_ERROR, MSG_IM_NO_PLUGINS);
+
+        m->state = KMM_MODULE_STATE_FAIL_NO_PLUGINS;
+#endif
+        record_failure = FALSE;
+        goto _exit;
+    }
+
+    m->state = KMM_MODULE_STATE_RUNNING;
+
+    exit_module = FALSE;
+    record_failure = FALSE;
+
+ _exit:
+    if(csp_mod) {
+        if(record_failure) {
+            FILETIME fct;
+
+            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(&fct);
+                khc_write_int64(csp_mod, L"FailureTime", FtToInt(&fct));
+            }
+
+            khc_write_int32(csp_mod, L"FailureReason", m->state);
+        }
+        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));
+
+    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)
+        kmmint_exit_module(m);
+
+    if(release_module)
+        kmm_release_module(kmm_handle_from_module(m));
+
+    if (kherr_is_error()) {
+        kherr_context * c;
+        kherr_event * err_e = NULL;
+        kherr_event * warn_e = NULL;
+        kherr_event * e;
+
+        c = kherr_peek_context();
+        err_e = kherr_get_err_event(c);
+        for(e = kherr_get_first_event(c);
+            e;
+            e = kherr_get_next_event(e)) {
+            if (e != err_e &&
+                e->severity == KHERR_WARNING) {
+                warn_e = e;
+                break;
+            }
+        }
+
+        kherr_evaluate_event(err_e);
+        if (warn_e)
+            kherr_evaluate_event(warn_e);
+
+        kherr_clear_error();
+
+        e = kherr_report(KHERR_ERROR,
+                         (wchar_t *) MSG_IMERR_TITLE,
+                         KHERR_FACILITY,
+                         NULL,
+                         err_e->long_desc,
+                         ((warn_e)? (wchar_t *)MSG_IMERR_SUGGEST: NULL),
+                         KHERR_FACILITY_ID,
+                         KHERR_SUGGEST_NONE,
+                         _cstr(m->name),
+                         ((warn_e)? _cstr(warn_e->long_desc):_vnull()),
+                         _vnull(),_vnull(),
+                         KHERR_RF_MSG_SHORT_DESC |
+                         ((warn_e)? KHERR_RF_MSG_SUGGEST: 0),
+                         KHERR_HMODULE);
+
+        kherr_evaluate_event(e);
+
+        kherr_release_context(c);
+    }
+
+    _end_task();
+}
+
+
+/*! \internal
+  \brief Uninitializes a module
+
+  \note Should only be called from the context of the registrar
+  thread */
+void kmmint_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
+        kmmint_exit_module().
+
+        When the last plugin is exited, the plugin management code
+        automatically signalls the registrar to remove the module.
+        kmmint_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;
+        kmmint_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 &&
+               (p->flags & KMM_PLUGIN_FLAG_IN_MODCOUNT)) {
+
+                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);
+        }
+
+#ifdef DEBUG
+        assert(np == m->plugin_count);
+#endif
+
+        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;
+        }
+
+    } else {
+
+#ifdef DEBUG
+        assert(m->plugin_count == 0 ||
+               m->state == KMM_MODULE_STATE_EXITPLUG);
+#endif
+
+        /* if there are still plug-ins waiting to be unloaded, then we
+           have to go back and wait for them to finish.  Once they are
+           done, kmmint_exit_module() will get called again. */
+        if (m->plugin_count > 0) {
+            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);
+        }
+    }
+
+    if(m->state > 0)
+        m->state = KMM_MODULE_STATE_EXITED;
+
+    LeaveCriticalSection(&cs_kmm);
+
+    if(!(m->flags & KMM_MODULE_FLAG_NOUNLOAD) &&
+       m->h_module) {
+        FreeLibrary(m->h_module);
+    }
+
+    if(!(m->flags & KMM_MODULE_FLAG_NOUNLOAD) &&
+       m->h_resource && (m->h_resource != m->h_module)) {
+        FreeLibrary(m->h_resource);
+    }
+
+    m->h_module = NULL;
+    m->h_resource = NULL;
+
+    if (m->flags & KMM_MODULE_FLAG_LOADED) {
+#ifdef DEBUG
+        assert(kmm_active_modules > 0);
+#endif
+        kmm_active_modules--;
+    }
+
+    m->flags = 0;
+
+    /* release the hold obtained in kmmint_init_module() */
+    kmm_release_module(kmm_handle_from_module(m));
+
+    /* Last but not least, now see if there are any modules left that
+       are running. If not, we can safely signal an exit. */
+    if (kmm_active_modules == 0) {
+        SetEvent(evt_exit);
+    }
+}
index 38fce2422f48297cf3bb7fdcfa3a38f30edde83f..1712e976cf645f005f543edd033907a9be0041e8 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KMMINTERNAL_H\r
-#define __KHIMAIRA_KMMINTERNAL_H\r
-\r
-#include<windows.h>\r
-#include<shlwapi.h>\r
-#include<strsafe.h>\r
-\r
-#define KHERR_FACILITY kmm_facility\r
-#define KHERR_FACILITY_ID KHM_FACILITY_KMM\r
-#define KHERR_HMODULE ((HMODULE) kmm_hInstance)\r
-#include<kherr.h>\r
-\r
-#include<kmm.h>\r
-#include<khmsgtypes.h>\r
-#include<kherror.h>\r
-#include<kplugin.h>\r
-\r
-#define NOEXPORT\r
-\r
-#include<utils.h>\r
-#include<kconfig.h>\r
-#include<kcreddb.h>\r
-#include<kmm_msgs.h>\r
-\r
-struct kmm_plugin_i_t; /* forward dcl */\r
-\r
-typedef struct kmm_module_i_t {\r
-    khm_int32 magic;\r
-\r
-    wchar_t * name;\r
-    wchar_t * path;\r
-    wchar_t * description;\r
-    wchar_t * vendor;\r
-    wchar_t * support;\r
-\r
-    khm_version file_version;\r
-    khm_version prod_version;\r
-\r
-    HMODULE h_module;\r
-\r
-    HMODULE h_resource;\r
-    WORD    lcid_resource;\r
-\r
-    khm_int32 flags;\r
-    khm_int32 state;\r
-    khm_int32 plugin_count; /* number of active plugins */\r
-\r
-    void * version_info;\r
-\r
-    khm_int32 refcount;\r
-\r
-    struct kmm_plugin_i_t * plugins; /* only used for registration */\r
-\r
-    LDCL(struct kmm_module_i_t);\r
-} kmm_module_i;\r
-\r
-#define KMM_MODULE_MAGIC 0x482f4e88\r
-\r
-#define kmm_is_module(m) ((m) && ((kmm_module_i *)m)->magic == KMM_MODULE_MAGIC)\r
-\r
-#define kmm_module_from_handle(m) ((kmm_module_i *) m)\r
-#define kmm_handle_from_module(m) ((kmm_module) m)\r
-\r
-/* LoadLibrary succeeded for module */\r
-#define KMM_MODULE_FLAG_LOADED      0x00000001\r
-\r
-/* init_module entry called */\r
-#define KMM_MODULE_FLAG_INITP       0x00000002\r
-\r
-/* the resources have been loaded */\r
-#define KMM_MODULE_FLAG_RES_LOADED  0x00000008\r
-\r
-/* the signature has been verified */\r
-#define KMM_MODULE_FLAG_SIG         0x00000010\r
-\r
-/* the module is disabled by the user\r
-   (option specified in configuration) */\r
-#define KMM_MODULE_FLAG_DISABLED    0x00000400\r
-\r
-/* the module should not be unloaded\r
-   (option specified in configuration)*/\r
-#define KMM_MODULE_FLAG_NOUNLOAD    0x00000800\r
-\r
-/* the module has been removed from the active modules list. */\r
-#define KMM_MODULE_FLAG_INACTIVE    0x00001000\r
-\r
-typedef struct kmm_plugin_i_t {\r
-    kmm_plugin_reg p;\r
-\r
-    khm_int32   magic;\r
-\r
-    kmm_module_i * module;\r
-    HANDLE      ht_thread;\r
-    DWORD       tid_thread;\r
-\r
-    khm_int32   state;\r
-    khm_int32   flags;\r
-    \r
-    int         refcount;\r
-\r
-    int         n_depends;\r
-    int         n_unresolved;\r
-    struct kmm_plugin_i_t * dependants[KMM_MAX_DEPENDANTS];\r
-    int         n_dependants;\r
-\r
-    LDCL(struct kmm_plugin_i_t);\r
-} kmm_plugin_i;\r
-\r
-#define KMM_PLUGIN_MAGIC 0x320e8fb4\r
-\r
-#define kmm_is_plugin(p) ((p) && ((kmm_plugin_i *) (p))->magic == KMM_PLUGIN_MAGIC)\r
-\r
-#define kmm_handle_from_plugin(p) ((kmm_plugin) p)\r
-#define kmm_plugin_from_handle(ph) ((kmm_plugin_i *) ph)\r
-\r
-/* the plugin has already been marked for unload */\r
-#define KMM_PLUGIN_FLAG_UNLOAD      0x00000001\r
-\r
-/* the plugin is in the kmm_listed_plugins list */\r
-#define KMM_PLUGIN_FLAG_IN_LIST     0x00000002\r
-\r
-/* the plugin is in the module's plugin list */\r
-#define KMM_PLUGIN_FLAG_IN_MODLIST  0x00000004\r
-\r
-/* the plugin has been included in the pending_plugins count. */\r
-#define KMM_PLUGIN_FLAG_IN_QUEUE    0x00000010\r
-\r
-/* the plugin is included in the module's plugin count */\r
-#define KMM_PLUGIN_FLAG_IN_MODCOUNT 0x00000020\r
-\r
-/* the plugin is disabled by the user\r
-    (option specified in configuration) */\r
-/* (this is defined in kmm.h)\r
-\r
- #define KMM_PLUGIN_FLAG_DISABLED   0x00000400\r
-\r
-*/\r
-\r
-enum kmm_registrar_uparam_t {\r
-    KMM_REG_INIT_MODULE,\r
-    KMM_REG_EXIT_MODULE,\r
-    KMM_REG_INIT_PLUGIN,\r
-    KMM_REG_EXIT_PLUGIN\r
-};\r
-\r
-extern kmm_module_i * kmm_all_modules;\r
-extern khm_size kmm_active_modules;\r
-extern kmm_plugin_i * kmm_listed_plugins;\r
-extern HANDLE ht_registrar;\r
-extern DWORD tid_registrar;\r
-extern DWORD tls_kmm;\r
-\r
-extern hashtable * hash_plugins;\r
-extern hashtable * hash_modules;\r
-\r
-extern CRITICAL_SECTION cs_kmm;\r
-extern int ready;\r
-extern HANDLE evt_startup;\r
-extern HANDLE evt_exit;\r
-extern const wchar_t * kmm_facility;\r
-\r
-extern HINSTANCE kmm_hInstance;\r
-\r
-extern kconf_schema schema_kmmconfig[];\r
-\r
-/* Registrar */\r
-\r
-khm_boolean KHMAPI \r
-kmmint_reg_cb(khm_int32 msg_type, \r
-              khm_int32 msg_sub_type, \r
-              khm_ui_4 uparam,\r
-              void *vparam);\r
-\r
-DWORD WINAPI kmmint_registrar(LPVOID lpParameter);\r
-\r
-DWORD WINAPI kmmint_plugin_broker(LPVOID lpParameter);\r
-\r
-void kmmint_init_plugin(kmm_plugin_i * p);\r
-void kmmint_exit_plugin(kmm_plugin_i * p);\r
-void kmmint_init_module(kmm_module_i * m);\r
-void kmmint_exit_module(kmm_module_i * m);\r
-\r
-/* Modules */\r
-kmm_module_i * \r
-kmmint_get_module_i(wchar_t * name);\r
-\r
-kmm_module_i * \r
-kmmint_find_module_i(wchar_t * name);\r
-\r
-void \r
-kmmint_free_module(kmm_module_i * m);\r
-\r
-khm_int32\r
-kmmint_read_module_info(kmm_module_i * m);\r
-\r
-/* Plugins */\r
-kmm_plugin_i * \r
-kmmint_get_plugin_i(wchar_t * name);\r
-\r
-kmm_plugin_i * \r
-kmmint_find_plugin_i(wchar_t * name);\r
-\r
-void \r
-kmmint_free_plugin(kmm_plugin_i * pi);\r
-\r
-void \r
-kmmint_list_plugin(kmm_plugin_i * p);\r
-\r
-void \r
-kmmint_delist_plugin(kmm_plugin_i * p);\r
-\r
-khm_boolean \r
-kmmint_load_locale_lib(kmm_module_i * m, kmm_module_locale * l);\r
-\r
-#define KMM_CSNAME_ROOT L"PluginManager"\r
-#define KMM_CSNAME_PLUGINS L"Plugins"\r
-#define KMM_CSNAME_MODULES L"Modules"\r
-#define KMM_VALNAME_LOADLIST L"LoadList"\r
-\r
-void\r
-kmmint_add_to_module_queue(void);\r
-\r
-void\r
-kmmint_remove_from_module_queue(void);\r
-\r
-#define _WAIT_FOR_START \\r
-    do { \\r
-    if(ready) break; \\r
-    WaitForSingleObject(evt_startup, INFINITE); \\r
-    } while(0)\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<shlwapi.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>
+
+#define NOEXPORT
+
+#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 * description;
+    wchar_t * vendor;
+    wchar_t * support;
+
+    khm_version file_version;
+    khm_version prod_version;
+
+    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)
+
+/* LoadLibrary succeeded for module */
+#define KMM_MODULE_FLAG_LOADED      0x00000001
+
+/* init_module entry called */
+#define KMM_MODULE_FLAG_INITP       0x00000002
+
+/* the resources have been loaded */
+#define KMM_MODULE_FLAG_RES_LOADED  0x00000008
+
+/* the signature has been verified */
+#define KMM_MODULE_FLAG_SIG         0x00000010
+
+/* the module is disabled by the user
+   (option specified in configuration) */
+#define KMM_MODULE_FLAG_DISABLED    0x00000400
+
+/* the module should not be unloaded
+   (option specified in configuration)*/
+#define KMM_MODULE_FLAG_NOUNLOAD    0x00000800
+
+/* the module has been removed from the active modules list. */
+#define KMM_MODULE_FLAG_INACTIVE    0x00001000
+
+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      0x00000001
+
+/* the plugin is in the kmm_listed_plugins list */
+#define KMM_PLUGIN_FLAG_IN_LIST     0x00000002
+
+/* the plugin is in the module's plugin list */
+#define KMM_PLUGIN_FLAG_IN_MODLIST  0x00000004
+
+/* the plugin has been included in the pending_plugins count. */
+#define KMM_PLUGIN_FLAG_IN_QUEUE    0x00000010
+
+/* the plugin is included in the module's plugin count */
+#define KMM_PLUGIN_FLAG_IN_MODCOUNT 0x00000020
+
+/* the plugin is disabled by the user
+    (option specified in configuration) */
+/* (this is defined in kmm.h)
+
+ #define KMM_PLUGIN_FLAG_DISABLED   0x00000400
+
+*/
+
+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 khm_size kmm_active_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 
+kmmint_reg_cb(khm_int32 msg_type, 
+              khm_int32 msg_sub_type, 
+              khm_ui_4 uparam,
+              void *vparam);
+
+DWORD WINAPI kmmint_registrar(LPVOID lpParameter);
+
+DWORD WINAPI kmmint_plugin_broker(LPVOID lpParameter);
+
+void kmmint_init_plugin(kmm_plugin_i * p);
+void kmmint_exit_plugin(kmm_plugin_i * p);
+void kmmint_init_module(kmm_module_i * m);
+void kmmint_exit_module(kmm_module_i * m);
+
+/* Modules */
+kmm_module_i * 
+kmmint_get_module_i(wchar_t * name);
+
+kmm_module_i * 
+kmmint_find_module_i(wchar_t * name);
+
+void 
+kmmint_free_module(kmm_module_i * m);
+
+khm_int32
+kmmint_read_module_info(kmm_module_i * m);
+
+/* Plugins */
+kmm_plugin_i * 
+kmmint_get_plugin_i(wchar_t * name);
+
+kmm_plugin_i * 
+kmmint_find_plugin_i(wchar_t * name);
+
+void 
+kmmint_free_plugin(kmm_plugin_i * pi);
+
+void 
+kmmint_list_plugin(kmm_plugin_i * p);
+
+void 
+kmmint_delist_plugin(kmm_plugin_i * p);
+
+khm_boolean 
+kmmint_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
index aa94a59a1a4127864a53fa21a5e164df85e0f421..e54dd6de26e02122dd189caf535f09d782e76958 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kmminternal.h>\r
-\r
-kmm_module_i * kmm_all_modules = NULL;\r
-kmm_plugin_i * kmm_listed_plugins = NULL;\r
-\r
-HANDLE ht_registrar = NULL;\r
-DWORD tid_registrar = 0;\r
-DWORD tls_kmm = 0;\r
-\r
-#define KMM_HASH_SIZE 31\r
-hashtable * hash_plugins = NULL;\r
-hashtable * hash_modules = NULL;\r
-\r
-CRITICAL_SECTION cs_kmm;\r
-HANDLE evt_startup = NULL;\r
-HANDLE evt_exit = NULL;\r
-int ready = 0;\r
-\r
-HINSTANCE kmm_hInstance;\r
-const wchar_t * kmm_facility = L"KMM";\r
-\r
-KHMEXP void KHMAPI kmm_init(void)\r
-{\r
-    DWORD dummy;\r
-\r
-    EnterCriticalSection(&cs_kmm);\r
-    kmm_all_modules = NULL;\r
-    kmm_listed_plugins = NULL;\r
-\r
-    tls_kmm = TlsAlloc();\r
-\r
-    hash_plugins = hash_new_hashtable(\r
-        KMM_HASH_SIZE, \r
-        hash_string, \r
-        hash_string_comp, \r
-        NULL, \r
-        NULL);\r
-\r
-    hash_modules = hash_new_hashtable(\r
-        KMM_HASH_SIZE,\r
-        hash_string,\r
-        hash_string_comp,\r
-        NULL,\r
-        NULL);\r
-\r
-    ht_registrar = CreateThread(\r
-        NULL,\r
-        0,\r
-        kmmint_registrar,\r
-        NULL,\r
-        0,\r
-        &dummy);\r
-\r
-    _WAIT_FOR_START;\r
-\r
-    khc_load_schema(NULL, schema_kmmconfig);\r
-\r
-    LeaveCriticalSection(&cs_kmm);\r
-}\r
-\r
-KHMEXP void KHMAPI kmm_exit(void)\r
-{\r
-    kmm_module_i * m;\r
-    kmm_plugin_i * p;\r
-\r
-    EnterCriticalSection(&cs_kmm);\r
-\r
-    p = kmm_listed_plugins;\r
-    while(p) {\r
-        kmm_plugin_i * pn;\r
-\r
-        pn = LNEXT(p);\r
-        /* plugins that were never resolved should be kicked off the\r
-           list.  Flipping the refcount will do that if no other\r
-           references exist for the plugin.  The plugins that were\r
-           waiting for unresolved dependencies will automatically get\r
-           freed when the placeholders and other plugins get freed. */\r
-        if(p->state == KMM_PLUGIN_STATE_PLACEHOLDER) {\r
-            kmm_hold_plugin(kmm_handle_from_plugin(p));\r
-            kmm_release_plugin(kmm_handle_from_plugin(p));\r
-        }\r
-\r
-        p = pn;\r
-    }\r
-\r
-    m = kmm_all_modules;\r
-    while(m) {\r
-        kmm_unload_module(kmm_handle_from_module(m));\r
-        m = LNEXT(m);\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_kmm);\r
-    WaitForSingleObject(evt_exit, INFINITE);\r
-    EnterCriticalSection(&cs_kmm);\r
-\r
-    kmq_post_thread_quit_message(tid_registrar, 0, NULL);\r
-\r
-    hash_del_hashtable(hash_plugins);\r
-    hash_del_hashtable(hash_modules);\r
-\r
-    LeaveCriticalSection(&cs_kmm);\r
-\r
-    TlsFree(tls_kmm);\r
-\r
-    tls_kmm = 0;\r
-}\r
-\r
-void kmm_dll_init(void)\r
-{\r
-    InitializeCriticalSection(&cs_kmm);\r
-    evt_startup = CreateEvent(NULL, TRUE, FALSE, NULL);\r
-    evt_exit = CreateEvent(NULL, TRUE, TRUE, NULL);\r
-}\r
-\r
-void kmm_dll_exit(void)\r
-{\r
-    DeleteCriticalSection(&cs_kmm);\r
-    if(evt_startup)\r
-        CloseHandle(evt_startup);\r
-    evt_startup = NULL;\r
-}\r
-\r
-void \r
-kmm_process_attach(HINSTANCE hinstDLL) {\r
-    kmm_hInstance = hinstDLL;\r
-    kmm_dll_init();\r
-}\r
-\r
-void\r
-kmm_process_detach(void) {\r
-    kmm_dll_exit();\r
-}\r
-\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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,
+        kmmint_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.  The plugins that were
+           waiting for unresolved dependencies will automatically get
+           freed when the placeholders and other plugins get freed. */
+        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();
+}
+
index e4e6003eecd3b5d4e9690b1f19b3d22026a28e5e..6eb4e13c2603fa585082f71950c2ebf4db3b371b 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KPLUGIN_H\r
-#define __KHIMAIRA_KPLUGIN_H\r
-\r
-#include<kmm.h>\r
-#include<kherror.h>\r
-\r
-/*! \addtogroup kmm\r
-@{*/\r
-/*! \defgroup kplugin NetIDMgr Plugin Callbacks\r
-\r
-See the following related documentation pages for more information \r
-about NetIDMgr plugins.\r
-\r
-These are prototypes of functions that must be implemented by a NetIDMgr\r
-plugin.\r
-\r
-- \ref plugins\r
-@{*/\r
-\r
-/*! \brief Initialize the module\r
-\r
-    This is the first callback function to be called in a module.\r
-    Perform all the required intialization when this is called.  As\r
-    mentioned in \ref plugins, you should not attempt to call any\r
-    NetIDMgr API function from DLLMain or other initialization code\r
-    other than this one.\r
-\r
-    You should use this call back to register the plugins that will be\r
-    implemented in this module and to notify the plugin manager of any\r
-    resource libraries that this module will use.\r
-\r
-    Call:\r
-    - kmm_set_locale() : to set the notify the plugin manager of the\r
-      locale specifc resource libraries that are used by this module.\r
-    - kmm_provide_plugin() : to register each plugin that is\r
-      implemented in this module.\r
-\r
-    This function is called in the context of the current user, from\r
-    the plug-in manager thread.  This same thread is used by the\r
-    plug-in manager to load and initialize all the modules for a\r
-    session.\r
-\r
-    The name of the callback must be init_module().  The calling\r
-    convention is KHMAPI, which is currently __stdcall.\r
-\r
-    If this function does not register any plugins, the plugin manager\r
-    will immediately call exit_module() and unload the module even if\r
-    the init_module() function completes successfully.\r
-\r
-    \return Return the following values to indicate whether the module\r
-        successfully initialized or not.\r
-        - KHM_ERROR_SUCCESS : Succeeded. The module manager will call\r
-            init_plugin() for each of the registered plugins for the\r
-            module.\r
-        - any other error code: Signals that the module did not\r
-            successfully initialize.  The plugin manager will\r
-            immediately call exit_module() and then unload the module.\r
-\r
-    \note This callback is required.\r
-*/\r
-KHMEXP khm_int32 KHMAPI init_module(kmm_module h_module);\r
-\r
-/*! \brief Type for init_module() */\r
-typedef khm_int32 (KHMAPI *init_module_t)(kmm_module);\r
-\r
-#if defined(_WIN64)\r
-#define EXP_INIT_MODULE "init_module"\r
-#elif defined(_WIN32)\r
-#define EXP_INIT_MODULE "_init_module@4"\r
-#else\r
-#error  EXP_INIT_MODULE not defined for platform\r
-#endif\r
-\r
-/*! \brief Plugin procedure\r
-\r
-    This is the message processor for a plugin.  See \ref pi_fw_pnm_p\r
-    for more information.\r
-\r
-    Essentially, this is a message subscriber for KMQ messages.\r
-*/\r
-KHMEXP khm_int32 KHMAPI _plugin_proc(khm_int32 msg_type, khm_int32 msg_subtype, khm_ui_4 uparam, void * vparam);\r
-\r
-/*! \brief Type for init_plugin() */\r
-typedef kmq_callback_t _plugin_proc_t;\r
-\r
-/*! \brief Exit a module\r
-\r
-    This is the last callback function that the NetIDMgr module\r
-    manager calls before unloading the module.  When this function is\r
-    called, all of the plugins for the module have already been\r
-    stopped.  However, any localization libraries that were loaded as\r
-    a result of init_module() calling kmm_set_locale_info() will still\r
-    be loaded.  These localization libraries will be unloaded\r
-    immediately after this callback returns.\r
-\r
-    Use this callback to perform any required cleanup tasks.  However,\r
-    it is advisable that each plugin perform its own cleanup tasks,\r
-    since each plugin may be stopped independently of others.\r
-\r
-    \return The return value of this function is ignored.\r
-\r
-    \note This callback is not required.\r
-*/\r
-KHMEXP khm_int32 KHMAPI exit_module(kmm_module h_module);\r
-\r
-/*! \brief Type for exit_module() */\r
-typedef khm_int32 (KHMAPI *exit_module_t)(kmm_module);\r
-\r
-#if defined(_WIN64)\r
-#define EXP_EXIT_MODULE "exit_module"\r
-#elif defined(_WIN32)\r
-#define EXP_EXIT_MODULE "_exit_module@4"\r
-#else\r
-#error  EXP_EXIT_MODULE not defined for platform\r
-#endif\r
-\r
-/*@}*/\r
-/*@}*/\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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"
+#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"
+#elif defined(_WIN32)
+#define EXP_EXIT_MODULE "_exit_module@4"
+#else
+#error  EXP_EXIT_MODULE not defined for platform
+#endif
+
+/*@}*/
+/*@}*/
+
+#endif
index d6041779f9d44e23a5e3f4dbc1109f214bc5966b..32bafec6e883612f2961c31049a8573744abd139 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kmqinternal.h>\r
-#include<assert.h>\r
-\r
-DWORD kmq_tls_queue;\r
-\r
-CRITICAL_SECTION cs_kmq_msg_ref;\r
-\r
-kmq_message_ref * kmq_msg_ref_free = NULL;\r
-\r
-/* ad-hoc subscriptions */\r
-kmq_msg_subscription * kmq_adhoc_subs = NULL;\r
-\r
-#ifdef DEBUG\r
-\r
-#include<stdio.h>\r
-\r
-void\r
-kmqint_dump_consumer(FILE * f) {\r
-    kmq_message_ref * r;\r
-    kmq_msg_subscription * s;\r
-\r
-    int n_free = 0;\r
-    int n_adhoc = 0;\r
-\r
-    EnterCriticalSection(&cs_kmq_msg_ref);\r
-\r
-    fprintf(f, "qc0\t*** Free Message References ***\n");\r
-\r
-    fprintf(f, "qc1\tAddress\n");\r
-\r
-    r = kmq_msg_ref_free;\r
-    while(r) {\r
-        n_free ++;\r
-\r
-        fprintf(f, "qc2\t0x%p\n", r);\r
-\r
-        r = LNEXT(r);\r
-    }\r
-\r
-    fprintf(f, "qc3\tTotal free message references : %d\n", n_free);\r
-\r
-    fprintf(f, "qc4\t--- End ---\n");\r
-\r
-    LeaveCriticalSection(&cs_kmq_msg_ref);\r
-\r
-    EnterCriticalSection(&cs_kmq_global);\r
-\r
-    fprintf(f, "qc5\t*** Adhoc Message Subscriptions ***\n");\r
-\r
-    fprintf(f, "qc6\tAddress\tMsg Type\tRcpt Type\tRcpt\tQueue\n");\r
-\r
-    s = kmq_adhoc_subs;\r
-\r
-    while(s) {\r
-        n_adhoc ++;\r
-\r
-        fprintf(f, "qc7\t0x%p\t%d\t%s\t0x%p\t0x%p\n",\r
-                s,\r
-                s->type,\r
-                (s->rcpt_type == KMQ_RCPTTYPE_CB)?"CALLBACK":"HWND",\r
-                (s->rcpt_type == KMQ_RCPTTYPE_CB) ? (void *) s->recipient.cb: (void *) s->recipient.hwnd,\r
-                s->queue);\r
-\r
-        s = LNEXT(s);\r
-    }\r
-\r
-    fprintf(f, "qc8\tTotal ad-hoc subscriptions : %d\n", n_adhoc);\r
-\r
-    fprintf(f, "qc9\t--- End ---\n");\r
-\r
-    LeaveCriticalSection(&cs_kmq_global);\r
-\r
-}\r
-\r
-#endif\r
-\r
-/*! \internal\r
-    \brief Get a message ref object\r
-    \note called with cs_kmq_msg_ref held */\r
-kmq_message_ref * kmqint_get_message_ref(void) {\r
-    kmq_message_ref * r;\r
-\r
-    LPOP(&kmq_msg_ref_free, &r);\r
-    if(!r) {\r
-        r = PMALLOC(sizeof(kmq_message_ref));\r
-    }\r
-    ZeroMemory(r, sizeof(kmq_message_ref));\r
-\r
-    r->msg = NULL;\r
-    r->recipient = NULL;\r
-\r
-    return r;\r
-}\r
-\r
-/*! \internal\r
-    \brief Free a message ref object\r
-    \note called with cs_kmq_msg_ref and cs_kmq_msg held */\r
-void kmqint_put_message_ref(kmq_message_ref * r) {\r
-    if(!r)\r
-        return;\r
-    if(r->msg) {\r
-        r->msg->refcount--;\r
-        r->msg = NULL;\r
-    }\r
-    LPUSH(&kmq_msg_ref_free, r);\r
-}\r
-\r
-/*! \internal\r
-    \brief Get the queue associated with the current thread\r
-    \note Obtains ::cs_kmq_global\r
-    */\r
-kmq_queue * kmqint_get_thread_queue(void) {\r
-    kmq_queue * q;\r
-\r
-    q = (kmq_queue *) TlsGetValue(kmq_tls_queue);\r
-    if(!q) {\r
-        kmqint_attach_this_thread();\r
-        q = (kmq_queue *) TlsGetValue(kmq_tls_queue);\r
-    }\r
-\r
-    return q;\r
-}\r
-\r
-/*! \internal\r
-    \brief Get the topmost message ref for a queue\r
-    \note Obtains kmq_queue::cs\r
-    */\r
-void kmqint_get_queue_message_ref(kmq_queue * q, kmq_message_ref ** r) {\r
-    EnterCriticalSection(&q->cs);\r
-\r
-    if (q->flags & KMQ_QUEUE_FLAG_DELETED) {\r
-        *r = NULL;\r
-    } else {\r
-        QGET(q,r);\r
-        if(QTOP(q))\r
-            SetEvent(q->wait_o);\r
-    }\r
-\r
-    LeaveCriticalSection(&q->cs);\r
-}\r
-\r
-/*! \internal\r
-    \brief Post a message to a queue\r
-    \note Obtains ::cs_kmq_msg_ref, ::cs_kmq_msg, kmq_queue::cs\r
-    */\r
-void kmqint_post_queue(kmq_queue * q, kmq_message *m) {\r
-    kmq_message_ref *r;\r
-\r
-    EnterCriticalSection(&q->cs);\r
-    if (q->flags & KMQ_QUEUE_FLAG_DELETED) {\r
-        LeaveCriticalSection(&q->cs);\r
-        return;\r
-    }\r
-    LeaveCriticalSection(&q->cs);\r
-\r
-    EnterCriticalSection(&cs_kmq_msg_ref);\r
-    r = kmqint_get_message_ref();\r
-    LeaveCriticalSection(&cs_kmq_msg_ref);\r
-\r
-    r->msg = m;\r
-    r->recipient = NULL;\r
-\r
-    EnterCriticalSection(&cs_kmq_msg);\r
-    m->refcount++;\r
-    m->nSent++;\r
-    LeaveCriticalSection(&cs_kmq_msg);\r
-\r
-    EnterCriticalSection(&q->cs);\r
-    QPUT(q,r);\r
-    SetEvent(q->wait_o);\r
-    LeaveCriticalSection(&q->cs);\r
-}\r
-\r
-/*! \internal\r
-    \brief Post a message to a subscriber\r
-    \note Obtains ::cs_kmq_msg_ref, ::cs_kmq_msg, kmq_queue::cs\r
-    \note Should be called with ::cs_kmq_msg held\r
-    */\r
-void kmqint_post(kmq_msg_subscription * s, kmq_message * m, khm_boolean try_send) {\r
-    if(s->rcpt_type == KMQ_RCPTTYPE_CB) {\r
-        kmq_queue *q;\r
-        kmq_message_ref *r;\r
-\r
-        q = s->queue;\r
-\r
-        if(try_send && q->thread == GetCurrentThreadId()) {\r
-            khm_int32 rv;\r
-            /* we are sending a message from this thread to this\r
-               thread.  just call the recipient directly, bypassing\r
-               the message queue. */\r
-            m->refcount++;\r
-            m->nSent++;\r
-            if (IsBadCodePtr(s->recipient.cb)) {\r
-                rv = KHM_ERROR_INVALID_OPERATION;\r
-            } else {\r
-                rv = s->recipient.cb(m->type, m->subtype, \r
-                                     m->uparam, m->vparam);\r
-            }\r
-            m->refcount--;\r
-            if(KHM_SUCCEEDED(rv))\r
-                m->nCompleted++;\r
-            else\r
-                m->nFailed++;\r
-        } else {\r
-\r
-            EnterCriticalSection(&q->cs);\r
-            if (q->flags & KMQ_QUEUE_FLAG_DELETED) {\r
-                LeaveCriticalSection(&q->cs);\r
-                return;\r
-            }\r
-            LeaveCriticalSection(&q->cs);\r
-\r
-            EnterCriticalSection(&cs_kmq_msg_ref);\r
-            r = kmqint_get_message_ref();\r
-            LeaveCriticalSection(&cs_kmq_msg_ref);\r
-\r
-            r->msg = m;\r
-            r->recipient = s->recipient.cb;\r
-\r
-            m->refcount++;\r
-            m->nSent++;\r
-\r
-            EnterCriticalSection(&q->cs);\r
-            QPUT(q,r);\r
-            SetEvent(q->wait_o);\r
-            LeaveCriticalSection(&q->cs);\r
-        }\r
-    }\r
-\r
-#ifdef _WIN32\r
-    else if(s->rcpt_type == KMQ_RCPTTYPE_HWND) {\r
-        if(try_send && \r
-           GetCurrentThreadId() == GetWindowThreadProcessId(s->recipient.hwnd, \r
-                                                            NULL)) {\r
-            /* kmqint_post does not know whether there are any other\r
-               messages waiting to be posted at this point.  Hence,\r
-               simply sending the message is not the right thing to do\r
-               as the recipient may incorrectly assume that the\r
-               message has completed when (m->nCompleted + m->nFailed\r
-               == m->nSent).  Therefore, we only increment nSent after\r
-               the message is sent. */\r
-\r
-            m->refcount++;\r
-\r
-            /* the kmq_wm_begin()/kmq_wm_end() and kmq_wm_dispatch()\r
-               handlers decrement the reference count on the message\r
-               when they are done. */\r
-            SendMessage(s->recipient.hwnd, KMQ_WM_DISPATCH, \r
-                        m->type, (LPARAM) m);\r
-\r
-            m->nSent++;\r
-\r
-        } else {\r
-            m->nSent++;\r
-            m->refcount++;\r
-\r
-            /* the kmq_wm_begin()/kmq_wm_end() and kmq_wm_dispatch()\r
-               handlers decrement the reference count on the message\r
-               when they are done. */\r
-            PostMessage(s->recipient.hwnd, KMQ_WM_DISPATCH, \r
-                        m->type, (LPARAM) m);\r
-        }\r
-    }\r
-#endif\r
-\r
-    else {\r
-        /* This could either be because we were passed in an invalid\r
-           subscription or because we lost a race to a thread that\r
-           deleted an ad-hoc subscription. */\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-    }\r
-}\r
-\r
-/*! \internal\r
-    \brief Subscribes a window to a message type\r
-    \note Obtains ::cs_kmq_types\r
-    */\r
-KHMEXP khm_int32 KHMAPI kmq_subscribe_hwnd(khm_int32 type, HWND hwnd) {\r
-    kmq_msg_subscription * s;\r
-\r
-    s = PMALLOC(sizeof(kmq_msg_subscription));\r
-    ZeroMemory(s, sizeof(*s));\r
-    s->magic = KMQ_MSG_SUB_MAGIC;\r
-    LINIT(s);\r
-    s->queue = NULL;\r
-    s->rcpt_type = KMQ_RCPTTYPE_HWND;\r
-    s->recipient.hwnd = hwnd;\r
-    kmqint_msg_type_add_sub(type, s);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/*! \internal\r
-    \note Obtains ::cs_kmq_types, ::cs_kmq_global\r
-    */\r
-KHMEXP khm_int32 KHMAPI kmq_subscribe(khm_int32 type, kmq_callback_t cb) {\r
-    kmq_msg_subscription * s;\r
-\r
-    s = PMALLOC(sizeof(kmq_msg_subscription));\r
-    ZeroMemory(s, sizeof(*s));\r
-    s->magic = KMQ_MSG_SUB_MAGIC;\r
-    LINIT(s);\r
-    s->queue = kmqint_get_thread_queue();\r
-    s->rcpt_type = KMQ_RCPTTYPE_CB;\r
-    s->recipient.cb = cb;\r
-    kmqint_msg_type_add_sub(type, s);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kmq_create_hwnd_subscription(HWND hw,\r
-                                                     khm_handle * result)\r
-{\r
-    kmq_msg_subscription * s;\r
-\r
-    s = PMALLOC(sizeof(kmq_msg_subscription));\r
-    ZeroMemory(s, sizeof(*s));\r
-    s->magic = KMQ_MSG_SUB_MAGIC;\r
-    LINIT(s);\r
-    s->queue = NULL;\r
-    s->rcpt_type = KMQ_RCPTTYPE_HWND;\r
-    s->recipient.hwnd = hw;\r
-\r
-    EnterCriticalSection(&cs_kmq_global);\r
-    LPUSH(&kmq_adhoc_subs, s);\r
-    LeaveCriticalSection(&cs_kmq_global);\r
-\r
-    *result = (khm_handle) s;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/*! \internal\r
-    \note Obtains ::cs_kmq_global\r
-*/\r
-KHMEXP khm_int32 KHMAPI kmq_create_subscription(kmq_callback_t cb, \r
-                                                khm_handle * result)\r
-{\r
-    kmq_msg_subscription * s;\r
-\r
-    s = PMALLOC(sizeof(kmq_msg_subscription));\r
-    ZeroMemory(s, sizeof(*s));\r
-    s->magic = KMQ_MSG_SUB_MAGIC;\r
-    LINIT(s);\r
-    s->queue = kmqint_get_thread_queue();\r
-    s->rcpt_type = KMQ_RCPTTYPE_CB;\r
-    s->recipient.cb = cb;\r
-\r
-    EnterCriticalSection(&cs_kmq_global);\r
-    LPUSH(&kmq_adhoc_subs, s);\r
-    LeaveCriticalSection(&cs_kmq_global);\r
-\r
-    *result = (khm_handle) s;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kmq_delete_subscription(khm_handle sub)\r
-{\r
-    kmq_msg_subscription * s;\r
-\r
-    s = (kmq_msg_subscription *) sub;\r
-\r
-    assert(s->magic == KMQ_MSG_SUB_MAGIC);\r
-\r
-    s->type = 0;\r
-\r
-    EnterCriticalSection(&cs_kmq_global);\r
-    LDELETE(&kmq_adhoc_subs, s);\r
-    LeaveCriticalSection(&cs_kmq_global);\r
-\r
-    PFREE(s);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/*! \internal\r
-    \brief Unsubscribes a window from a message type\r
-    \note Obtains ::cs_kmq_types\r
-    */\r
-KHMEXP khm_int32 KHMAPI kmq_unsubscribe_hwnd(khm_int32 type, HWND hwnd) {\r
-    kmq_msg_subscription * s;\r
-\r
-    s = kmqint_msg_type_del_sub_hwnd(type, hwnd);\r
-    if(s)\r
-        PFREE(s);\r
-    return (s)?KHM_ERROR_SUCCESS:KHM_ERROR_NOT_FOUND;\r
-}\r
-\r
-/*! \internal\r
-    \brief Unsubscribe a callback from a message type\r
-    \note Obtains ::cs_kmq_types, ::cs_kmq_global\r
-    */\r
-KHMEXP khm_int32 KHMAPI kmq_unsubscribe(khm_int32 type, kmq_callback_t cb) {\r
-    kmq_msg_subscription * s;\r
-\r
-    s = kmqint_msg_type_del_sub_cb(type,cb);\r
-    if(s)\r
-        PFREE(s);\r
-\r
-    return (s)?KHM_ERROR_SUCCESS:KHM_ERROR_NOT_FOUND;\r
-}\r
-\r
-KHMEXP LRESULT KHMAPI kmq_wm_begin(LPARAM lparm, kmq_message ** m) {\r
-    *m = (kmq_message *) lparm;\r
-    if ((*m)->err_ctx) {\r
-        kherr_push_context((*m)->err_ctx);\r
-    }\r
-    return TRUE;\r
-}\r
-\r
-/*! \internal\r
-    \note Obtains ::cs_kmq_msg\r
-    */\r
-KHMEXP LRESULT KHMAPI kmq_wm_end(kmq_message *m, khm_int32 rv) {\r
-    if (m->err_ctx)\r
-        kherr_pop_context();\r
-\r
-    EnterCriticalSection(&cs_kmq_msg);\r
-    m->refcount--;\r
-    if(KHM_SUCCEEDED(rv))\r
-        m->nCompleted++;\r
-    else\r
-        m->nFailed++;\r
-\r
-    if(m->nCompleted + m->nFailed == m->nSent) {\r
-        kmqint_put_message(m);\r
-    }\r
-    LeaveCriticalSection(&cs_kmq_msg);\r
-\r
-    return TRUE;\r
-}\r
-\r
-/*! \internal\r
-    \note Obtains ::cs_kmq_msg\r
-    */\r
-KHMEXP LRESULT KHMAPI kmq_wm_dispatch(LPARAM lparm, kmq_callback_t cb) {\r
-    kmq_message *m;\r
-    khm_int32 rv;\r
-\r
-    m = (kmq_message *) lparm;\r
-\r
-    if (m->err_ctx)\r
-        kherr_push_context(m->err_ctx);\r
-\r
-    rv = cb(m->type, m->subtype, m->uparam, m->vparam);\r
-\r
-    if (m->err_ctx)\r
-        kherr_pop_context();\r
-\r
-    EnterCriticalSection(&cs_kmq_msg);\r
-\r
-    m->refcount--;\r
-    if(KHM_SUCCEEDED(rv))\r
-        m->nCompleted++;\r
-    else\r
-        m->nFailed++;\r
-\r
-    if(m->nCompleted + m->nFailed == m->nSent) {\r
-        kmqint_put_message(m);\r
-    }\r
-    LeaveCriticalSection(&cs_kmq_msg);\r
-\r
-    return TRUE;\r
-}\r
-\r
-KHMEXP khm_boolean KHMAPI kmq_is_call_aborted(void) {\r
-    /* TODO: Implement this */\r
-    return FALSE;\r
-}\r
-\r
-/*! \internal\r
-\r
-    \note Obtains ::cs_kmq_global, kmq_queue::cs, ::cs_kmq_msg_ref, ::cs_kmq_msg, \r
-*/\r
-KHMEXP khm_int32 KHMAPI kmq_dispatch(kmq_timer timeout) {\r
-    kmq_queue * q;\r
-    kmq_message_ref * r;\r
-    kmq_message *m;\r
-    DWORD hr;\r
-\r
-    q = kmqint_get_thread_queue();\r
-\r
-    assert(q->wait_o);\r
-\r
-    hr = WaitForSingleObject(q->wait_o, timeout);\r
-    if(hr == WAIT_OBJECT_0) {\r
-        /* signalled */\r
-        kmqint_get_queue_message_ref(q, &r);\r
-\r
-        m = r->msg;\r
-\r
-        if(m->type != KMSG_SYSTEM || m->subtype != KMSG_SYSTEM_EXIT) {\r
-            khm_boolean rv;\r
-\r
-            if (m->err_ctx)\r
-                kherr_push_context(m->err_ctx);\r
-\r
-            /* TODO: before dispatching the message, the message being\r
-               dispatched for this thread needs to be stored so that\r
-               it can be looked up in kmq_is_call_aborted(). This\r
-               needs to happen in kmq_wm_dispatch() and\r
-               kmq_wm_begin() as well. */\r
-\r
-            /* dispatch */\r
-            rv = r->recipient(m->type, m->subtype, m->uparam, m->vparam);\r
-\r
-            if (m->err_ctx)\r
-                kherr_pop_context();\r
-\r
-            EnterCriticalSection(&cs_kmq_msg);\r
-            EnterCriticalSection(&cs_kmq_msg_ref);\r
-            kmqint_put_message_ref(r);\r
-            LeaveCriticalSection(&cs_kmq_msg_ref);\r
-\r
-            if(KHM_SUCCEEDED(rv))\r
-                m->nCompleted++;\r
-            else\r
-                m->nFailed++;\r
-\r
-            if(m->nCompleted + m->nFailed == m->nSent) {\r
-                kmqint_put_message(m);\r
-            }\r
-            LeaveCriticalSection(&cs_kmq_msg);\r
-\r
-            return KHM_ERROR_SUCCESS;\r
-        } else {\r
-            EnterCriticalSection(&cs_kmq_msg);\r
-            EnterCriticalSection(&cs_kmq_msg_ref);\r
-            kmqint_put_message_ref(r);\r
-            LeaveCriticalSection(&cs_kmq_msg_ref);\r
-            m->nCompleted++;\r
-            if(m->nCompleted + m->nFailed == m->nSent) {\r
-                kmqint_put_message(m);\r
-            }\r
-            LeaveCriticalSection(&cs_kmq_msg);\r
-\r
-            return KHM_ERROR_EXIT;\r
-        }\r
-    } else {\r
-        return KHM_ERROR_TIMEOUT;\r
-    }\r
-}\r
-\r
-/* TODO: rename this file to subscriber.c */\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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;
+
+#ifdef DEBUG
+
+#include<stdio.h>
+
+void
+kmqint_dump_consumer(FILE * f) {
+    kmq_message_ref * r;
+    kmq_msg_subscription * s;
+
+    int n_free = 0;
+    int n_adhoc = 0;
+
+    EnterCriticalSection(&cs_kmq_msg_ref);
+
+    fprintf(f, "qc0\t*** Free Message References ***\n");
+
+    fprintf(f, "qc1\tAddress\n");
+
+    r = kmq_msg_ref_free;
+    while(r) {
+        n_free ++;
+
+        fprintf(f, "qc2\t0x%p\n", r);
+
+        r = LNEXT(r);
+    }
+
+    fprintf(f, "qc3\tTotal free message references : %d\n", n_free);
+
+    fprintf(f, "qc4\t--- End ---\n");
+
+    LeaveCriticalSection(&cs_kmq_msg_ref);
+
+    EnterCriticalSection(&cs_kmq_global);
+
+    fprintf(f, "qc5\t*** Adhoc Message Subscriptions ***\n");
+
+    fprintf(f, "qc6\tAddress\tMsg Type\tRcpt Type\tRcpt\tQueue\n");
+
+    s = kmq_adhoc_subs;
+
+    while(s) {
+        n_adhoc ++;
+
+        fprintf(f, "qc7\t0x%p\t%d\t%s\t0x%p\t0x%p\n",
+                s,
+                s->type,
+                (s->rcpt_type == KMQ_RCPTTYPE_CB)?"CALLBACK":"HWND",
+                (s->rcpt_type == KMQ_RCPTTYPE_CB) ? (void *) s->recipient.cb: (void *) s->recipient.hwnd,
+                s->queue);
+
+        s = LNEXT(s);
+    }
+
+    fprintf(f, "qc8\tTotal ad-hoc subscriptions : %d\n", n_adhoc);
+
+    fprintf(f, "qc9\t--- End ---\n");
+
+    LeaveCriticalSection(&cs_kmq_global);
+
+}
+
+#endif
+
+/*! \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 = PMALLOC(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);
+
+    if (q->flags & KMQ_QUEUE_FLAG_DELETED) {
+        *r = NULL;
+    } else {
+        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(&q->cs);
+    if (q->flags & KMQ_QUEUE_FLAG_DELETED) {
+        LeaveCriticalSection(&q->cs);
+        return;
+    }
+    LeaveCriticalSection(&q->cs);
+
+    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++;
+            if (IsBadCodePtr(s->recipient.cb)) {
+                rv = KHM_ERROR_INVALID_OPERATION;
+            } else {
+                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(&q->cs);
+            if (q->flags & KMQ_QUEUE_FLAG_DELETED) {
+                LeaveCriticalSection(&q->cs);
+                return;
+            }
+            LeaveCriticalSection(&q->cs);
+
+            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) {
+        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. */
+
+            m->refcount++;
+
+            /* the kmq_wm_begin()/kmq_wm_end() and kmq_wm_dispatch()
+               handlers decrement the reference count on the message
+               when they are done. */
+            SendMessage(s->recipient.hwnd, KMQ_WM_DISPATCH, 
+                        m->type, (LPARAM) m);
+
+            m->nSent++;
+
+        } else {
+            m->nSent++;
+            m->refcount++;
+
+            /* the kmq_wm_begin()/kmq_wm_end() and kmq_wm_dispatch()
+               handlers decrement the reference count on the message
+               when they are done. */
+            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);
+#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 = PMALLOC(sizeof(kmq_msg_subscription));
+    ZeroMemory(s, sizeof(*s));
+    s->magic = KMQ_MSG_SUB_MAGIC;
+    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 = PMALLOC(sizeof(kmq_msg_subscription));
+    ZeroMemory(s, sizeof(*s));
+    s->magic = KMQ_MSG_SUB_MAGIC;
+    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;
+}
+
+KHMEXP khm_int32 KHMAPI kmq_create_hwnd_subscription(HWND hw,
+                                                     khm_handle * result)
+{
+    kmq_msg_subscription * s;
+
+    s = PMALLOC(sizeof(kmq_msg_subscription));
+    ZeroMemory(s, sizeof(*s));
+    s->magic = KMQ_MSG_SUB_MAGIC;
+    LINIT(s);
+    s->queue = NULL;
+    s->rcpt_type = KMQ_RCPTTYPE_HWND;
+    s->recipient.hwnd = hw;
+
+    EnterCriticalSection(&cs_kmq_global);
+    LPUSH(&kmq_adhoc_subs, s);
+    LeaveCriticalSection(&cs_kmq_global);
+
+    *result = (khm_handle) 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 = PMALLOC(sizeof(kmq_msg_subscription));
+    ZeroMemory(s, sizeof(*s));
+    s->magic = KMQ_MSG_SUB_MAGIC;
+    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;
+
+    assert(s->magic == KMQ_MSG_SUB_MAGIC);
+
+    s->type = 0;
+
+    EnterCriticalSection(&cs_kmq_global);
+    LDELETE(&kmq_adhoc_subs, s);
+    LeaveCriticalSection(&cs_kmq_global);
+
+    PFREE(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)
+        PFREE(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)
+        PFREE(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;
+}
+
+KHMEXP khm_boolean KHMAPI kmq_is_call_aborted(void) {
+    /* TODO: Implement this */
+    return FALSE;
+}
+
+/*! \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();
+
+    assert(q->wait_o);
+
+    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);
+
+            /* TODO: before dispatching the message, the message being
+               dispatched for this thread needs to be stored so that
+               it can be looked up in kmq_is_call_aborted(). This
+               needs to happen in kmq_wm_dispatch() and
+               kmq_wm_begin() as well. */
+
+            /* 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 */
index 875e3eaf3a69a4453b569cff3128e301a2680f21..493780f7982545ed7457f827ae76dc0c011f55c6 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kmqinternal.h>\r
-#include<kconfig.h>\r
-#include<assert.h>\r
-\r
-CRITICAL_SECTION cs_kmq_global;\r
-kmq_timer kmq_queue_dead_timeout;\r
-kmq_timer kmq_call_dead_timeout;\r
-\r
-kmq_queue * queues;\r
-\r
-LONG kmq_init_once = 0;\r
-\r
-void kmqint_init(void) {\r
-    khm_handle hconfig = NULL;\r
-\r
-    queues = NULL;\r
-\r
-    InitializeCriticalSection(&cs_kmq_global);\r
-    InitializeCriticalSection(&cs_kmq_msg);\r
-    InitializeCriticalSection(&cs_kmq_msg_ref);\r
-\r
-    EnterCriticalSection(&cs_kmq_global);\r
-    khc_load_schema(NULL, schema_kmqconfig);\r
-    khc_open_space(NULL, KMQ_CONF_SPACE_NAME, KHM_PERM_READ, &hconfig);\r
-    if(hconfig) {\r
-        khm_int32 t = 0;\r
-\r
-        khc_read_int32(hconfig, KMQ_CONF_QUEUE_DEAD_TIMEOUT_NAME, &t);\r
-        kmq_queue_dead_timeout = t;\r
-\r
-        khc_read_int32(hconfig, KMQ_CONF_CALL_DEAD_TIMEOUT_NAME, &t);\r
-        kmq_call_dead_timeout = t;\r
-\r
-        khc_close_space(hconfig);\r
-    }\r
-    kmqint_init_msg_types();\r
-    LeaveCriticalSection(&cs_kmq_global);\r
-\r
-    kmq_tls_queue = TlsAlloc();\r
-}\r
-\r
-void kmqint_exit(void) {\r
-    EnterCriticalSection(&cs_kmq_global);\r
-    kmqint_exit_msg_types();\r
-    LeaveCriticalSection(&cs_kmq_global);\r
-    DeleteCriticalSection(&cs_kmq_msg);\r
-    DeleteCriticalSection(&cs_kmq_msg_ref);\r
-    DeleteCriticalSection(&cs_kmq_global);\r
-\r
-    TlsFree(kmq_tls_queue);\r
-}\r
-\r
-/*! \internal\r
-    \brief Preps a thread for use with kmq\r
-    \note Obtains ::cs_kmq_global\r
-    */\r
-void kmqint_attach_this_thread(void) {\r
-    kmq_queue * q;\r
-\r
-    EnterCriticalSection(&cs_kmq_global);\r
-\r
-    q = (kmq_queue *) TlsGetValue(kmq_tls_queue);\r
-    if(!q) {\r
-        q = PMALLOC(sizeof(kmq_queue));\r
-\r
-        InitializeCriticalSection(&q->cs);\r
-        q->thread = GetCurrentThreadId();\r
-        QINIT(q);\r
-        LINIT(q);\r
-        q->wait_o = CreateEvent(NULL, FALSE, FALSE, NULL);\r
-        q->load = 0;\r
-        q->last_post = 0;\r
-        q->flags = 0;\r
-\r
-        LPUSH(&queues, q);\r
-\r
-        TlsSetValue(kmq_tls_queue, (LPVOID) q);\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_kmq_global);\r
-}\r
-\r
-/*! \internal\r
-    \brief Detaches the current thread from kmq\r
-    \note Obtains ::cs_kmq_global\r
-    */\r
-void kmqint_detach_this_thread(void) {\r
-    kmq_queue * q;\r
-\r
-    q = (kmq_queue *) TlsGetValue(kmq_tls_queue);\r
-    if(q) {\r
-        kmq_message_ref * r;\r
-        kmq_message * m;\r
-\r
-        EnterCriticalSection(&q->cs);\r
-\r
-        if (q->flags & KMQ_QUEUE_FLAG_DETACHING) {\r
-#ifdef DEBUG\r
-            assert(FALSE);\r
-#endif\r
-            LeaveCriticalSection(&q->cs);\r
-            return;\r
-        }\r
-\r
-        q->flags |= KMQ_QUEUE_FLAG_DELETED | KMQ_QUEUE_FLAG_DETACHING;\r
-\r
-        QGET(q, &r);\r
-        while(r) {\r
-\r
-            m = r->msg;\r
-\r
-            LeaveCriticalSection(&q->cs);\r
-\r
-            EnterCriticalSection(&cs_kmq_msg);\r
-            EnterCriticalSection(&cs_kmq_msg_ref);\r
-            kmqint_put_message_ref(r);\r
-            LeaveCriticalSection(&cs_kmq_msg_ref);\r
-\r
-            m->nFailed++;\r
-            if(m->nCompleted + m->nFailed == m->nSent) {\r
-                kmqint_put_message(m);\r
-            }\r
-            LeaveCriticalSection(&cs_kmq_msg);\r
-\r
-            EnterCriticalSection(&q->cs);\r
-\r
-            QGET(q, &r);\r
-        }\r
-\r
-        CloseHandle(q->wait_o);\r
-\r
-        q->wait_o = NULL;\r
-\r
-        q->flags &= ~KMQ_QUEUE_FLAG_DETACHING;\r
-        \r
-        LeaveCriticalSection(&q->cs);\r
-\r
-        /* For now, we don't free the queue. */\r
-\r
-        /* TODO: before we can free the queue, we have to go through\r
-           all the message type subscriptions and ad-hoc subscriptions\r
-           and make sure no subscriptions exist which refer to this\r
-           message queue. */\r
-    }\r
-}\r
-\r
-HANDLE kmq_h_compl = NULL;\r
-kmq_thread_id kmq_tid_compl;\r
-\r
-/* Message transfer */\r
-struct tag_kmq_msg_xfer {\r
-    QDCL(kmq_message);\r
-} kmq_completion_xfer;\r
-\r
-HANDLE compl_wx;\r
-BOOL compl_continue;\r
-CRITICAL_SECTION cs_compl;\r
-\r
-DWORD WINAPI kmqint_completion_thread_proc(LPVOID p) {\r
-    kmq_message * m;\r
-    kherr_context * ctx;\r
-\r
-    PDESCTHREAD(L"Msg completion thread", L"KMQ");\r
-\r
-    EnterCriticalSection(&cs_compl);\r
-    do {\r
-       \r
-        if (QTOP(&kmq_completion_xfer) == NULL) {\r
-            LeaveCriticalSection(&cs_compl);\r
-            WaitForSingleObject(compl_wx, INFINITE);\r
-            EnterCriticalSection(&cs_compl);\r
-            /* go through the loop again before checking the queue */\r
-        } else {\r
-            QGET(&kmq_completion_xfer, &m);\r
-            LeaveCriticalSection(&cs_compl);\r
-            EnterCriticalSection(&cs_kmq_msg);\r
-\r
-            ctx = m->err_ctx;\r
-\r
-            if (ctx)\r
-                kherr_push_context(ctx);\r
-\r
-            kmqint_put_message(m);\r
-\r
-            if (ctx)\r
-                kherr_pop_context();\r
-\r
-            LeaveCriticalSection(&cs_kmq_msg);\r
-            EnterCriticalSection(&cs_compl);\r
-        }\r
-\r
-    } while(compl_continue);\r
-\r
-    LeaveCriticalSection(&cs_compl);\r
-\r
-    ExitThread(0);\r
-\r
-    /* not reached */\r
-    return 0;\r
-}\r
-\r
-int kmqint_call_completion_handler(kmq_msg_completion_handler h,\r
-                                    kmq_message * m) {\r
-    if (h == NULL)\r
-        return 0;\r
-\r
-    /* We only dispatch to the completion thread if we are not the\r
-       completion thread.  If calling the completion handler results\r
-       in more messages completing, then we just call the completion\r
-       handler directly.  We also make an exception for completions\r
-       that happen before the message queue is properly intiailized. */\r
-\r
-    if (kmq_tid_compl != GetCurrentThreadId() &&\r
-        kmq_h_compl != NULL) {\r
-\r
-        EnterCriticalSection(&cs_compl);\r
-        QPUT(&kmq_completion_xfer, m);\r
-        SetEvent(compl_wx);\r
-        LeaveCriticalSection(&cs_compl);\r
-\r
-        return 1;\r
-\r
-    } else {\r
-        h(m);\r
-\r
-        return 0;\r
-    }\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kmq_init(void) {\r
-    if (InterlockedIncrement(&kmq_init_once) == 1) {\r
-        EnterCriticalSection(&cs_kmq_global);\r
-\r
-        InitializeCriticalSection(&cs_compl);\r
-        compl_wx = CreateEvent(NULL, FALSE, FALSE, NULL);\r
-        compl_continue = TRUE;\r
-        QINIT(&kmq_completion_xfer);\r
-\r
-        kmq_h_compl = CreateThread(NULL,\r
-                                   0,\r
-                                   kmqint_completion_thread_proc,\r
-                                   NULL,\r
-                                   0,\r
-                                   &kmq_tid_compl);\r
-\r
-        assert(kmq_h_compl != NULL);\r
-\r
-        LeaveCriticalSection(&cs_kmq_global);\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kmq_exit(void) {\r
-    if (InterlockedDecrement(&kmq_init_once) == 0) {\r
-\r
-        EnterCriticalSection(&cs_compl);\r
-        compl_continue = FALSE;\r
-        SetEvent(compl_wx);\r
-        LeaveCriticalSection(&cs_compl);\r
-\r
-        WaitForSingleObject(kmq_h_compl, INFINITE);\r
-\r
-        EnterCriticalSection(&cs_kmq_global);\r
-        CloseHandle(kmq_h_compl);\r
-        kmq_h_compl = NULL;\r
-        kmq_tid_compl = 0;\r
-        CloseHandle(compl_wx);\r
-        DeleteCriticalSection(&cs_compl);\r
-        LeaveCriticalSection(&cs_kmq_global);\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-#ifdef DEBUG\r
-\r
-void kmqint_dump_consumer(FILE * f);\r
-void kmqint_dump_publisher(FILE * f);\r
-\r
-\r
-KHMEXP void KHMAPI kmqint_dump(FILE * f) {\r
-    kmqint_dump_consumer(f);\r
-    kmqint_dump_publisher(f);\r
-}\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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) {
+        khm_int32 t = 0;
+
+        khc_read_int32(hconfig, KMQ_CONF_QUEUE_DEAD_TIMEOUT_NAME, &t);
+        kmq_queue_dead_timeout = t;
+
+        khc_read_int32(hconfig, KMQ_CONF_CALL_DEAD_TIMEOUT_NAME, &t);
+        kmq_call_dead_timeout = t;
+
+        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;
+
+    EnterCriticalSection(&cs_kmq_global);
+
+    q = (kmq_queue *) TlsGetValue(kmq_tls_queue);
+    if(!q) {
+        q = PMALLOC(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;
+        q->flags = 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) {
+        kmq_message_ref * r;
+        kmq_message * m;
+
+        EnterCriticalSection(&q->cs);
+
+        if (q->flags & KMQ_QUEUE_FLAG_DETACHING) {
+#ifdef DEBUG
+            assert(FALSE);
+#endif
+            LeaveCriticalSection(&q->cs);
+            return;
+        }
+
+        q->flags |= KMQ_QUEUE_FLAG_DELETED | KMQ_QUEUE_FLAG_DETACHING;
+
+        QGET(q, &r);
+        while(r) {
+
+            m = r->msg;
+
+            LeaveCriticalSection(&q->cs);
+
+            EnterCriticalSection(&cs_kmq_msg);
+            EnterCriticalSection(&cs_kmq_msg_ref);
+            kmqint_put_message_ref(r);
+            LeaveCriticalSection(&cs_kmq_msg_ref);
+
+            m->nFailed++;
+            if(m->nCompleted + m->nFailed == m->nSent) {
+                kmqint_put_message(m);
+            }
+            LeaveCriticalSection(&cs_kmq_msg);
+
+            EnterCriticalSection(&q->cs);
+
+            QGET(q, &r);
+        }
+
+        CloseHandle(q->wait_o);
+
+        q->wait_o = NULL;
+
+        q->flags &= ~KMQ_QUEUE_FLAG_DETACHING;
+        
+        LeaveCriticalSection(&q->cs);
+
+        /* For now, we don't free the queue. */
+
+        /* TODO: before we can free the queue, we have to go through
+           all the message type subscriptions and ad-hoc subscriptions
+           and make sure no subscriptions exist which refer to this
+           message queue. */
+    }
+}
+
+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;
+
+    PDESCTHREAD(L"Msg completion thread", L"KMQ");
+
+    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;
+}
+
+#ifdef DEBUG
+
+void kmqint_dump_consumer(FILE * f);
+void kmqint_dump_publisher(FILE * f);
+
+
+KHMEXP void KHMAPI kmqint_dump(FILE * f) {
+    kmqint_dump_consumer(f);
+    kmqint_dump_publisher(f);
+}
+
+#endif
index 46ce74f3b2f3b65645fe28ef5769adffae8bdae2..d1a3db3c2d1fab8d4ca59a2e75a8060567039d6b 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KMQ_H__\r
-#define __KHIMAIRA_KMQ_H__\r
-\r
-/*! \defgroup kmq NetIDMgr Message Queue */\r
-/*@{*/\r
-\r
-#include<khdefs.h>\r
-#include<khlist.h>\r
-#include<kherr.h>\r
-\r
-/* general */\r
-#ifdef _WIN32\r
-typedef DWORD kmq_thread_id;\r
-typedef DWORD kmq_timer;\r
-#endif\r
-\r
-#ifdef _WIN32\r
-/*! \brief Window message for kmq\r
-\r
-   This message is sent to the window procedure of a window if that\r
-   window is a subscriber to KMQ messages.\r
-\r
-    \see kmq_subscribe_hwnd() for more information about handling this\r
-        window message\r
- */\r
-#define KMQ_WM_DISPATCH (WM_APP+0x100)\r
-#endif\r
-\r
-/* callback */\r
-\r
-/*! \brief A message callback\r
-\r
-    Should return TRUE if the message is properly handled.  Otherwise\r
-    return FALSE */\r
-typedef khm_int32 (KHMAPI *kmq_callback_t)(khm_int32 msg_type, \r
-                                           khm_int32 msg_sub_type, \r
-                                           khm_ui_4 uparam, \r
-                                           void * vparam);\r
-\r
-/* message */\r
-\r
-/*! \brief A single response.\r
-\r
-    Certain broadcast messages may user scatter-gather type\r
-    notification and result gathering.  Individual subscribers to a\r
-    message attach their individual responses to a ::kmq_response\r
-    object and attach that to the message which can later be read by\r
-    the sender of the message.\r
- */\r
-typedef struct tag_kmq_response {\r
-    kmq_thread_id thread;\r
-    void * response;\r
-\r
-    LDCL(struct tag_kmq_response);\r
-} kmq_response;\r
-\r
-/*! \brief A single message\r
- */\r
-typedef struct tag_kmq_message {\r
-    khm_int32 type;             /*!< Type of message */\r
-    khm_int32 subtype;          /*!< Subtype of message */\r
-\r
-    khm_ui_4 uparam;             /*!< Integer parameter */\r
-    void * vparam;            /*!< Pointer to parameter blob */\r
-       \r
-    khm_int32 nSent;            /*!< Number of instances of message\r
-                                  sent (for broadcast messages) */\r
-\r
-    khm_int32 nCompleted;       /*!< Number of instances that have\r
-                                  completed processing (for broadcast\r
-                                  messages) */\r
-\r
-    khm_int32 nFailed;          /*!< Number of instances that failed\r
-                                  to process (for broadcast\r
-                                  messages) */\r
-\r
-    kmq_response * responses;   /*!< List of responses */\r
-    HANDLE wait_o;              /*!< Event to wait on (only valid if\r
-                                  the publisher of the message\r
-                                  requested a handle to the call) */\r
-\r
-    kmq_timer timeSent;         /*!< Time at which the message was\r
-                                  sent */\r
-    kmq_timer timeExpire;       /*!< Time at which the message\r
-                                  expires */\r
-\r
-    kherr_context * err_ctx;    /*!< Error context for the message */\r
-\r
-    khm_boolean aborted;        /*!< TRUE if the message has been\r
-                                  aborted. */\r
-\r
-    khm_int32 refcount;         /*!< Internal use */\r
-\r
-    LDCL(struct tag_kmq_message); /*!< Internal use */\r
-} kmq_message;\r
-\r
-/*! \brief A handle to a call\r
- */\r
-typedef kmq_message *kmq_call;\r
-\r
-/* publishers */\r
-\r
-/*! \brief A completion handler for a message\r
-\r
-    Each message type can have a completion handler.  Once a message\r
-    of this a specific type has been broadcast and handled by all the\r
-    subscripbers, the message will be passed down to the completion\r
-    handler before the associated data structures are freed.  This\r
-    allows applications that define message type to also define clean\r
-    up for each message.  For example, the completion handler can\r
-    initiate another message if the messages form a sequence or free\r
-    up blocks of memory that was passed as the parameter to the\r
-    message.\r
- */\r
-typedef void (KHMAPI *kmq_msg_completion_handler)(kmq_message *);\r
-\r
-#ifdef NOEXPORT\r
-\r
-KHMEXP khm_int32 KHMAPI kmq_init(void);\r
-\r
-KHMEXP khm_int32 KHMAPI kmq_exit(void);\r
-\r
-#endif\r
-\r
-/*! \brief Register a message type\r
-\r
-    Registers a custom message type.  The \a name parameter specifies\r
-    a language independent name for the message type and must be\r
-    unique and must be less than ::KMQ_MAXCCH_TYPE_NAME characters.\r
-\r
-    \param[in] name Name of the message type.  Upto\r
-        ::KMQ_MAXCCH_TYPE_NAME characters including terminating NULL.\r
-        The \a name cannot be a zero length string.\r
-\r
-    \param[out] new_id Receives the new message type ID.  Specify NULL\r
-        if the new message type is not required.\r
-\r
-    \see kmq_find_type() and kmq_unregister_type()\r
-\r
-    \retval KHM_ERROR_INVALID_PARAM The \a name parameter was invalid.\r
-    \retval KHM_ERROR_EXISTS A message type with that name already exists.\r
-    \retval KHM_ERROR_NO_RESOURCES Can't register any more message types.\r
-    \retval KHM_ERROR_SUCCESS The operation succeeded.\r
- */\r
-KHMEXP khm_int32 KHMAPI kmq_register_type(wchar_t * name, khm_int32 * new_id);\r
-\r
-/*! \brief Find a message type\r
-\r
-    Find the message type with the given name.  If found, the type ID\r
-    is returned in \a id.\r
-\r
-    \retval KHM_ERROR_SUCCESS A message type with the given name was\r
-        found.\r
-    \retval KHM_ERROR_NOT_FOUND A message type with the given name was\r
-        not found.\r
- */\r
-KHMEXP khm_int32 KHMAPI kmq_find_type(wchar_t * name, khm_int32 * id);\r
-\r
-/*! \brief Unregister a message type\r
-\r
-    Unregisters a message type that was registered using\r
-    kmq_register_type().\r
-\r
-    \retval KHM_ERROR_SUCCESS The specified message type was\r
-        successfully unregistered.\r
-\r
-    \retval KHM_ERROR_NOT_FOUND The message type was not found.\r
- */\r
-KHMEXP khm_int32 KHMAPI kmq_unregister_type(khm_int32 id);\r
-\r
-/*! \brief Subscribte to a message type.\r
-\r
-    Adds a subscription to messages of type \a type.  Subscriptions\r
-    are managed per thread.  Therefore the subscription is actually\r
-    added to the subscription list for the current thread (the thread\r
-    which calls kmq_subscribe()).\r
-\r
-    When a message of type \a type is received by the thread, it is\r
-    dispatched to the callback function identified by \a cb within the\r
-    context of this thread.\r
-\r
-    \note Calling kmq_subscribe() from within multiple threads with\r
-        the same \a type and \a cb will result in multiple\r
-        subscriptions.\r
-\r
-    \see kmq_unsubscribe()\r
-    \see kmq_dispatch()\r
-*/\r
-KHMEXP khm_int32 KHMAPI kmq_subscribe(khm_int32 type, kmq_callback_t cb);\r
-\r
-/*! \brief Subscribe a window to a message type\r
-\r
-    Adds the window specified by \a hwnd to the subscription list for\r
-    the message type \a type.  When a message of this type is posted,\r
-    then the window procedure of the window \a hwnd receives a message\r
-    ::KMQ_WM_DISPATCH.\r
-\r
-    When a window receives a ::KMQ_WM_DISPATCH message, it means that\r
-    a message has been posted which is of a type that the window has\r
-    subscribed for.  Because of the way Windows handles window\r
-    messages and the way NetIDMgr message queues work, a thread which\r
-    has a window (or thread) procedure can not call kmq_dispatch() to\r
-    handle these messages.  For threads that have window or thread\r
-    message loops, they must call kmq_subscribe_hwnd() to subscribe a\r
-    particular window (for thread message loops, this would be the\r
-    HWND of the message window for the thread) to NetIDMgr messages.\r
-\r
-    There are two supported ways of handling the ::KMQ_WM_DISPATCH\r
-    message.  Examples of both are provided below.\r
-\r
-    Handling the message inline:\r
-\r
-    \code\r
-    LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {\r
-    kmq_message * m;\r
-    khm_int32 rv;\r
-    ...\r
-    switch(uMsg) {\r
-    case WM_CREATE:\r
-       ...\r
-       kmq_subscribe_hwnd(KMSG_CRED, hwnd);\r
-       ...\r
-       break;\r
-\r
-    case WM_DESTROY:\r
-       ...\r
-       kmq_unsubscribe_hwnd(KMSG_CRED, hwnd);\r
-       ...\r
-       break;\r
-\r
-    ...\r
-    case KMQ_WM_DISPATCH:\r
-        kmq_wm_begin(lParam,&m);\r
-\r
-       if(m->type == KMSG_CRED && m->subtype == KMSG_CRED_ROOTDELTA) {\r
-       // do something\r
-        rv = KHM_ERROR_SUCCESS;\r
-       }\r
-\r
-       return kmq_wm_end(m, rv);\r
-    ...\r
-    };\r
-    ...\r
-    }\r
-    \endcode\r
-\r
-    The other method is to dispatch the ::KMQ_WM_DISPATCH message to a\r
-    secondary callback function:\r
-\r
-    \code\r
-    khm_int32 msg_handler(khm_int32 t, khm_int32 st, khm_ui_4 up, void * pb) {\r
-        khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-        //handle message\r
-\r
-       return rv;\r
-    }\r
-\r
-    LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {\r
-    kmq_message * m;\r
-    khm_int32 rv;\r
-    ...\r
-    switch(uMsg) {\r
-    ...\r
-\r
-    case WM_CREATE:\r
-       ...\r
-       kmq_subscribe_hwnd(KMSG_CRED, hwnd);\r
-       ...\r
-       break;\r
-\r
-    case WM_DESTROY:\r
-       ...\r
-       kmq_unsubscribe_hwnd(KMSG_CRED, hwnd);\r
-       ...\r
-       break;\r
-\r
-    ...\r
-    case KMQ_WM_DISPATCH:\r
-        return kmq_wm_dispatch(lParam, msg_handler);\r
-    ...\r
-    };\r
-    ...\r
-    }\r
-    \endcode\r
-\r
-    \note Make sure you unsubscribe from the message type when the\r
-        window is destroyed.\r
-\r
-    \see kmq_unsubscribe_hwnd()\r
-    \see kmq_wm_begin()\r
-    \see kmq_wm_end()\r
-    \see kmq_wm_dispatch()\r
- */\r
-KHMEXP khm_int32 KHMAPI kmq_subscribe_hwnd(khm_int32 type, HWND hwnd);\r
-\r
-#ifdef _WIN32\r
-/*! \brief Begins handling a KMQ_WM_DISPATCH message\r
-\r
-    \return The return value of this function should be ignored.\r
-\r
-    \see kmq_subscribe_hwnd() for more details about handling ::KMQ_WM_DISPATCH\r
- */\r
-KHMEXP LRESULT KHMAPI kmq_wm_begin(LPARAM lparm, kmq_message ** m);\r
-\r
-/*! \brief Ends handling a KMQ_WM_DISPATCH message\r
-\r
-    \return The return value of this function should be the return\r
-        value of the window procedure.  See kmq_subscribe_hwnd()\r
-        documentation for example\r
-\r
-    \see kmq_subscribe_hwnd() for more details about handling ::KMQ_WM_DISPATCH\r
- */\r
-KHMEXP LRESULT KHMAPI kmq_wm_end(kmq_message *m, khm_int32 rv);\r
-\r
-/*! \brief Dispatches a KMQ_WM_DISPATCH message to a callback\r
-\r
-    \return The return value of this function should be the return\r
-        value of the window procedure.  See kmq_subscribe_hwnd()\r
-        documentation for example.\r
-\r
-    \see kmq_subscribe_hwnd() for more details about handling ::KMQ_WM_DISPATCH\r
- */\r
-KHMEXP LRESULT KHMAPI kmq_wm_dispatch(LPARAM lparm, kmq_callback_t cb);\r
-#endif\r
-\r
-/*! \brief Unsubscribe a callback from a message type\r
-\r
-    Removes the subscription for message type \a type for callback\r
-    function \a cb from the subscription list for the current thread\r
-    (the thread that calls kmq_unsubscribe()).\r
-\r
-    \note kmq_unsubscribe() can only remove subscriptions for the subscription\r
-        list for the current thread.\r
-\r
-    \see kmq_subscribe()\r
-    \see kmq_dispatch()\r
-*/\r
-KHMEXP khm_int32 KHMAPI kmq_unsubscribe(khm_int32 type, kmq_callback_t cb);\r
-\r
-/*! \brief Unsubscribe a window from a message type\r
-\r
-    Removes the specific window from the subsription list for message\r
-    type \a type.\r
-\r
-    \see kmq_subscribe_hwnd()\r
-*/\r
-KHMEXP khm_int32 KHMAPI kmq_unsubscribe_hwnd(khm_int32 type, HWND hwnd);\r
-\r
-/*! \brief Create an ad-hoc subscription\r
-\r
-    An ad-hoc subscription describes a callback point in a thread that\r
-    can be dispatched messages to individually without broadcasting.\r
-\r
-    \see kmq_post_sub_msg(), kmq_post_sub_msg_ex(),\r
-        kmq_send_sub_msg(), kmq_post_subs_msg(),\r
-        kmq_post_subs_msg_ex(), kmq_send_subs_msg(),\r
-        kmq_delete_subscription()\r
-*/\r
-KHMEXP khm_int32 KHMAPI kmq_create_subscription(\r
-    kmq_callback_t cb, \r
-    khm_handle * result);\r
-\r
-/*! \brief Create an ad-hoc subscription for a window\r
-\r
-    An ad-hoc subscription describes a window that will be dispatched\r
-    messages individually without broadcasting.\r
-\r
-    \see kmq_post_sub_msg(), kmq_post_sub_msg_ex(),\r
-        kmq_send_sub_msg(), kmq_post_subs_msg(),\r
-        kmq_post_subs_msg_ex(), kmq_send_subs_msg(),\r
-        kmq_delete_subscription()\r
- */\r
-KHMEXP khm_int32 KHMAPI kmq_create_hwnd_subscription(HWND hw,\r
-                                                     khm_handle * result);\r
-\r
-/*! \brief Delete an ad-hoc subscription\r
-\r
-    Deletes a subscriptoin that was created using\r
-    kmq_create_subscription()\r
- */\r
-KHMEXP khm_int32 KHMAPI kmq_delete_subscription(khm_handle sub);\r
-\r
-/*! \brief Post a message to a subscription\r
-\r
-    Equivalent of kmq_post_msg() but only posts the message to the\r
-    specified subscription.\r
- */\r
-KHMEXP khm_int32 KHMAPI kmq_post_sub_msg(\r
-    khm_handle sub, \r
-    khm_int32 type, \r
-    khm_int32 subtype, \r
-    khm_ui_4 uparam, \r
-    void * vparam);\r
-\r
-/*! \brief Post a message to a subscription and acquire a handle to the call\r
- */\r
-KHMEXP khm_int32 KHMAPI kmq_post_sub_msg_ex(\r
-    khm_handle sub, \r
-    khm_int32 type, \r
-    khm_int32 subtype, \r
-    khm_ui_4 uparam, \r
-    void * vparam, \r
-    kmq_call * call);\r
-\r
-/*! \brief Send a synchronous message to a subscription\r
-\r
-    \retval KHM_ERROR_SUCCESS The call succeeded, and no subscribers reported errors\r
-    \retval KHM_ERROR_PARTIAL The call succeeded, but at least one subscriber reported errors\r
- */\r
-KHMEXP khm_int32 KHMAPI kmq_send_sub_msg(\r
-    khm_handle sub, \r
-    khm_int32 type, \r
-    khm_int32 subtype, \r
-    khm_ui_4 uparam, \r
-    void * vparam);\r
-\r
-/*! \brief Post a message to a group of subscriptions\r
-\r
-    The block of memory pointed to by \a subs should be an array of\r
-    subscriptions.  The number of elements in that array should be \a\r
-    n_subs.  A message as specified by the remaining parameters will\r
-    be dispatched to all of the subscription points in the array.\r
- */\r
-KHMEXP khm_int32 KHMAPI kmq_post_subs_msg(\r
-    khm_handle * subs, \r
-    khm_size  n_subs, \r
-    khm_int32 type, \r
-    khm_int32 subtype, \r
-    khm_ui_4 uparam, \r
-    void * vparam);\r
-\r
-/*! \brief Post a message to a group of subscriptions and acquire a handle to the call\r
-\r
-    The block of memory pointed to by \a subs should be an array of\r
-    subscriptions.  The number of elements in that array should be \a\r
-    n_subs.  A message as specified by the remaining parameters will\r
-    be dispatched to all of the subscription points in the array, and\r
-    a handle to the call will be returned in \a call.\r
-\r
-    The returned \a call will reference all of the dispatches that\r
-    were made.\r
-*/\r
-KHMEXP khm_int32 KHMAPI kmq_post_subs_msg_ex(\r
-    khm_handle * subs, \r
-    khm_int32 n_subs, \r
-    khm_int32 type, \r
-    khm_int32 subtype, \r
-    khm_ui_4 uparam, \r
-    void * vparam, \r
-    kmq_call * call);\r
-\r
-/*! \brief Send a synchronous message to a group of subscriptions\r
-\r
-    The block of memory pointed to by \a subs should be an array of\r
-    subscriptions.  The number of elements in that array should be \a\r
-    n_subs.  A message as specified by the remaining parameters will\r
-    be dispatched to all of the subscription points in the array.  The\r
-    function will not return until all of the calls have succeeded.\r
-\r
-    \retval KHM_ERROR_SUCCESS The call succeeded, and no subscribers reported errors\r
-    \retval KHM_ERROR_PARTIAL The call succeeded, but at least one subscriber reported errors\r
-*/\r
-KHMEXP khm_int32 KHMAPI kmq_send_subs_msg(\r
-    khm_handle *subs, \r
-    khm_int32 n_subs,\r
-    khm_int32 type, \r
-    khm_int32 subtype, \r
-    khm_ui_4 uparam, \r
-    void * vparam);\r
-\r
-/*! \brief Dispatch a message for the current thread.\r
-\r
-    This function opens the message list for the current thread and\r
-    dispatches the first message instance that is found.  Note that if\r
-    multiple callbacks subscribe to the same message type in the same\r
-    thread, then when a message of that type is received, multiple\r
-    message instances are added to the message queue corresponding to\r
-    each subscription.\r
-\r
-    If no message instances are waiting in the queue, kmq_dispatch()\r
-    waits for the \a timeout period for a message.\r
-\r
-    \param[in] timeout The timeout period in milliseconds.  Specify INFINITE for\r
-        kmq_dispatch() to wait indefinitely.\r
-\r
-    \retval KHM_ERROR_SUCCESS A message instance was dispatched\r
-    \retval KHM_ERROR_TIMEOUT The timeout period elapsed\r
-    \retval KHM_ERROR_EXIT The message found on the queue was <KMSG_SYSTEM,KMSG_SYSTEM_EXIT>\r
-*/\r
-KHMEXP khm_int32 KHMAPI kmq_dispatch(kmq_timer timeout);\r
-\r
-/*! \brief Send a message\r
-\r
-    The specified message will be posted to all the subscribers of the\r
-    message type.  Then the function will wait for all the subscribers\r
-    to finish processing the message before returning.\r
-    \r
-    \param[in] type The type of the message\r
-    \param[in] subtype The subtype\r
-    \param[in] uparam The khm_ui_4 parameter for the message\r
-    \param[in] blob The parameter blob for the message\r
-\r
-    \note The internal timeout for this function is INFINITE.  If you\r
-        it is desirable to use a different timeout, use\r
-        kmq_post_message_ex() and kmq_wait() functions.\r
-\r
-    \retval KHM_ERROR_SUCCESS The call succeeded and no subscribers returned errors\r
-    \retval KHM_ERROR_PARTIAL The call succeeded but at least one subscriber returned an error\r
-*/\r
-KHMEXP khm_int32 KHMAPI kmq_send_message(\r
-    khm_int32 type, \r
-    khm_int32 subtype, \r
-    khm_ui_4 uparam, \r
-    void * blob);\r
-\r
-/*! \brief Post a message\r
-\r
-    The specified message will be posted to all the subscribers of the\r
-    message type.  The function returns immediately.\r
-    \r
-    If you want to be able to wait for all the subscribers to finish\r
-    processing the message, you should use kmq_post_message_ex()\r
-    instead.\r
-\r
-    \param[in] type The type of the message\r
-    \param[in] subtype The subtype\r
-    \param[in] uparam The khm_ui_4 parameter for the message\r
-    \param[in] blob The parameter blob for the message\r
-*/\r
-KHMEXP khm_int32 KHMAPI kmq_post_message(\r
-    khm_int32 type, \r
-    khm_int32 subtype, \r
-    khm_ui_4 uparam, \r
-    void * blob);\r
-\r
-/*! \brief Post a message and acquire a handle to the call.\r
-\r
-    The specified message is posted to all the subscribers.  In\r
-    addition, a handle is obtained for the call which can be used in\r
-    subsequent call to kmq_free_call() or kmq_wait().\r
-\r
-    Call kmq_free_call() to free the handle.\r
-\r
-    \param[in] type The type of the message\r
-    \param[in] subtype The subtype\r
-    \param[in] uparam The khm_ui_4 parameter for the message\r
-    \param[in] blob The parameter blob for the message\r
-    \param[out] call Receives the call handle.  Set to NULL if the call handle is not required.\r
-\r
-    \see kmq_free_call()\r
-*/\r
-KHMEXP khm_int32 KHMAPI kmq_post_message_ex(\r
-    khm_int32 type, \r
-    khm_int32 subtype, \r
-    khm_ui_4 uparam, \r
-    void * blob, \r
-    kmq_call * call);\r
-\r
-/*! \brief Free a handle to a call obtained through kmq_post_message_ex()\r
-\r
-    All call handles obtained through kmq_post_message_ex() must be\r
-    freed via a call to kmq_free_call().\r
-*/\r
-KHMEXP khm_int32 KHMAPI kmq_free_call(kmq_call call);\r
-\r
-/*! \brief Sends a <KMSG_SYSTEM,KMSG_SYSTEM_EXIT> message to the specified thread.\r
-\r
-    The message itself will not be received by any callback function,\r
-    however, any kmq_dispatch() function that is currently active of\r
-    becomes active will exit with a KHM_ERROR_EXIT code.\r
-    kmq_send_thread_quit_message() will wait for this to happen before\r
-    returning.\r
-    */\r
-KHMEXP khm_int32 KHMAPI kmq_send_thread_quit_message(\r
-    kmq_thread_id thread, \r
-    khm_ui_4 uparam);\r
-\r
-/*! \brief Post a <KMSG_SYSTEM,KMSG_SYSTEM_EXIT> message to the specified thread.\r
-\r
-    The message itself will not be received by any callback function,\r
-    however, any kmq_dispatch() function that is currently active of\r
-    becomes active will exit with a KHM_ERROR_EXIT code.\r
-    kmq_post_thread_quit_message() will return immediately.\r
-    */\r
-KHMEXP khm_int32 KHMAPI kmq_post_thread_quit_message(\r
-    kmq_thread_id thread, \r
-    khm_ui_4 uparam, \r
-    kmq_call * call);\r
-\r
-KHMEXP khm_int32 KHMAPI kmq_get_next_response(kmq_call call, void ** resp);\r
-\r
-/*! \brief Check if a specific call has completed\r
-\r
-    \return TRUE if the call has completed. FALSE otherwise.\r
-*/\r
-KHMEXP khm_boolean KHMAPI kmq_has_completed(kmq_call call);\r
-\r
-/*! \brief Wait for a call to complete.\r
-\r
-    Waits for the specified call to complete.  If the call dispatched\r
-    to multiple recipients, the function waits for all dispatches to\r
-    complete.\r
-\r
-    If the call has already completed, then the function returns\r
-    immediately.\r
-\r
-    If more than one thread is waiting for a single message to\r
-    complete, then only one of them will be released when the message\r
-    compeltes.  Each subsequent thread will be released as each\r
-    released thread calls kmq_free_call().\r
-\r
-    \param[in] call A handle to a call.\r
-    \param[in] timeout Specifies, in milliseconds, the amount of time\r
-        to wait for the call to complete. Specify INFINITE to wait\r
-        indefinitely.\r
-\r
-    \retval KHM_ERROR_SUCCESS The call completed\r
-    \retval KHM_ERROR_TIMEOUT The timeout period expired\r
-    \retval KHM_ERROR_INVALID_PARAM One of the parameters were invalid.\r
-*/\r
-KHMEXP khm_int32 KHMAPI kmq_wait(kmq_call call, kmq_timer timeout);\r
-\r
-/*! \brief Abort a call\r
-\r
-    Abort a pending call.  The call handle should have been obtained\r
-    using a prior call to kmq_post_message_ex().\r
-\r
-    Note that this function may not abort the call immediately.  It\r
-    merely marks the message as being in an aborted state.  It is upto\r
-    the individual handlers of the message to check if the message has\r
-    been aborted and act accordingly.\r
-\r
-    The handlers are expected to call kmq_is_call_aborted()\r
-    periodicially during the processing of specially lengthy\r
-    operations during the course of handling a message. That function\r
-    will return \a TRUE if the last dispatched message is now in an\r
-    aborted state.  In which case, the handler is expected to abort\r
-    handling the message and return control to the dispatcher.\r
- */\r
-KHMEXP khm_int32 KHMAPI kmq_abort_call(kmq_call call);\r
-\r
-/*! \brief Check if the last dispatched message was aborted\r
-\r
-    The sender of a message may abort it using a call to\r
-    kmq_abort_call().  This function checks if the last dispatched\r
-    message was aborted.\r
-\r
-    A handler of a message is expected to call this function\r
-    periodically if handling the message is going to take a specially\r
-    long time (e.g. more than 5 or 10 seconds).  If the message is\r
-    found to be aborted, the handler is expected to abort handling the\r
-    message, perform any necessary cleanup and return control to the\r
-    dispatcher.\r
-\r
-    Doing this allows operations like new credentials acquisition to\r
-    be cleanly aborted by the user if she so wishes.  Otherwise,\r
-    Network Identity Manager has to wait for the message to complete\r
-    processing since it has no means of cleanly terminating an\r
-    executing plug-in thread.\r
-*/\r
-KHMEXP khm_boolean KHMAPI kmq_is_call_aborted(void);\r
-\r
-/*! \brief Sets the completion handler for a specified message type.\r
-\r
-    \note Only one completion handler can exist for one message type.\r
-        Calling this function overwrites the previous completion\r
-        handler.\r
-*/\r
-KHMEXP khm_int32 KHMAPI kmq_set_completion_handler(\r
-    khm_int32 type, \r
-    kmq_msg_completion_handler hander);\r
-\r
-/*@}*/\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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_boolean aborted;        /*!< TRUE if the message has been
+                                  aborted. */
+
+    khm_int32 refcount;         /*!< Internal use */
+
+    LDCL(struct tag_kmq_message); /*!< Internal use */
+} kmq_message;
+
+/*! \brief A handle to a call
+ */
+typedef kmq_message *kmq_call;
+
+/* 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 *);
+
+#ifdef NOEXPORT
+
+KHMEXP khm_int32 KHMAPI kmq_init(void);
+
+KHMEXP khm_int32 KHMAPI kmq_exit(void);
+
+#endif
+
+/*! \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_PARAM 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 Create an ad-hoc subscription for a window
+
+    An ad-hoc subscription describes a window that will be dispatched
+    messages 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_hwnd_subscription(HWND hw,
+                                                     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_PARAM One of the parameters were invalid.
+*/
+KHMEXP khm_int32 KHMAPI kmq_wait(kmq_call call, kmq_timer timeout);
+
+/*! \brief Abort a call
+
+    Abort a pending call.  The call handle should have been obtained
+    using a prior call to kmq_post_message_ex().
+
+    Note that this function may not abort the call immediately.  It
+    merely marks the message as being in an aborted state.  It is upto
+    the individual handlers of the message to check if the message has
+    been aborted and act accordingly.
+
+    The handlers are expected to call kmq_is_call_aborted()
+    periodicially during the processing of specially lengthy
+    operations during the course of handling a message. That function
+    will return \a TRUE if the last dispatched message is now in an
+    aborted state.  In which case, the handler is expected to abort
+    handling the message and return control to the dispatcher.
+ */
+KHMEXP khm_int32 KHMAPI kmq_abort_call(kmq_call call);
+
+/*! \brief Check if the last dispatched message was aborted
+
+    The sender of a message may abort it using a call to
+    kmq_abort_call().  This function checks if the last dispatched
+    message was aborted.
+
+    A handler of a message is expected to call this function
+    periodically if handling the message is going to take a specially
+    long time (e.g. more than 5 or 10 seconds).  If the message is
+    found to be aborted, the handler is expected to abort handling the
+    message, perform any necessary cleanup and return control to the
+    dispatcher.
+
+    Doing this allows operations like new credentials acquisition to
+    be cleanly aborted by the user if she so wishes.  Otherwise,
+    Network Identity Manager has to wait for the message to complete
+    processing since it has no means of cleanly terminating an
+    executing plug-in thread.
+*/
+KHMEXP khm_boolean KHMAPI kmq_is_call_aborted(void);
+
+/*! \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
index 1d6196b307258263e7ce035a2ccf72e9e18c7cbb..bd97f1bb6da59abb540f1c1f60b88acc26f5b7fd 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KMQINTERNAL_H\r
-#define __KHIMAIRA_KMQINTERNAL_H\r
-\r
-#include<windows.h>\r
-#include<kmq.h>\r
-#include<khlist.h>\r
-#include<kherror.h>\r
-#include<khmsgtypes.h>\r
-#include<kconfig.h>\r
-\r
-#define NOEXPORT\r
-\r
-#include<utils.h>\r
-#include<strsafe.h>\r
-\r
-\r
-\r
-\r
-/*! \brief Message reference */\r
-typedef struct tag_kmq_message_ref {\r
-    kmq_message * msg;          /*!< Message that we are referring\r
-                                  to */\r
-    kmq_callback_t recipient;   /*!< The recipient of the message */\r
-\r
-    LDCL(struct tag_kmq_message_ref);\r
-} kmq_message_ref;\r
-\r
-\r
-\r
-\r
-/*! \brief Message queue\r
-\r
-    Each thread gets its own message queue.  When a message is\r
-    broadcast to which there is a subscriber in a particular thread, a\r
-    reference to the message is placed in the message queue of the\r
-    thread.  The dispatch procedure then dispatches the message as\r
-    described in the message reference.\r
-*/\r
-typedef struct tag_kmq_queue {\r
-    kmq_thread_id thread;       /*!< The thread id  */\r
-\r
-    CRITICAL_SECTION cs;\r
-    HANDLE wait_o;\r
-\r
-    khm_int32 load;             /*!< Number of messages waiting to be\r
-                                  processed on this message queue.  */\r
-    kmq_timer last_post;        /*!< Time the last message was\r
-                                  received */\r
-\r
-    khm_int32 flags;            /*!< Flags.  Currently, it's just KMQ_QUEUE_FLAG_DELETED */\r
-\r
-    /*Q*/\r
-    QDCL(kmq_message_ref);      /*!< Queue of message references  */\r
-\r
-    /*Lnode*/\r
-    LDCL(struct tag_kmq_queue);\r
-} kmq_queue;\r
-\r
-#define KMQ_QUEUE_FLAG_DELETED   0x00000008\r
-#define KMQ_QUEUE_FLAG_DETACHING 0x00000010\r
-\r
-/*! \brief Message subscription\r
-\r
-    A subscription binds a recipient with a message type.  These are\r
-    specific to a thread. I.e. a subscription that was made in one\r
-    thread will not receive messages in the context of another thread.\r
-*/\r
-typedef struct tag_kmq_msg_subscription {\r
-    khm_int32 magic;            /*!< Magic number.  Should always be\r
-                                  ::KMQ_MSG_SUB_MAGIC */\r
-    khm_int32 type;             /*!< Type of message */\r
-    khm_int32 rcpt_type;        /*!< Type of recipient.  One of\r
-                                  ::KMQ_RCPTTYPE_CB or\r
-                                  ::KMQ_RCPTTYPE_HWND  */\r
-    union {\r
-        kmq_callback_t cb;      /*!< Callback if the subscription is\r
-                                  of callback type */\r
-        HWND hwnd;              /*!< Window handle if the subscription\r
-                                  is a windows message type */\r
-    } recipient;\r
-\r
-    kmq_queue * queue;          /*!< Associated queue */\r
-\r
-    /*lnode*/\r
-    LDCL(struct tag_kmq_msg_subscription);\r
-} kmq_msg_subscription;\r
-\r
-#define KMQ_MSG_SUB_MAGIC 0x3821b58e\r
-\r
-/*! \brief Callback recipient type\r
-\r
-    The recipient is a callback function */\r
-#define KMQ_RCPTTYPE_CB     1\r
-\r
-/*! \brief Windows recipient type\r
-\r
-    The recipient is a window */\r
-#define KMQ_RCPTTYPE_HWND   2\r
-\r
-\r
-\r
-\r
-/*! \brief A message type\r
- */\r
-typedef struct tag_kmq_msg_type {\r
-    khm_int32 id;               /*!< Identifier for the message\r
-                                  type. */\r
-    kmq_msg_subscription * subs; /*!< The list of subscriptions */\r
-    kmq_msg_completion_handler completion_handler; /*!< Completion\r
-                                  handler for the message type */\r
-\r
-    wchar_t * name;             /*!< Name of the message type for\r
-                                  named types.  Message type names are\r
-                                  language independant. */\r
-\r
-    /*Lnode*/\r
-    LDCL(struct tag_kmq_msg_type);\r
-} kmq_msg_type;\r
-\r
-/*! \brief The maximum number of message types\r
- */\r
-#define KMQ_MSG_TYPE_MAX 255\r
-\r
-/*! \brief Maximum number of characters in a message type name\r
-\r
-    The count includes the terminating NULL\r
- */\r
-#define KMQ_MAXCCH_TYPE_NAME 256\r
-\r
-/*! \brief Maximum number of bytes in a message type name\r
-\r
-    Type count includes the terminating NULL\r
- */\r
-#define KMQ_MAXCB_TYPE_NAME (KMQ_MAXCCH_TYPE_NAME * sizeof(wchar_t))\r
-\r
-\r
-\r
-\r
-#define KMQ_CONF_SPACE_NAME L"KMQ"\r
-#define KMQ_CONF_QUEUE_DEAD_TIMEOUT_NAME L"QueueDeadTimeout"\r
-#define KMQ_CONF_CALL_DEAD_TIMEOUT_NAME L"CallDeadTimeout"\r
-\r
-extern CRITICAL_SECTION cs_kmq_global;\r
-extern kmq_timer kmq_queue_dead_timeout;\r
-extern kmq_timer kmq_call_dead_timeout;\r
-\r
-extern kmq_queue * queues;\r
-\r
-/* message type */\r
-extern CRITICAL_SECTION cs_kmq_types;\r
-extern kmq_msg_type *msg_types[KMQ_MSG_TYPE_MAX+1];\r
-\r
-void kmqint_init_msg_types(void);\r
-void kmqint_exit_msg_types(void);\r
-void kmqint_free_msg_type(int t);\r
-void kmqint_msg_type_create(int t);\r
-void kmqint_msg_type_add_sub(int t, kmq_msg_subscription *s);\r
-void kmqint_msg_type_del_sub(kmq_msg_subscription *s);\r
-kmq_msg_subscription * kmqint_msg_type_del_sub_hwnd(khm_int32 t, HWND hwnd);\r
-kmq_msg_subscription * kmqint_msg_type_del_sub_cb(khm_int32 t, kmq_callback_t cb);\r
-khm_int32 kmqint_msg_publish(kmq_message * m, khm_boolean try_send);\r
-khm_int32 kmqint_msg_type_set_handler(khm_int32 type, kmq_msg_completion_handler handler);\r
-int kmqint_notify_msg_completion(kmq_message * m);\r
-\r
-/* consumer */\r
-extern DWORD kmq_tls_queue;\r
-\r
-void kmqint_post_queue(kmq_queue * q, kmq_message *m);\r
-void kmqint_post(kmq_msg_subscription * s, kmq_message * m, khm_boolean try_send);\r
-kmq_queue * kmqint_get_thread_queue(void);\r
-void kmqint_get_queue_message_ref(kmq_queue * q, kmq_message_ref ** r);\r
-void kmqint_put_message_ref(kmq_message_ref * r);\r
-\r
-/* publisher */\r
-extern CRITICAL_SECTION cs_kmq_msg;\r
-extern CRITICAL_SECTION cs_kmq_msg_ref;\r
-\r
-kmq_message * kmqint_get_message(void);\r
-void kmqint_put_message(kmq_message *m);\r
-\r
-void kmqint_init(void);\r
-void kmqint_exit(void);\r
-void kmqint_attach_this_thread(void);\r
-void kmqint_detach_this_thread(void);\r
-\r
-khm_int32 kmqint_post_message_ex(\r
-    khm_int32 type, \r
-    khm_int32 subtype, \r
-    khm_ui_4 uparam, \r
-    void * blob, \r
-    kmq_call * call,\r
-    khm_boolean try_send);\r
-\r
-int kmqint_call_completion_handler(kmq_msg_completion_handler h,\r
-                                   kmq_message * m);\r
-\r
-/* global */\r
-extern kconf_schema schema_kmqconfig[];\r
-\r
-/* Lock hiearchy :\r
-\r
-    cs_kmq_types\r
-    cs_kmq_msg\r
-    cs_kmq_msg_ref\r
-    cs_compl\r
-    cs_kmq_global\r
-    kmq_queue::cs\r
-\r
-    If you have a level 'x' lock, you can obtain a level 'x+n' lock.\r
-    You can't obtain a 'x-n' lock if you already have a level 'x' lock.\r
-    If you don't have any locks, you can obtain any lock.\r
- */\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+
+#define NOEXPORT
+
+#include<utils.h>
+#include<strsafe.h>
+
+
+
+
+/*! \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 */
+
+    khm_int32 flags;            /*!< Flags.  Currently, it's just KMQ_QUEUE_FLAG_DELETED */
+
+    /*Q*/
+    QDCL(kmq_message_ref);      /*!< Queue of message references  */
+
+    /*Lnode*/
+    LDCL(struct tag_kmq_queue);
+} kmq_queue;
+
+#define KMQ_QUEUE_FLAG_DELETED   0x00000008
+#define KMQ_QUEUE_FLAG_DETACHING 0x00000010
+
+/*! \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 magic;            /*!< Magic number.  Should always be
+                                  ::KMQ_MSG_SUB_MAGIC */
+    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;
+
+#define KMQ_MSG_SUB_MAGIC 0x3821b58e
+
+/*! \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
+
+
+
+
+/*! \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))
+
+
+
+
+#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);
+void kmqint_put_message_ref(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
index 3e8176f1b3b3dad8605e9f64763f7d82b2b4b626..fecfc17fdc24f355334b410fd1513cb8355d6312 100644 (file)
@@ -1,47 +1,47 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kmqinternal.h>\r
-\r
-void\r
-kmq_process_attach(void) {\r
-    kmqint_init();\r
-}\r
-\r
-void\r
-kmq_process_detach(void) {\r
-    kmqint_exit();\r
-}\r
-\r
-void\r
-kmq_thread_attach(void) {\r
-    kmqint_attach_this_thread();\r
-}\r
-\r
-void\r
-kmq_thread_detach(void) {\r
-    kmqint_detach_this_thread();\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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();
+}
index 1b0868d836c7371436b5bf1878785cc3cb7b5c70..b7ea21fcabd1cc704d7b2c65a54f7702eed51a3a 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kmqinternal.h>\r
-\r
-CRITICAL_SECTION cs_kmq_types;\r
-\r
-kmq_msg_type *msg_types[KMQ_MSG_TYPE_MAX + 1];\r
-kmq_msg_type *all_msg_types = NULL;\r
-\r
-/*! \internal\r
-    \brief Initializes the message type data structures\r
-    \note called with cs_mkq_global held */\r
-void kmqint_init_msg_types(void) {\r
-    ZeroMemory(msg_types, sizeof(kmq_msg_type *) * (KMQ_MSG_TYPE_MAX + 1));\r
-    InitializeCriticalSection(&cs_kmq_types);\r
-}\r
-\r
-/*! \internal\r
-    \brief Frees up the message type data structures\r
-    \note called with cs_mkq_global held */\r
-void kmqint_exit_msg_types(void) {\r
-    int i;\r
-\r
-    EnterCriticalSection(&cs_kmq_types);\r
-    for(i=0;i<KMQ_MSG_TYPE_MAX;i++) {\r
-        if(msg_types[i])\r
-            kmqint_free_msg_type(i);\r
-    }\r
-    LeaveCriticalSection(&cs_kmq_types);\r
-    DeleteCriticalSection(&cs_kmq_types);\r
-}\r
-\r
-/*! \internal\r
-    \brief Notifies that the message has completed\r
-\r
-    \return Zero if the completion handling is done.  Nonzero if the\r
-    handling is queued.\r
-    */\r
-int kmqint_notify_msg_completion(kmq_message * m) {\r
-    kmq_msg_type * mt;\r
-    kmq_msg_completion_handler h;\r
-\r
-    /* doing it this way to elude race conditions without\r
-       obtaining a lock */\r
-\r
-    mt = msg_types[m->type];\r
-    if(mt == NULL)\r
-        return 0;\r
-    h = mt->completion_handler;\r
-\r
-    /* handler is set to NULL before freeing type */\r
-    if(h == NULL || msg_types[m->type] == NULL)\r
-        return 0;\r
-\r
-    return kmqint_call_completion_handler(h,m);\r
-}\r
-\r
-/* called with cs_mkq_global && cs_kmq_types held */\r
-void kmqint_free_msg_type(int t) {\r
-    kmq_msg_type * pt;\r
-    kmq_msg_subscription * s;\r
-\r
-    pt = msg_types[t];\r
-\r
-    msg_types[t] = NULL;\r
-\r
-    if (pt == NULL)\r
-        return;\r
-\r
-    /* all the subscriptions attached to a message type are owned by\r
-       the message type */\r
-    LPOP(&pt->subs, &s);\r
-    while(s) {\r
-        s->magic = 0;\r
-\r
-        PFREE(s);\r
-\r
-        LPOP(&pt->subs, &s);\r
-    }\r
-\r
-    pt->completion_handler = NULL;\r
-\r
-    PFREE(pt);\r
-}\r
-\r
-/*! \internal\r
-    \brief Create a message type\r
-    \note Obtains ::cs_kmq_types\r
-    */\r
-void kmqint_msg_type_create(int t) {\r
-    if(t < 0 || t > KMQ_MSG_TYPE_MAX)\r
-        return;\r
-\r
-    EnterCriticalSection(&cs_kmq_types);\r
-    if(!msg_types[t]) {\r
-        kmq_msg_type * mt;\r
-        mt = PMALLOC(sizeof(kmq_msg_type));\r
-        ZeroMemory(mt, sizeof(kmq_msg_type));\r
-        mt->id = t;\r
-        LINIT(mt);\r
-        mt->subs = NULL;\r
-        msg_types[t] = mt;\r
-\r
-        LPUSH(&all_msg_types, mt);\r
-    }\r
-    LeaveCriticalSection(&cs_kmq_types);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kmq_register_type(wchar_t * name, \r
-                                          khm_int32 * new_id)\r
-{\r
-    int i;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    BOOL registered = FALSE;\r
-    int first_free = 0;\r
-    size_t sz;\r
-\r
-    if(FAILED(StringCbLength(name, KMQ_MAXCB_TYPE_NAME, &sz)) ||\r
-       sz == 0)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    sz += sizeof(wchar_t);\r
-\r
-    EnterCriticalSection(&cs_kmq_types);\r
-    for(i=KMSGBASE_USER; i <= KMQ_MSG_TYPE_MAX; i++) {\r
-        if(msg_types[i] == NULL) {\r
-            if(first_free == 0)\r
-                first_free = i;\r
-            /* continue searching since we might find that this type\r
-               is already registered. */\r
-        } else {\r
-            if(msg_types[i]->name != NULL && \r
-               !wcscmp(msg_types[i]->name, name)) {\r
-\r
-                registered = TRUE;\r
-                if (new_id)\r
-                    *new_id = i;\r
-                break;\r
-            }\r
-        }\r
-    }\r
-\r
-    if(registered) {\r
-        rv = KHM_ERROR_EXISTS;\r
-    } else if(first_free == 0) {\r
-        rv = KHM_ERROR_NO_RESOURCES;\r
-    } else {\r
-        kmqint_msg_type_create(first_free);\r
-        msg_types[first_free]->name = PMALLOC(sz);\r
-        StringCbCopy(msg_types[first_free]->name, sz, name);\r
-\r
-        if(new_id != NULL)\r
-            *new_id = first_free;\r
-    }\r
-    LeaveCriticalSection(&cs_kmq_types);\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kmq_find_type(wchar_t * name, khm_int32 * id)\r
-{\r
-    int i;\r
-\r
-    EnterCriticalSection(&cs_kmq_types);\r
-    for(i=KMSGBASE_USER; i <= KMQ_MSG_TYPE_MAX; i++) {\r
-        if(msg_types[i] != NULL && msg_types[i]->name != NULL) {\r
-            if(!wcscmp(msg_types[i]->name, name))\r
-                break;\r
-        }\r
-    }\r
-    LeaveCriticalSection(&cs_kmq_types);\r
-\r
-    if(i <= KMQ_MSG_TYPE_MAX) {\r
-        if(id != NULL)\r
-            *id = i;\r
-        return KHM_ERROR_SUCCESS;\r
-    }\r
-\r
-    return KHM_ERROR_NOT_FOUND;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI kmq_unregister_type(khm_int32 id)\r
-{\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if(id < KMSGBASE_USER || id > KMQ_MSG_TYPE_MAX)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_kmq_types);\r
-    if(msg_types[id] != NULL) {\r
-        EnterCriticalSection(&cs_kmq_global);\r
-        kmqint_free_msg_type(id);\r
-        LeaveCriticalSection(&cs_kmq_global);\r
-    } else {\r
-        rv = KHM_ERROR_NOT_FOUND;\r
-    }\r
-    LeaveCriticalSection(&cs_kmq_types);\r
-\r
-    return rv;\r
-}\r
-\r
-/*! \internal\r
-    \brief Adds a subscription to a message type\r
-    \note Obtains ::cs_kmq_types\r
-    */\r
-void kmqint_msg_type_add_sub(int t, kmq_msg_subscription *s) {\r
-    kmq_msg_subscription * ts;\r
-\r
-    if(t < 0 || t > KMQ_MSG_TYPE_MAX)\r
-        return;\r
-\r
-    if(!msg_types[t])\r
-        kmqint_msg_type_create(t);\r
-\r
-    EnterCriticalSection(&cs_kmq_types);\r
-    s->type = t;\r
-    /* check if we already have this subscription */\r
-    ts = msg_types[t]->subs;\r
-    while(ts) {\r
-        if((ts->rcpt_type == s->rcpt_type) &&\r
-            (((ts->rcpt_type == KMQ_RCPTTYPE_CB) && (ts->recipient.cb == s->recipient.cb)) ||\r
-             ((ts->rcpt_type == KMQ_RCPTTYPE_HWND) && (ts->recipient.hwnd == s->recipient.hwnd))))\r
-            break;\r
-        ts = LNEXT(ts);\r
-    }\r
-    /* add it if we didn't find it */\r
-    if(!ts) {\r
-        LPUSH(&msg_types[t]->subs, s);\r
-    }\r
-    LeaveCriticalSection(&cs_kmq_types);\r
-}\r
-\r
-/*! \internal\r
-    \brief Delete a subscription\r
-    \note Obtains ::cs_kmq_types\r
-    */\r
-void kmqint_msg_type_del_sub(kmq_msg_subscription *s) {\r
-    int t = s->type;\r
-\r
-    EnterCriticalSection(&cs_kmq_types);\r
-    if(msg_types[t]) {\r
-        LDELETE(&msg_types[t]->subs,s);\r
-    }\r
-    LeaveCriticalSection(&cs_kmq_types);\r
-}\r
-\r
-\r
-/*! \internal\r
-    \brief Deletes a window subscription from a message type\r
-    \note Obtains ::cs_kmq_types\r
-*/\r
-kmq_msg_subscription * kmqint_msg_type_del_sub_hwnd(khm_int32 t, HWND hwnd) {\r
-    kmq_msg_subscription *s = NULL;\r
-\r
-    if(t < 0 || t > KMQ_MSG_TYPE_MAX)\r
-        return NULL;\r
-\r
-    EnterCriticalSection(&cs_kmq_types);\r
-    if(msg_types[t]) {\r
-        s = msg_types[t]->subs;\r
-        while(s) {\r
-            kmq_msg_subscription * n = LNEXT(s);\r
-            if(s->rcpt_type == KMQ_RCPTTYPE_HWND && s->recipient.hwnd == hwnd) {\r
-                /*TODO: do more here? */\r
-                LDELETE(&msg_types[t]->subs, s);\r
-                break;\r
-            }\r
-            s = n;\r
-        }\r
-    }\r
-    LeaveCriticalSection(&cs_kmq_types);\r
-\r
-    return s;\r
-}\r
-\r
-/*! \internal\r
-    \brief Delete a callback from a message type\r
-    \note Obtains ::cs_kmq_types, ::cs_kmq_global\r
-    */\r
-kmq_msg_subscription * kmqint_msg_type_del_sub_cb(khm_int32 t, kmq_callback_t cb) {\r
-    kmq_msg_subscription *s;\r
-    kmq_queue *q;\r
-\r
-    if(t < 0 || t > KMQ_MSG_TYPE_MAX)\r
-        return NULL;\r
-\r
-    if(!msg_types[t])\r
-        return NULL;\r
-\r
-    q = kmqint_get_thread_queue();\r
-\r
-    EnterCriticalSection(&cs_kmq_types);\r
-    s = msg_types[t]->subs;\r
-    while(s) {\r
-        kmq_msg_subscription * n = LNEXT(s);\r
-        if(s->rcpt_type == KMQ_RCPTTYPE_CB && \r
-           s->recipient.cb == cb && \r
-           s->queue == q) {\r
-            /*TODO: do more here? */\r
-            LDELETE(&msg_types[t]->subs, s);\r
-            break;\r
-        }\r
-        s = n;\r
-    }\r
-    LeaveCriticalSection(&cs_kmq_types);\r
-\r
-    return s;\r
-}\r
-\r
-/*! \internal\r
-    \brief Publish a message\r
-    \note Obtains ::cs_kmq_types, ::cs_kmq_msg_ref, kmq_queue::cs, ::cs_kmq_msg\r
-    */\r
-khm_int32 kmqint_msg_publish(kmq_message * m, khm_boolean try_send) {\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if(msg_types[m->type]) {\r
-        kmq_msg_type *t;\r
-        kmq_msg_subscription * s;\r
-\r
-        EnterCriticalSection(&cs_kmq_types);\r
-        EnterCriticalSection(&cs_kmq_msg);\r
-        t = msg_types[m->type];\r
-        s = t->subs;\r
-        while(s) {\r
-            kmqint_post(s, m, try_send);\r
-            s = LNEXT(s);\r
-        }\r
-\r
-        if(m->nCompleted + m->nFailed == m->nSent) {\r
-            kmqint_put_message(m);\r
-        }\r
-\r
-        LeaveCriticalSection(&cs_kmq_msg);\r
-        LeaveCriticalSection(&cs_kmq_types);\r
-\r
-    } else {\r
-        EnterCriticalSection(&cs_kmq_msg);\r
-        kmqint_put_message(m);\r
-        LeaveCriticalSection(&cs_kmq_msg);\r
-    }\r
-    return rv;\r
-}\r
-\r
-/*! \internal\r
-    \brief Sets the completion handler for a message type\r
-    \note Obtains ::cs_kmq_types\r
-    */\r
-khm_int32 kmqint_msg_type_set_handler(khm_int32 type, kmq_msg_completion_handler handler) {\r
-\r
-    if (type == KMSG_SYSTEM)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(!msg_types[type])\r
-        kmqint_msg_type_create(type);\r
-\r
-    if(!msg_types[type])\r
-        return KHM_ERROR_NO_RESOURCES;\r
-\r
-    EnterCriticalSection(&cs_kmq_types);\r
-    msg_types[type]->completion_handler = handler;\r
-    LeaveCriticalSection(&cs_kmq_types);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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) {
+    kmq_msg_type * pt;
+    kmq_msg_subscription * s;
+
+    pt = msg_types[t];
+
+    msg_types[t] = NULL;
+
+    if (pt == NULL)
+        return;
+
+    /* all the subscriptions attached to a message type are owned by
+       the message type */
+    LPOP(&pt->subs, &s);
+    while(s) {
+        s->magic = 0;
+
+        PFREE(s);
+
+        LPOP(&pt->subs, &s);
+    }
+
+    pt->completion_handler = NULL;
+
+    PFREE(pt);
+}
+
+/*! \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 = PMALLOC(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_PARAM;
+    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;
+            /* continue searching since we might find that this type
+               is already registered. */
+        } else {
+            if(msg_types[i]->name != NULL && 
+               !wcscmp(msg_types[i]->name, name)) {
+
+                registered = TRUE;
+                if (new_id)
+                    *new_id = i;
+                break;
+            }
+        }
+    }
+
+    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 = PMALLOC(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_PARAM;
+
+    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 = NULL;
+
+    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_PARAM;
+
+    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;
+}
index 66360fb50b0c1f04540e2b3f57d0385162d7a9a9..2323837e2d04213c73d94a09291e4e897f4d34ce 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<kmqinternal.h>\r
-\r
-CRITICAL_SECTION cs_kmq_msg;\r
-kmq_message * msg_free = NULL;\r
-kmq_message * msg_active = NULL;\r
-\r
-#ifdef DEBUG\r
-\r
-#include<stdio.h>\r
-\r
-void\r
-kmqint_dump_publisher(FILE * f) {\r
-\r
-    int n_free = 0;\r
-    int n_active = 0;\r
-    kmq_message * m;\r
-\r
-    EnterCriticalSection(&cs_kmq_msg);\r
-\r
-    fprintf(f, "qp0\t*** Free Messages ***\n");\r
-    fprintf(f, "qp1\tAddress\n");\r
-\r
-    m = msg_free;\r
-    while(m) {\r
-        n_free++;\r
-\r
-        fprintf(f, "qp2\t0x%p\n", m);\r
-\r
-        m = LNEXT(m);\r
-    }\r
-\r
-    fprintf(f, "qp3\tTotal free messages : %d\n", n_free);\r
-\r
-    fprintf(f, "qp4\t*** Active Messages ***\n");\r
-    fprintf(f, "qp5\tAddress\tType\tSubtype\tuParam\tvParam\tnSent\tnCompleted\tnFailed\twait_o\trefcount\n");\r
-\r
-    m = msg_active;\r
-    while(m) {\r
-\r
-        n_active++;\r
-\r
-        fprintf(f, "qp6\t0x%p\t%d\t%d\t0x%x\t0x%p\t%d\t%d\t%d\t0x%p\t%d\n",\r
-                m,\r
-                (int) m->type,\r
-                (int) m->subtype,\r
-                (unsigned int) m->uparam,\r
-                m->vparam,\r
-                (int) m->nSent,\r
-                (int) m->nCompleted,\r
-                (int) m->nFailed,\r
-                (void *) m->wait_o,\r
-                (int) m->refcount);\r
-\r
-        m = LNEXT(m);\r
-    }\r
-\r
-    fprintf(f, "qp7\tTotal number of active messages = %d\n", n_active);\r
-\r
-    fprintf(f, "qp8\t--- End ---\n");\r
-\r
-    LeaveCriticalSection(&cs_kmq_msg);\r
-\r
-}\r
-\r
-#endif\r
-\r
-/*! \internal\r
-    \brief Get a message object\r
-    \note called with ::cs_kmq_msg held */\r
-kmq_message * \r
-kmqint_get_message(void) {\r
-    kmq_message * m;\r
-\r
-    LPOP(&msg_free,&m);\r
-    if(!m) {\r
-        /* allocate one */\r
-        m = PMALLOC(sizeof(kmq_message));\r
-    }\r
-    ZeroMemory((void*)m, sizeof(kmq_message));\r
-\r
-    LPUSH(&msg_active, m);\r
-\r
-    return m;\r
-}\r
-\r
-/*! \internal\r
-    \brief Frees a message object\r
-    \note called with ::cs_kmq_msg held\r
-    */\r
-void \r
-kmqint_put_message(kmq_message *m) {\r
-    int queued;\r
-    /* we can only free a message if the refcount is zero.\r
-       Otherwise we have to wait until the call is freed. */\r
-    if(m->refcount == 0) {\r
-        LDELETE(&msg_active, m);\r
-        LeaveCriticalSection(&cs_kmq_msg);\r
-        queued = kmqint_notify_msg_completion(m);\r
-        EnterCriticalSection(&cs_kmq_msg);\r
-        if (!queued) {\r
-            if(m->err_ctx) {\r
-                kherr_release_context(m->err_ctx);\r
-                m->err_ctx = NULL;\r
-            }\r
-            if(m->wait_o) {\r
-                CloseHandle(m->wait_o);\r
-                m->wait_o = NULL;\r
-            }\r
-            LPUSH(&msg_free,m);\r
-        }\r
-    } else if(m->wait_o) {\r
-        SetEvent(m->wait_o);\r
-    }\r
-}\r
-\r
-/*! \internal\r
-    \note Obtains ::cs_kmq_msg, ::cs_kmq_types, ::cs_kmq_msg_ref, kmq_queue::cs\r
-    */\r
-KHMEXP khm_int32 KHMAPI \r
-kmq_send_message(khm_int32 type, khm_int32 subtype, \r
-                 khm_ui_4 uparam, void * blob) {\r
-    kmq_call c;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    rv = kmqint_post_message_ex(type, subtype, uparam, blob, &c, TRUE);\r
-    if(KHM_FAILED(rv))\r
-        return rv;\r
-\r
-    rv = kmq_wait(c, INFINITE);\r
-    if(KHM_SUCCEEDED(rv) && c->nFailed > 0)\r
-        rv = KHM_ERROR_PARTIAL;\r
-\r
-    kmq_free_call(c);\r
-\r
-    return rv;\r
-}\r
-\r
-/*! \internal\r
-    \note Obtains ::cs_kmq_msg, ::cs_kmq_types, ::cs_kmq_msg_ref, kmq_queue::cs\r
-    */\r
-KHMEXP khm_int32 KHMAPI \r
-kmq_post_message(khm_int32 type, khm_int32 subtype, \r
-                 khm_ui_4 uparam, void * blob) {\r
-    return kmqint_post_message_ex(type, subtype, uparam, blob, NULL, FALSE);\r
-}\r
-\r
-/*! \internal\r
-    \brief Frees a call\r
-    \note Obtains ::cs_kmq_msg\r
-    */\r
-KHMEXP khm_int32 KHMAPI \r
-kmq_free_call(kmq_call call) {\r
-    kmq_message * m;\r
-\r
-    m = call;\r
-\r
-    EnterCriticalSection(&cs_kmq_msg);\r
-    m->refcount--;\r
-    if(!m->refcount) {\r
-        kmqint_put_message(m);\r
-    }\r
-    LeaveCriticalSection(&cs_kmq_msg);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/*! \internal\r
-    \note Obtains ::cs_kmq_msg, ::cs_kmq_types, ::cs_kmq_msg_ref, kmq_queue::cs \r
-    */\r
-khm_int32 \r
-kmqint_post_message_ex(khm_int32 type, khm_int32 subtype, khm_ui_4 uparam, \r
-    void * blob, kmq_call * call, khm_boolean try_send) \r
-{\r
-    kmq_message * m;\r
-    kherr_context * ctx;\r
-\r
-    EnterCriticalSection(&cs_kmq_msg);\r
-    m = kmqint_get_message();\r
-    LeaveCriticalSection(&cs_kmq_msg);\r
-\r
-    m->type = type;\r
-    m->subtype = subtype;\r
-    m->uparam = uparam;\r
-    m->vparam = blob;\r
-\r
-    m->timeSent = GetTickCount();\r
-    m->timeExpire = m->timeSent + kmq_call_dead_timeout;\r
-\r
-    ctx = kherr_peek_context();\r
-    if (ctx) {\r
-        if (ctx->flags & KHERR_CF_TRANSITIVE) {\r
-            m->err_ctx = ctx;\r
-            /* leave it held */\r
-        } else {\r
-            kherr_release_context(ctx);\r
-        }\r
-    }\r
-\r
-    if(call) {\r
-        m->wait_o = CreateEvent(NULL,FALSE,FALSE,NULL);\r
-        *call = m;\r
-        m->refcount++;\r
-    } else\r
-        m->wait_o = NULL;\r
-\r
-    kmqint_msg_publish(m, try_send);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kmq_post_message_ex(khm_int32 type, khm_int32 subtype, \r
-                    khm_ui_4 uparam, void * blob, kmq_call * call)\r
-{\r
-    return kmqint_post_message_ex(type, subtype, uparam, blob, call, FALSE);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-kmq_abort_call(kmq_call call)\r
-{\r
-    /* TODO: Implement this */\r
-    return KHM_ERROR_NOT_IMPLEMENTED;\r
-}\r
-\r
-/*! \internal\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kmq_post_sub_msg(khm_handle sub, khm_int32 type, khm_int32 subtype, \r
-                 khm_ui_4 uparam, void * vparam)\r
-{\r
-    return kmq_post_sub_msg_ex(sub, type, subtype, uparam, vparam, NULL);\r
-}\r
-\r
-/*! \internal\r
-*/\r
-khm_int32 \r
-kmqint_post_sub_msg_ex(khm_handle sub, khm_int32 type, khm_int32 subtype, \r
-                       khm_ui_4 uparam, void * vparam, \r
-                       kmq_call * call, khm_boolean try_send)\r
-{\r
-    kmq_message * m;\r
-    kherr_context * ctx;\r
-\r
-    EnterCriticalSection(&cs_kmq_msg);\r
-    m = kmqint_get_message();\r
-    LeaveCriticalSection(&cs_kmq_msg);\r
-\r
-    m->type = type;\r
-    m->subtype = subtype;\r
-    m->uparam = uparam;\r
-    m->vparam = vparam;\r
-\r
-    m->timeSent = GetTickCount();\r
-    m->timeExpire = m->timeSent + kmq_call_dead_timeout;\r
-\r
-    ctx = kherr_peek_context();\r
-    if (ctx) {\r
-        if (ctx->flags & KHERR_CF_TRANSITIVE) {\r
-            m->err_ctx = ctx;\r
-            /* leave it held */\r
-        } else {\r
-            kherr_release_context(ctx);\r
-        }\r
-    }\r
-\r
-    if(call) {\r
-        m->wait_o = CreateEvent(NULL,FALSE,FALSE,NULL);\r
-        *call = m;\r
-        m->refcount++;\r
-    } else\r
-        m->wait_o = NULL;\r
-\r
-    EnterCriticalSection(&cs_kmq_msg);\r
-    kmqint_post((kmq_msg_subscription *) sub, m, try_send);\r
-\r
-    if(m->nCompleted + m->nFailed == m->nSent) {\r
-        kmqint_put_message(m);\r
-    }\r
-    LeaveCriticalSection(&cs_kmq_msg);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kmq_post_sub_msg_ex(khm_handle sub, khm_int32 type, khm_int32 subtype, \r
-                    khm_ui_4 uparam, void * vparam, kmq_call * call)\r
-{\r
-    return kmqint_post_sub_msg_ex(sub, type, subtype, \r
-                                  uparam, vparam, call, FALSE);\r
-}\r
-\r
-khm_int32 \r
-kmqint_post_subs_msg_ex(khm_handle * subs, khm_size   n_subs, khm_int32 type, \r
-                        khm_int32 subtype, khm_ui_4 uparam, void * vparam, \r
-                        kmq_call * call, khm_boolean try_send)\r
-{\r
-    kmq_message * m;\r
-    kherr_context * ctx;\r
-    khm_size i;\r
-\r
-    if(n_subs == 0)\r
-        return KHM_ERROR_SUCCESS;\r
-\r
-    EnterCriticalSection(&cs_kmq_msg);\r
-    m = kmqint_get_message();\r
-    LeaveCriticalSection(&cs_kmq_msg);\r
-\r
-    m->type = type;\r
-    m->subtype = subtype;\r
-    m->uparam = uparam;\r
-    m->vparam = vparam;\r
-\r
-    m->timeSent = GetTickCount();\r
-    m->timeExpire = m->timeSent + kmq_call_dead_timeout;\r
-\r
-    ctx = kherr_peek_context();\r
-    if (ctx) {\r
-        if (ctx->flags & KHERR_CF_TRANSITIVE) {\r
-            m->err_ctx = ctx;\r
-            /* leave it held */\r
-        } else {\r
-            kherr_release_context(ctx);\r
-        }\r
-    }\r
-\r
-    if(call) {\r
-        m->wait_o = CreateEvent(NULL,FALSE,FALSE,NULL);\r
-        *call = m;\r
-        m->refcount++;\r
-    } else\r
-        m->wait_o = NULL;\r
-\r
-    EnterCriticalSection(&cs_kmq_msg);\r
-    for(i=0;i<n_subs;i++) {\r
-        kmqint_post((kmq_msg_subscription *) subs[i], m, try_send);\r
-    }\r
-\r
-    if(m->nCompleted + m->nFailed == m->nSent) {\r
-        kmqint_put_message(m);\r
-    }\r
-    LeaveCriticalSection(&cs_kmq_msg);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kmq_post_subs_msg(khm_handle * subs, \r
-                  khm_size   n_subs, \r
-                  khm_int32 type, \r
-                  khm_int32 subtype, \r
-                  khm_ui_4 uparam, \r
-                  void * vparam)\r
-{\r
-    return kmqint_post_subs_msg_ex(subs,\r
-                                   n_subs,\r
-                                   type,\r
-                                   subtype,\r
-                                   uparam,\r
-                                   vparam,\r
-                                   NULL,\r
-                                   FALSE);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kmq_post_subs_msg_ex(khm_handle * subs, \r
-                     khm_int32 n_subs, \r
-                     khm_int32 type, \r
-                     khm_int32 subtype, \r
-                     khm_ui_4 uparam, \r
-                     void * vparam, \r
-                     kmq_call * call)\r
-{\r
-    return kmqint_post_subs_msg_ex(subs, n_subs, type, subtype, \r
-                                   uparam, vparam, call, FALSE);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kmq_send_subs_msg(khm_handle *subs, \r
-                  khm_int32 n_subs,\r
-                  khm_int32 type, \r
-                  khm_int32 subtype, \r
-                  khm_ui_4 uparam, \r
-                  void * vparam)\r
-{\r
-    kmq_call c;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    rv = kmqint_post_subs_msg_ex(subs, n_subs, type, subtype,\r
-                                 uparam, vparam, &c, TRUE);\r
-    if(KHM_FAILED(rv))\r
-        return rv;\r
-\r
-    rv = kmq_wait(c, INFINITE);\r
-    if(KHM_SUCCEEDED(rv) && c->nFailed > 0)\r
-        rv = KHM_ERROR_PARTIAL;\r
-\r
-    kmq_free_call(c);\r
-\r
-    return rv;\r
-}\r
-\r
-/*! \internal\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-kmq_send_sub_msg(khm_handle sub, khm_int32 type, khm_int32 subtype, \r
-                 khm_ui_4 uparam, void * vparam)\r
-{\r
-    kmq_call c;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    rv = kmqint_post_sub_msg_ex(sub, type, subtype, uparam, vparam, &c, TRUE);\r
-    if(KHM_FAILED(rv))\r
-        return rv;\r
-\r
-    rv = kmq_wait(c, INFINITE);\r
-    if(KHM_SUCCEEDED(rv) && c->nFailed > 0)\r
-        rv = KHM_ERROR_PARTIAL;\r
-\r
-    kmq_free_call(c);\r
-\r
-    return rv;\r
-}\r
-\r
-/*! \internal\r
-    \note Obtains ::cs_kmq_global, ::cs_kmq_msg, ::cs_kmq_msg_ref, kmq_queue::cs\r
-    */\r
-KHMEXP khm_int32 KHMAPI \r
-kmq_send_thread_quit_message(kmq_thread_id thread, khm_ui_4 uparam) {\r
-    kmq_call c;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    rv = kmq_post_thread_quit_message(thread, uparam, &c);\r
-    if(KHM_FAILED(rv))\r
-        return rv;\r
-\r
-    rv = kmq_wait(c, INFINITE);\r
-\r
-    kmq_free_call(c);\r
-\r
-    return rv;\r
-}\r
-\r
-/*! \internal\r
-    \note Obtains ::cs_kmq_global, ::cs_kmq_msg, ::cs_kmq_msg_ref, kmq_queue::cs\r
-    */ \r
-KHMEXP khm_int32 KHMAPI \r
-kmq_post_thread_quit_message(kmq_thread_id thread, \r
-                             khm_ui_4 uparam, kmq_call * call) {\r
-    kmq_message * m;\r
-    kmq_queue * q;\r
-\r
-    EnterCriticalSection(&cs_kmq_global);\r
-    q = queues;\r
-    while(q) {\r
-        if(q->thread == thread)\r
-            break;\r
-        q = LNEXT(q);\r
-    }\r
-    LeaveCriticalSection(&cs_kmq_global);\r
-\r
-    if(!q)\r
-        return KHM_ERROR_NOT_FOUND;\r
-\r
-    EnterCriticalSection(&cs_kmq_msg);\r
-    m = kmqint_get_message();\r
-    LeaveCriticalSection(&cs_kmq_msg);\r
-\r
-    m->type = KMSG_SYSTEM;\r
-    m->subtype = KMSG_SYSTEM_EXIT;\r
-    m->uparam = uparam;\r
-    m->vparam = NULL;\r
-\r
-    m->timeSent = GetTickCount();\r
-    m->timeExpire = m->timeSent + kmq_call_dead_timeout;\r
-\r
-    if(call) {\r
-        m->wait_o = CreateEvent(NULL,FALSE,FALSE,NULL);\r
-        *call = m;\r
-        m->refcount++;\r
-    } else\r
-        m->wait_o = NULL;\r
-\r
-    kmqint_post_queue(q, m);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kmq_get_next_response(kmq_call call, void ** resp) {\r
-    /* TODO: Implement this */\r
-    return 0;\r
-}\r
-\r
-KHMEXP khm_boolean KHMAPI \r
-kmq_has_completed(kmq_call call) {\r
-    khm_boolean completed;\r
-\r
-    EnterCriticalSection(&cs_kmq_msg);\r
-    completed = (call->nCompleted + call->nFailed == call->nSent);\r
-    LeaveCriticalSection(&cs_kmq_msg);\r
-\r
-    return completed;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-kmq_wait(kmq_call call, kmq_timer timeout) {\r
-    kmq_message * m = call;\r
-    DWORD rv;\r
-    /*TODO: check for call free */\r
-\r
-    if(m && m->wait_o) {\r
-        rv = WaitForSingleObject(m->wait_o, timeout);\r
-        if(rv == WAIT_OBJECT_0)\r
-            return KHM_ERROR_SUCCESS;\r
-        else\r
-            return KHM_ERROR_TIMEOUT;\r
-    } else\r
-        return KHM_ERROR_INVALID_PARAM;\r
-}\r
-\r
-/*! \internal\r
-    \note Obtains ::cs_kmq_types\r
-    */\r
-KHMEXP khm_int32 KHMAPI \r
-kmq_set_completion_handler(khm_int32 type, \r
-                           kmq_msg_completion_handler handler) {\r
-    return kmqint_msg_type_set_handler(type, handler);\r
-}\r
-\r
-\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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;
+
+#ifdef DEBUG
+
+#include<stdio.h>
+
+void
+kmqint_dump_publisher(FILE * f) {
+
+    int n_free = 0;
+    int n_active = 0;
+    kmq_message * m;
+
+    EnterCriticalSection(&cs_kmq_msg);
+
+    fprintf(f, "qp0\t*** Free Messages ***\n");
+    fprintf(f, "qp1\tAddress\n");
+
+    m = msg_free;
+    while(m) {
+        n_free++;
+
+        fprintf(f, "qp2\t0x%p\n", m);
+
+        m = LNEXT(m);
+    }
+
+    fprintf(f, "qp3\tTotal free messages : %d\n", n_free);
+
+    fprintf(f, "qp4\t*** Active Messages ***\n");
+    fprintf(f, "qp5\tAddress\tType\tSubtype\tuParam\tvParam\tnSent\tnCompleted\tnFailed\twait_o\trefcount\n");
+
+    m = msg_active;
+    while(m) {
+
+        n_active++;
+
+        fprintf(f, "qp6\t0x%p\t%d\t%d\t0x%x\t0x%p\t%d\t%d\t%d\t0x%p\t%d\n",
+                m,
+                (int) m->type,
+                (int) m->subtype,
+                (unsigned int) m->uparam,
+                m->vparam,
+                (int) m->nSent,
+                (int) m->nCompleted,
+                (int) m->nFailed,
+                (void *) m->wait_o,
+                (int) m->refcount);
+
+        m = LNEXT(m);
+    }
+
+    fprintf(f, "qp7\tTotal number of active messages = %d\n", n_active);
+
+    fprintf(f, "qp8\t--- End ---\n");
+
+    LeaveCriticalSection(&cs_kmq_msg);
+
+}
+
+#endif
+
+/*! \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 = PMALLOC(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);
+}
+
+KHMEXP khm_int32 KHMAPI
+kmq_abort_call(kmq_call call)
+{
+    /* TODO: Implement this */
+    return KHM_ERROR_NOT_IMPLEMENTED;
+}
+
+/*! \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;
+}
+
+KHMEXP khm_int32 KHMAPI 
+kmq_get_next_response(kmq_call call, void ** resp) {
+    /* TODO: Implement this */
+    return 0;
+}
+
+KHMEXP khm_boolean KHMAPI 
+kmq_has_completed(kmq_call call) {
+    khm_boolean completed;
+
+    EnterCriticalSection(&cs_kmq_msg);
+    completed = (call->nCompleted + call->nFailed == call->nSent);
+    LeaveCriticalSection(&cs_kmq_msg);
+
+    return completed;
+}
+
+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_PARAM;
+}
+
+/*! \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);
+}
+
+
index e54e2813742012bbd0de88d7ae7142afd23277c5..696911df4d9279a6fa0ffae891f88032cca040cc 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<windows.h>\r
-\r
-/* forward dcls */\r
-void\r
-kherr_process_attach(void);\r
-\r
-void\r
-kherr_process_detach(void);\r
-\r
-void\r
-kherr_thread_attach(void);\r
-\r
-void\r
-kherr_thread_detach(void);\r
-\r
-void\r
-kconfig_process_attach(void);\r
-\r
-void\r
-kconfig_process_detach(void);\r
-\r
-void\r
-kmq_process_attach(void);\r
-\r
-void\r
-kmq_process_detach(void);\r
-\r
-void\r
-kmq_thread_attach(void);\r
-\r
-void\r
-kmq_thread_detach(void);\r
-\r
-void\r
-kcdb_process_attach(HINSTANCE);\r
-\r
-void\r
-kcdb_process_detach(void);\r
-\r
-void \r
-kmm_process_attach(HINSTANCE);\r
-\r
-void\r
-kmm_process_detach(void);\r
-\r
-void\r
-uilib_process_attach(void);\r
-\r
-void\r
-uilib_process_detach(void);\r
-\r
-\r
-BOOL WINAPI DllMain(\r
-    HINSTANCE hinstDLL,  // handle to DLL module\r
-    DWORD fdwReason,     // reason for calling function\r
-    LPVOID lpReserved )  // reserved\r
-{\r
-    switch(fdwReason) {\r
-    case DLL_PROCESS_ATTACH:\r
-        kherr_process_attach();\r
-        kconfig_process_attach();\r
-        kmq_process_attach();\r
-        kcdb_process_attach(hinstDLL);\r
-        kmm_process_attach(hinstDLL);\r
-        uilib_process_attach();\r
-        break;\r
-\r
-    case DLL_PROCESS_DETACH:\r
-        kherr_process_detach();\r
-        kconfig_process_detach();\r
-        kmq_process_detach();\r
-        kcdb_process_detach();\r
-        kmm_process_detach();\r
-        uilib_process_detach();\r
-        break;\r
-\r
-    case DLL_THREAD_ATTACH:\r
-        kherr_thread_attach();\r
-        kmq_thread_attach();\r
-        break;\r
-\r
-    case DLL_THREAD_DETACH:\r
-        kherr_thread_detach();\r
-        kmq_thread_detach();\r
-        break;\r
-    }\r
-    return TRUE;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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;
+}
index f49987ca733bc2bf7a3c4d954c21a52d6f0f44c2..24fa1a551533ba737e8cbfbaa2f480806441878a 100644 (file)
-/*\r
-* Copyright (c) 2005 Massachusetts Institute of Technology\r
-* Copyright (c) 2007 Secure Endpoints Inc.\r
-*\r
-* Permission is hereby granted, free of charge, to any person\r
-* obtaining a copy of this software and associated documentation\r
-* files (the "Software"), to deal in the Software without\r
-* restriction, including without limitation the rights to use, copy,\r
-* modify, merge, publish, distribute, sublicense, and/or sell copies\r
-* of the Software, and to permit persons to whom the Software is\r
-* furnished to do so, subject to the following conditions:\r
-*\r
-* The above copyright notice and this permission notice shall be\r
-* included in all copies or substantial portions of the Software.\r
-*\r
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
-* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
-* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
-* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
-* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
-* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
-* SOFTWARE.\r
-*/\r
-\r
-/* $Id$ */\r
-\r
-#include<windows.h>\r
-#include<netidmgr.h>\r
-#include<dynimport.h>\r
-\r
-HINSTANCE hKrb4 = 0;\r
-HINSTANCE hKrb5 = 0;\r
-HINSTANCE hKrb524 = 0;\r
-HINSTANCE hSecur32 = 0;\r
-HINSTANCE hComErr = 0;\r
-HINSTANCE hService = 0;\r
-HINSTANCE hProfile = 0;\r
-HINSTANCE hPsapi = 0; \r
-HINSTANCE hToolHelp32 = 0; \r
-HINSTANCE hCCAPI = 0;\r
-\r
-DWORD     AfsAvailable = 0;\r
-\r
-// CCAPI\r
-DECL_FUNC_PTR(cc_initialize);\r
-DECL_FUNC_PTR(cc_shutdown);\r
-DECL_FUNC_PTR(cc_get_NC_info);\r
-DECL_FUNC_PTR(cc_free_NC_info);\r
-\r
-// krb4 functions\r
-DECL_FUNC_PTR(get_krb_err_txt_entry);\r
-DECL_FUNC_PTR(k_isinst);\r
-DECL_FUNC_PTR(k_isname);\r
-DECL_FUNC_PTR(k_isrealm);\r
-DECL_FUNC_PTR(kadm_change_your_password);\r
-DECL_FUNC_PTR(kname_parse);\r
-DECL_FUNC_PTR(krb_get_cred);\r
-DECL_FUNC_PTR(krb_get_krbhst);\r
-DECL_FUNC_PTR(krb_get_lrealm);\r
-DECL_FUNC_PTR(krb_get_pw_in_tkt);\r
-DECL_FUNC_PTR(krb_get_tf_realm);\r
-DECL_FUNC_PTR(krb_mk_req);\r
-DECL_FUNC_PTR(krb_realmofhost);\r
-DECL_FUNC_PTR(tf_init);\r
-DECL_FUNC_PTR(tf_close);\r
-DECL_FUNC_PTR(tf_get_cred);\r
-DECL_FUNC_PTR(tf_get_pname);\r
-DECL_FUNC_PTR(tf_get_pinst);\r
-DECL_FUNC_PTR(LocalHostAddr);\r
-DECL_FUNC_PTR(tkt_string);\r
-DECL_FUNC_PTR(krb_set_tkt_string);\r
-DECL_FUNC_PTR(initialize_krb_error_func);\r
-DECL_FUNC_PTR(initialize_kadm_error_table);\r
-DECL_FUNC_PTR(dest_tkt);\r
-DECL_FUNC_PTR(krb_in_tkt);\r
-DECL_FUNC_PTR(krb_save_credentials);\r
-DECL_FUNC_PTR(krb_get_krbconf2);\r
-DECL_FUNC_PTR(krb_get_krbrealm2);\r
-DECL_FUNC_PTR(krb_life_to_time);\r
-\r
-// krb5 functions\r
-DECL_FUNC_PTR(krb5_change_password);\r
-DECL_FUNC_PTR(krb5_get_init_creds_opt_init);\r
-DECL_FUNC_PTR(krb5_get_init_creds_opt_set_tkt_life);\r
-DECL_FUNC_PTR(krb5_get_init_creds_opt_set_renew_life);\r
-DECL_FUNC_PTR(krb5_get_init_creds_opt_set_forwardable);\r
-DECL_FUNC_PTR(krb5_get_init_creds_opt_set_proxiable);\r
-DECL_FUNC_PTR(krb5_get_init_creds_opt_set_address_list);\r
-DECL_FUNC_PTR(krb5_get_init_creds_opt_set_change_password_prompt);\r
-DECL_FUNC_PTR(krb5_get_init_creds_password);\r
-DECL_FUNC_PTR(krb5_get_prompt_types);\r
-DECL_FUNC_PTR(krb5_build_principal_ext);\r
-DECL_FUNC_PTR(krb5_cc_get_name);\r
-DECL_FUNC_PTR(krb5_cc_get_type);\r
-DECL_FUNC_PTR(krb5_cc_resolve);\r
-DECL_FUNC_PTR(krb5_cc_default);\r
-DECL_FUNC_PTR(krb5_cc_default_name);\r
-DECL_FUNC_PTR(krb5_cc_set_default_name);\r
-DECL_FUNC_PTR(krb5_cc_initialize);\r
-DECL_FUNC_PTR(krb5_cc_destroy);\r
-DECL_FUNC_PTR(krb5_cc_close);\r
-DECL_FUNC_PTR(krb5_cc_store_cred);\r
-DECL_FUNC_PTR(krb5_cc_copy_creds);\r
-DECL_FUNC_PTR(krb5_cc_retrieve_cred);\r
-DECL_FUNC_PTR(krb5_cc_get_principal);\r
-DECL_FUNC_PTR(krb5_cc_start_seq_get);\r
-DECL_FUNC_PTR(krb5_cc_next_cred);\r
-DECL_FUNC_PTR(krb5_cc_end_seq_get);\r
-DECL_FUNC_PTR(krb5_cc_remove_cred);\r
-DECL_FUNC_PTR(krb5_cc_set_flags);\r
-// DECL_FUNC_PTR(krb5_cc_get_type);\r
-DECL_FUNC_PTR(krb5_free_context);\r
-DECL_FUNC_PTR(krb5_free_cred_contents);\r
-DECL_FUNC_PTR(krb5_free_principal);\r
-DECL_FUNC_PTR(krb5_get_in_tkt_with_password);\r
-DECL_FUNC_PTR(krb5_init_context);\r
-DECL_FUNC_PTR(krb5_parse_name);\r
-DECL_FUNC_PTR(krb5_timeofday);\r
-DECL_FUNC_PTR(krb5_timestamp_to_sfstring);\r
-DECL_FUNC_PTR(krb5_unparse_name);\r
-DECL_FUNC_PTR(krb5_get_credentials);\r
-DECL_FUNC_PTR(krb5_mk_req);\r
-DECL_FUNC_PTR(krb5_sname_to_principal);\r
-DECL_FUNC_PTR(krb5_get_credentials_renew);\r
-DECL_FUNC_PTR(krb5_free_data);\r
-DECL_FUNC_PTR(krb5_free_data_contents);\r
-// DECL_FUNC_PTR(krb5_get_realm_domain);\r
-DECL_FUNC_PTR(krb5_free_unparsed_name);\r
-DECL_FUNC_PTR(krb5_os_localaddr);\r
-DECL_FUNC_PTR(krb5_copy_keyblock_contents);\r
-DECL_FUNC_PTR(krb5_copy_data);\r
-DECL_FUNC_PTR(krb5_free_creds);\r
-DECL_FUNC_PTR(krb5_build_principal);\r
-DECL_FUNC_PTR(krb5_get_renewed_creds);\r
-DECL_FUNC_PTR(krb5_get_default_config_files);\r
-DECL_FUNC_PTR(krb5_free_config_files);\r
-DECL_FUNC_PTR(krb5_get_default_realm);\r
-DECL_FUNC_PTR(krb5_set_default_realm);\r
-DECL_FUNC_PTR(krb5_free_ticket);\r
-DECL_FUNC_PTR(krb5_decode_ticket);\r
-DECL_FUNC_PTR(krb5_get_host_realm);\r
-DECL_FUNC_PTR(krb5_free_host_realm);\r
-DECL_FUNC_PTR(krb5_c_random_make_octets);\r
-DECL_FUNC_PTR(krb5_free_addresses);\r
-DECL_FUNC_PTR(krb5_free_default_realm);\r
-DECL_FUNC_PTR(krb5_string_to_deltat);\r
-\r
-// Krb524 functions\r
-DECL_FUNC_PTR(krb524_init_ets);\r
-DECL_FUNC_PTR(krb524_convert_creds_kdc);\r
-\r
-// ComErr functions\r
-DECL_FUNC_PTR(com_err);\r
-DECL_FUNC_PTR(error_message);\r
-\r
-// Profile functions\r
-DECL_FUNC_PTR(profile_init);    \r
-DECL_FUNC_PTR(profile_flush);\r
-DECL_FUNC_PTR(profile_release); \r
-DECL_FUNC_PTR(profile_get_subsection_names);\r
-DECL_FUNC_PTR(profile_free_list);\r
-DECL_FUNC_PTR(profile_get_string);\r
-DECL_FUNC_PTR(profile_get_integer);\r
-DECL_FUNC_PTR(profile_get_values);\r
-DECL_FUNC_PTR(profile_get_relation_names);\r
-DECL_FUNC_PTR(profile_clear_relation);\r
-DECL_FUNC_PTR(profile_add_relation);\r
-DECL_FUNC_PTR(profile_update_relation);\r
-DECL_FUNC_PTR(profile_release_string);\r
-DECL_FUNC_PTR(profile_rename_section);\r
-\r
-// Service functions\r
-DECL_FUNC_PTR(OpenSCManagerA);\r
-DECL_FUNC_PTR(OpenServiceA);\r
-DECL_FUNC_PTR(QueryServiceStatus);\r
-DECL_FUNC_PTR(CloseServiceHandle);\r
-DECL_FUNC_PTR(LsaNtStatusToWinError);\r
-\r
-// LSA Functions\r
-DECL_FUNC_PTR(LsaConnectUntrusted);\r
-DECL_FUNC_PTR(LsaLookupAuthenticationPackage);\r
-DECL_FUNC_PTR(LsaCallAuthenticationPackage);\r
-DECL_FUNC_PTR(LsaFreeReturnBuffer);\r
-DECL_FUNC_PTR(LsaGetLogonSessionData);\r
-\r
-// CCAPI\r
-FUNC_INFO ccapi_fi[] = {\r
-    MAKE_FUNC_INFO(cc_initialize),\r
-    MAKE_FUNC_INFO(cc_shutdown),\r
-    MAKE_FUNC_INFO(cc_get_NC_info),\r
-    MAKE_FUNC_INFO(cc_free_NC_info),\r
-    END_FUNC_INFO\r
-};\r
-\r
-FUNC_INFO k4_fi[] = {\r
-    MAKE_FUNC_INFO(get_krb_err_txt_entry),\r
-    MAKE_FUNC_INFO(k_isinst),\r
-    MAKE_FUNC_INFO(k_isname),\r
-    MAKE_FUNC_INFO(k_isrealm),\r
-    MAKE_FUNC_INFO(kadm_change_your_password),\r
-    MAKE_FUNC_INFO(kname_parse),\r
-    MAKE_FUNC_INFO(krb_get_cred),\r
-    MAKE_FUNC_INFO(krb_get_krbhst),\r
-    MAKE_FUNC_INFO(krb_get_lrealm),\r
-    MAKE_FUNC_INFO(krb_get_pw_in_tkt),\r
-    MAKE_FUNC_INFO(krb_get_tf_realm),\r
-    MAKE_FUNC_INFO(krb_mk_req),\r
-    MAKE_FUNC_INFO(krb_realmofhost),\r
-    MAKE_FUNC_INFO(tf_init),\r
-    MAKE_FUNC_INFO(tf_close),\r
-    MAKE_FUNC_INFO(tf_get_cred),\r
-    MAKE_FUNC_INFO(tf_get_pname),\r
-    MAKE_FUNC_INFO(tf_get_pinst),\r
-    MAKE_FUNC_INFO(LocalHostAddr),\r
-    MAKE_FUNC_INFO(tkt_string),\r
-    MAKE_FUNC_INFO(krb_set_tkt_string),\r
-    MAKE_FUNC_INFO(initialize_krb_error_func),\r
-    MAKE_FUNC_INFO(initialize_kadm_error_table),\r
-    MAKE_FUNC_INFO(dest_tkt),\r
-    /*        MAKE_FUNC_INFO(lsh_LoadKrb4LeashErrorTables), */// XXX\r
-    MAKE_FUNC_INFO(krb_in_tkt),\r
-    MAKE_FUNC_INFO(krb_save_credentials),\r
-    MAKE_FUNC_INFO(krb_get_krbconf2),\r
-    MAKE_FUNC_INFO(krb_get_krbrealm2),\r
-    MAKE_FUNC_INFO(krb_life_to_time),\r
-    END_FUNC_INFO\r
-};\r
-\r
-FUNC_INFO k5_fi[] = {\r
-    MAKE_FUNC_INFO(krb5_change_password),\r
-    MAKE_FUNC_INFO(krb5_get_init_creds_opt_init),\r
-    MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_tkt_life),\r
-    MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_renew_life),\r
-    MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_forwardable),\r
-    MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_proxiable),\r
-    MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_address_list),\r
-    MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_change_password_prompt),\r
-    MAKE_FUNC_INFO(krb5_get_init_creds_password),\r
-    MAKE_FUNC_INFO(krb5_get_prompt_types),\r
-    MAKE_FUNC_INFO(krb5_build_principal_ext),\r
-    MAKE_FUNC_INFO(krb5_cc_get_name),\r
-    MAKE_FUNC_INFO(krb5_cc_get_type),\r
-    MAKE_FUNC_INFO(krb5_cc_resolve),\r
-    MAKE_FUNC_INFO(krb5_cc_default),\r
-    MAKE_FUNC_INFO(krb5_cc_default_name),\r
-    MAKE_FUNC_INFO(krb5_cc_set_default_name),\r
-    MAKE_FUNC_INFO(krb5_cc_initialize),\r
-    MAKE_FUNC_INFO(krb5_cc_destroy),\r
-    MAKE_FUNC_INFO(krb5_cc_close),\r
-    MAKE_FUNC_INFO(krb5_cc_copy_creds),\r
-    MAKE_FUNC_INFO(krb5_cc_store_cred),\r
-    MAKE_FUNC_INFO(krb5_cc_retrieve_cred),\r
-    MAKE_FUNC_INFO(krb5_cc_get_principal),\r
-    MAKE_FUNC_INFO(krb5_cc_start_seq_get),\r
-    MAKE_FUNC_INFO(krb5_cc_next_cred),\r
-    MAKE_FUNC_INFO(krb5_cc_end_seq_get),\r
-    MAKE_FUNC_INFO(krb5_cc_remove_cred),\r
-    MAKE_FUNC_INFO(krb5_cc_set_flags),\r
-    // MAKE_FUNC_INFO(krb5_cc_get_type),\r
-    MAKE_FUNC_INFO(krb5_free_context),\r
-    MAKE_FUNC_INFO(krb5_free_cred_contents),\r
-    MAKE_FUNC_INFO(krb5_free_principal),\r
-    MAKE_FUNC_INFO(krb5_get_in_tkt_with_password),\r
-    MAKE_FUNC_INFO(krb5_init_context),\r
-    MAKE_FUNC_INFO(krb5_parse_name),\r
-    MAKE_FUNC_INFO(krb5_timeofday),\r
-    MAKE_FUNC_INFO(krb5_timestamp_to_sfstring),\r
-    MAKE_FUNC_INFO(krb5_unparse_name),\r
-    MAKE_FUNC_INFO(krb5_get_credentials),\r
-    MAKE_FUNC_INFO(krb5_mk_req),\r
-    MAKE_FUNC_INFO(krb5_sname_to_principal),\r
-    MAKE_FUNC_INFO(krb5_get_credentials_renew),\r
-    MAKE_FUNC_INFO(krb5_free_data),\r
-    MAKE_FUNC_INFO(krb5_free_data_contents),\r
-    //  MAKE_FUNC_INFO(krb5_get_realm_domain),\r
-    MAKE_FUNC_INFO(krb5_free_unparsed_name),\r
-    MAKE_FUNC_INFO(krb5_os_localaddr),\r
-    MAKE_FUNC_INFO(krb5_copy_keyblock_contents),\r
-    MAKE_FUNC_INFO(krb5_copy_data),\r
-    MAKE_FUNC_INFO(krb5_free_creds),\r
-    MAKE_FUNC_INFO(krb5_build_principal),\r
-    MAKE_FUNC_INFO(krb5_get_renewed_creds),\r
-    MAKE_FUNC_INFO(krb5_free_addresses),\r
-    MAKE_FUNC_INFO(krb5_get_default_config_files),\r
-    MAKE_FUNC_INFO(krb5_free_config_files),\r
-    MAKE_FUNC_INFO(krb5_get_default_realm),\r
-    MAKE_FUNC_INFO(krb5_set_default_realm),\r
-    MAKE_FUNC_INFO(krb5_free_ticket),\r
-    MAKE_FUNC_INFO(krb5_decode_ticket),\r
-    MAKE_FUNC_INFO(krb5_get_host_realm),\r
-    MAKE_FUNC_INFO(krb5_free_host_realm),\r
-    MAKE_FUNC_INFO(krb5_c_random_make_octets),\r
-    MAKE_FUNC_INFO(krb5_free_default_realm),\r
-    MAKE_FUNC_INFO(krb5_string_to_deltat),\r
-    END_FUNC_INFO\r
-};\r
-\r
-FUNC_INFO k524_fi[] = {\r
-    MAKE_FUNC_INFO(krb524_init_ets),\r
-    MAKE_FUNC_INFO(krb524_convert_creds_kdc),\r
-    END_FUNC_INFO\r
-};\r
-\r
-FUNC_INFO profile_fi[] = {\r
-    MAKE_FUNC_INFO(profile_init),\r
-    MAKE_FUNC_INFO(profile_flush),\r
-    MAKE_FUNC_INFO(profile_release), \r
-    MAKE_FUNC_INFO(profile_get_subsection_names),\r
-    MAKE_FUNC_INFO(profile_free_list),\r
-    MAKE_FUNC_INFO(profile_get_string),\r
-    MAKE_FUNC_INFO(profile_get_integer),\r
-    MAKE_FUNC_INFO(profile_get_values),\r
-    MAKE_FUNC_INFO(profile_get_relation_names),\r
-    MAKE_FUNC_INFO(profile_clear_relation),\r
-    MAKE_FUNC_INFO(profile_add_relation),\r
-    MAKE_FUNC_INFO(profile_update_relation),\r
-    MAKE_FUNC_INFO(profile_release_string),\r
-    MAKE_FUNC_INFO(profile_rename_section),\r
-    END_FUNC_INFO\r
-};\r
-\r
-FUNC_INFO ce_fi[] = {\r
-    MAKE_FUNC_INFO(com_err),\r
-    MAKE_FUNC_INFO(error_message),\r
-    END_FUNC_INFO\r
-};\r
-\r
-FUNC_INFO service_fi[] = {\r
-    MAKE_FUNC_INFO(OpenSCManagerA),\r
-    MAKE_FUNC_INFO(OpenServiceA),\r
-    MAKE_FUNC_INFO(QueryServiceStatus),\r
-    MAKE_FUNC_INFO(CloseServiceHandle),\r
-    MAKE_FUNC_INFO(LsaNtStatusToWinError),\r
-    END_FUNC_INFO\r
-};\r
-\r
-FUNC_INFO lsa_fi[] = {\r
-    MAKE_FUNC_INFO(LsaConnectUntrusted),\r
-    MAKE_FUNC_INFO(LsaLookupAuthenticationPackage),\r
-    MAKE_FUNC_INFO(LsaCallAuthenticationPackage),\r
-    MAKE_FUNC_INFO(LsaFreeReturnBuffer),\r
-    MAKE_FUNC_INFO(LsaGetLogonSessionData),\r
-    END_FUNC_INFO\r
-};\r
-\r
-// psapi functions\r
-DECL_FUNC_PTR(GetModuleFileNameExA);\r
-DECL_FUNC_PTR(EnumProcessModules);\r
-\r
-FUNC_INFO psapi_fi[] = {\r
-    MAKE_FUNC_INFO(GetModuleFileNameExA),\r
-        MAKE_FUNC_INFO(EnumProcessModules),\r
-        END_FUNC_INFO\r
-};\r
-\r
-// toolhelp functions\r
-DECL_FUNC_PTR(CreateToolhelp32Snapshot);\r
-DECL_FUNC_PTR(Module32First);\r
-DECL_FUNC_PTR(Module32Next);\r
-\r
-FUNC_INFO toolhelp_fi[] = {\r
-    MAKE_FUNC_INFO(CreateToolhelp32Snapshot),\r
-        MAKE_FUNC_INFO(Module32First),\r
-        MAKE_FUNC_INFO(Module32Next),\r
-        END_FUNC_INFO\r
-};\r
-\r
-khm_int32 init_imports(void) {\r
-    OSVERSIONINFO osvi;\r
-    int imp_rv = 1;\r
-\r
-#define CKRV(m)           \\r
-  do {                    \\r
-    if(!imp_rv) {         \\r
-      _reportf(L"Can't locate all required exports from module [%S]", (m)); \\r
-      goto _err_ret;      \\r
-    }                     \\r
- } while (FALSE)\r
-\r
-#ifndef _WIN64\r
-    imp_rv = LoadFuncs(KRB4_DLL, k4_fi, &hKrb4, 0, 1, 0, 0);\r
-    CKRV(KRB4_DLL);\r
-#endif\r
-\r
-    imp_rv = LoadFuncs(KRB5_DLL, k5_fi, &hKrb5, 0, 1, 0, 0);\r
-    CKRV(KRB5_DLL);\r
-\r
-    imp_rv = LoadFuncs(COMERR_DLL, ce_fi, &hComErr, 0, 0, 1, 0);\r
-    CKRV(COMERR_DLL);\r
-\r
-    imp_rv = LoadFuncs(SERVICE_DLL, service_fi, &hService, 0, 1, 0, 0);\r
-    CKRV(SERVICE_DLL);\r
-\r
-    imp_rv = LoadFuncs(SECUR32_DLL, lsa_fi, &hSecur32, 0, 1, 1, 1);\r
-    CKRV(SECUR32_DLL);\r
-\r
-    imp_rv = LoadFuncs(KRB524_DLL, k524_fi, &hKrb524, 0, 1, 1, 1);\r
-    CKRV(KRB524_DLL);\r
-\r
-    imp_rv = LoadFuncs(PROFILE_DLL, profile_fi, &hProfile, 0, 1, 0, 0);\r
-    CKRV(PROFILE_DLL);\r
-\r
-    imp_rv = LoadFuncs(CCAPI_DLL, ccapi_fi, &hCCAPI, 0, 1, 0, 0);\r
-    /* CCAPI_DLL is optional.  No error check. */\r
-\r
-    memset(&osvi, 0, sizeof(OSVERSIONINFO));\r
-    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);\r
-    GetVersionEx(&osvi);\r
-\r
-    // XXX: We should really use feature testing, first\r
-    // checking for CreateToolhelp32Snapshot.  If that's\r
-    // not around, we try the psapi stuff.\r
-    //\r
-    // Only load LSA functions if on NT/2000/XP\r
-    if(osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)\r
-    {\r
-        // Windows 9x\r
-        imp_rv = LoadFuncs(TOOLHELPDLL, toolhelp_fi, &hToolHelp32, 0, 1, 0, 0);\r
-        CKRV(TOOLHELPDLL);\r
-\r
-        hPsapi = 0;\r
-    }             \r
-    else if(osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)\r
-    {\r
-        // Windows NT\r
-        imp_rv = LoadFuncs(PSAPIDLL, psapi_fi, &hPsapi, 0, 1, 0, 0);\r
-        CKRV(PSAPIDLL);\r
-\r
-        hToolHelp32 = 0;\r
-    }\r
-\r
-    AfsAvailable = TRUE; //afscompat_init();\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-\r
- _err_ret:\r
-    return KHM_ERROR_NOT_FOUND;\r
-}\r
-\r
-khm_int32 exit_imports(void) {\r
-    //afscompat_close();\r
-\r
-    if (hKrb4)\r
-        FreeLibrary(hKrb4);\r
-    if (hKrb5)\r
-        FreeLibrary(hKrb5);\r
-    if (hProfile)\r
-        FreeLibrary(hProfile);\r
-    if (hComErr)\r
-        FreeLibrary(hComErr);\r
-    if (hService)\r
-        FreeLibrary(hService);\r
-    if (hSecur32)\r
-        FreeLibrary(hSecur32);\r
-    if (hKrb524)\r
-        FreeLibrary(hKrb524);\r
-    if (hPsapi)\r
-        FreeLibrary(hPsapi);\r
-    if (hToolHelp32)\r
-        FreeLibrary(hToolHelp32);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-int (*Lcom_err)(LPSTR,long,LPSTR,...);\r
-LPSTR (*Lerror_message)(long);\r
-LPSTR (*Lerror_table_name)(long);\r
-\r
-void Leash_load_com_err_callback(FARPROC ce,\r
-                                 FARPROC em,\r
-                                 FARPROC etn)\r
-{\r
-    (FARPROC)Lcom_err=ce;\r
-    (FARPROC)Lerror_message=em;\r
-    (FARPROC)Lerror_table_name=etn;\r
-}\r
+/*
+* Copyright (c) 2005 Massachusetts Institute of Technology
+* Copyright (c) 2007 Secure Endpoints Inc.
+*
+* Permission is hereby granted, free of charge, to any person
+* obtaining a copy of this software and associated documentation
+* files (the "Software"), to deal in the Software without
+* restriction, including without limitation the rights to use, copy,
+* modify, merge, publish, distribute, sublicense, and/or sell copies
+* of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+* ACTION OF 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<netidmgr.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_opt_set_change_password_prompt);
+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_get_type);
+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_set_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);
+DECL_FUNC_PTR(krb5_string_to_deltat);
+
+// 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_flush);
+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_get_integer);
+DECL_FUNC_PTR(profile_get_values);
+DECL_FUNC_PTR(profile_get_relation_names);
+DECL_FUNC_PTR(profile_clear_relation);
+DECL_FUNC_PTR(profile_add_relation);
+DECL_FUNC_PTR(profile_update_relation);
+DECL_FUNC_PTR(profile_release_string);
+DECL_FUNC_PTR(profile_rename_section);
+
+// 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_opt_set_change_password_prompt),
+    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_get_type),
+    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_set_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),
+    MAKE_FUNC_INFO(krb5_string_to_deltat),
+    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_flush),
+    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_get_integer),
+    MAKE_FUNC_INFO(profile_get_values),
+    MAKE_FUNC_INFO(profile_get_relation_names),
+    MAKE_FUNC_INFO(profile_clear_relation),
+    MAKE_FUNC_INFO(profile_add_relation),
+    MAKE_FUNC_INFO(profile_update_relation),
+    MAKE_FUNC_INFO(profile_release_string),
+    MAKE_FUNC_INFO(profile_rename_section),
+    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;
+    int imp_rv = 1;
+
+#define CKRV(m)           \
+  do {                    \
+    if(!imp_rv) {         \
+      _reportf(L"Can't locate all required exports from module [%S]", (m)); \
+      goto _err_ret;      \
+    }                     \
+ } while (FALSE)
+
+#ifndef _WIN64
+    imp_rv = LoadFuncs(KRB4_DLL, k4_fi, &hKrb4, 0, 1, 0, 0);
+    CKRV(KRB4_DLL);
+#endif
+
+    imp_rv = LoadFuncs(KRB5_DLL, k5_fi, &hKrb5, 0, 1, 0, 0);
+    CKRV(KRB5_DLL);
+
+    imp_rv = LoadFuncs(COMERR_DLL, ce_fi, &hComErr, 0, 0, 1, 0);
+    CKRV(COMERR_DLL);
+
+    imp_rv = LoadFuncs(SERVICE_DLL, service_fi, &hService, 0, 1, 0, 0);
+    CKRV(SERVICE_DLL);
+
+    imp_rv = LoadFuncs(SECUR32_DLL, lsa_fi, &hSecur32, 0, 1, 1, 1);
+    CKRV(SECUR32_DLL);
+
+    imp_rv = LoadFuncs(KRB524_DLL, k524_fi, &hKrb524, 0, 1, 1, 1);
+    CKRV(KRB524_DLL);
+
+    imp_rv = LoadFuncs(PROFILE_DLL, profile_fi, &hProfile, 0, 1, 0, 0);
+    CKRV(PROFILE_DLL);
+
+    imp_rv = LoadFuncs(CCAPI_DLL, ccapi_fi, &hCCAPI, 0, 1, 0, 0);
+    /* CCAPI_DLL is optional.  No error check. */
+
+    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
+        imp_rv = LoadFuncs(TOOLHELPDLL, toolhelp_fi, &hToolHelp32, 0, 1, 0, 0);
+        CKRV(TOOLHELPDLL);
+
+        hPsapi = 0;
+    }             
+    else if(osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
+    {
+        // Windows NT
+        imp_rv = LoadFuncs(PSAPIDLL, psapi_fi, &hPsapi, 0, 1, 0, 0);
+        CKRV(PSAPIDLL);
+
+        hToolHelp32 = 0;
+    }
+
+    AfsAvailable = TRUE; //afscompat_init();
+
+    return KHM_ERROR_SUCCESS;
+
+ _err_ret:
+    return KHM_ERROR_NOT_FOUND;
+}
+
+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;
+}
index 7f3f598b066d9a150c29e474ffb01c46703a249e..850d96e5f69405c2a54dd19074cf65c5a6fd04b9 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_DYNIMPORT_H\r
-#define __KHIMAIRA_DYNIMPORT_H\r
-\r
-/* Dynamic imports */\r
-#include<khdefs.h>\r
-#include<tlhelp32.h>\r
-\r
-#if _WIN32_WINNT < 0x0501\r
-#define KHM_SAVE_WIN32_WINNT _WIN32_WINNT\r
-#undef _WIN32_WINNT\r
-#define _WIN32_WINNT 0x0501\r
-#endif\r
-#include<ntsecapi.h>\r
-#ifdef KHM_SAVE_WIN32_WINNT\r
-#undef _WIN32_WINNT\r
-#define _WIN32_WINNT KHM_SAVE_WIN32_WINNT\r
-#undef KHM_SAVE_WIN32_WINNT\r
-#endif\r
-\r
-extern HINSTANCE hKrb4;\r
-extern HINSTANCE hKrb5;\r
-extern HINSTANCE hProfile;\r
-\r
-///////////////////////////////////////////////////////////////////////////////\r
-\r
-#define CCAPI_DLL     "krbcc32.dll"\r
-#define KRBCC32_DLL   "krbcc32.dll"\r
-#define SERVICE_DLL   "advapi32.dll"\r
-#define SECUR32_DLL   "secur32.dll"\r
-#define PROFILE_DLL   "xpprof32.dll"\r
-\r
-//////////////////////////////////////////////////////////////////////////////\r
-\r
-#include <loadfuncs-com_err.h>\r
-#include <loadfuncs-krb5.h>\r
-#include <loadfuncs-profile.h>\r
-#include <loadfuncs-krb.h>\r
-#include <loadfuncs-krb524.h>\r
-#include <loadfuncs-lsa.h>\r
-\r
-//// CCAPI\r
-/* In order to avoid including the private CCAPI headers */\r
-typedef int cc_int32;\r
-\r
-#define CC_API_VER_1 1\r
-#define CC_API_VER_2 2\r
-\r
-#define CCACHE_API cc_int32\r
-\r
-/*\r
-** The Official Error Codes\r
-*/\r
-#define CC_NOERROR           0\r
-#define CC_BADNAME           1\r
-#define CC_NOTFOUND          2\r
-#define CC_END               3\r
-#define CC_IO                4\r
-#define CC_WRITE             5\r
-#define CC_NOMEM             6\r
-#define CC_FORMAT            7\r
-#define CC_LOCKED            8\r
-#define CC_BAD_API_VERSION   9\r
-#define CC_NO_EXIST          10\r
-#define CC_NOT_SUPP          11\r
-#define CC_BAD_PARM          12\r
-#define CC_ERR_CACHE_ATTACH  13\r
-#define CC_ERR_CACHE_RELEASE 14\r
-#define CC_ERR_CACHE_FULL    15\r
-#define CC_ERR_CRED_VERSION  16\r
-\r
-enum {\r
-    CC_CRED_VUNKNOWN = 0,       // For validation\r
-    CC_CRED_V4 = 1,\r
-    CC_CRED_V5 = 2,\r
-    CC_CRED_VMAX = 3            // For validation\r
-};\r
-\r
-typedef struct opaque_dll_control_block_type* apiCB;\r
-typedef struct _infoNC {\r
-    char*     name;\r
-    char*     principal;\r
-    cc_int32  vers;\r
-} infoNC;\r
-\r
-TYPEDEF_FUNC(\r
-CCACHE_API,\r
-CALLCONV_C,\r
-cc_initialize,\r
-    (\r
-    apiCB** cc_ctx,           // <  DLL's primary control structure.\r
-                              //    returned here, passed everywhere else\r
-    cc_int32 api_version,     // >  ver supported by caller (use CC_API_VER_1)\r
-    cc_int32*  api_supported, // <  if ~NULL, max ver supported by DLL\r
-    const char** vendor       // <  if ~NULL, vendor name in read only C string\r
-    )\r
-);\r
-\r
-TYPEDEF_FUNC(\r
-CCACHE_API,\r
-CALLCONV_C,\r
-cc_shutdown,\r
-    (\r
-    apiCB** cc_ctx            // <> DLL's primary control structure. NULL after\r
-    )\r
-);\r
-\r
-TYPEDEF_FUNC(\r
-CCACHE_API,\r
-CALLCONV_C,\r
-cc_get_NC_info,\r
-    (\r
-    apiCB* cc_ctx,          // >  DLL's primary control structure\r
-    struct _infoNC*** ppNCi // <  (NULL before call) null terminated,\r
-                            //    list of a structs (free via cc_free_infoNC())\r
-    )\r
-);\r
-\r
-TYPEDEF_FUNC(\r
-CCACHE_API,\r
-CALLCONV_C,\r
-cc_free_NC_info,\r
-    (\r
-    apiCB* cc_ctx,\r
-    struct _infoNC*** ppNCi // <  free list of structs returned by\r
-                            //    cc_get_cache_names().  set to NULL on return\r
-    )\r
-);\r
-//// \CCAPI\r
-\r
-extern  DWORD AfsAvailable;\r
-\r
-// service definitions\r
-typedef SC_HANDLE (WINAPI *FP_OpenSCManagerA)(char *, char *, DWORD);\r
-typedef SC_HANDLE (WINAPI *FP_OpenServiceA)(SC_HANDLE, char *, DWORD);\r
-typedef BOOL (WINAPI *FP_QueryServiceStatus)(SC_HANDLE, LPSERVICE_STATUS);\r
-typedef BOOL (WINAPI *FP_CloseServiceHandle)(SC_HANDLE);\r
-\r
-//////////////////////////////////////////////////////////////////////////////\r
-\r
-// CCAPI\r
-extern DECL_FUNC_PTR(cc_initialize);\r
-extern DECL_FUNC_PTR(cc_shutdown);\r
-extern DECL_FUNC_PTR(cc_get_NC_info);\r
-extern DECL_FUNC_PTR(cc_free_NC_info);\r
-\r
-// krb4 functions\r
-extern DECL_FUNC_PTR(get_krb_err_txt_entry);\r
-extern DECL_FUNC_PTR(k_isinst);\r
-extern DECL_FUNC_PTR(k_isname);\r
-extern DECL_FUNC_PTR(k_isrealm);\r
-extern DECL_FUNC_PTR(kadm_change_your_password);\r
-extern DECL_FUNC_PTR(kname_parse);\r
-extern DECL_FUNC_PTR(krb_get_cred);\r
-extern DECL_FUNC_PTR(krb_get_krbhst);\r
-extern DECL_FUNC_PTR(krb_get_lrealm);\r
-extern DECL_FUNC_PTR(krb_get_pw_in_tkt);\r
-extern DECL_FUNC_PTR(krb_get_tf_realm);\r
-extern DECL_FUNC_PTR(krb_mk_req);\r
-extern DECL_FUNC_PTR(krb_realmofhost);\r
-extern DECL_FUNC_PTR(tf_init);\r
-extern DECL_FUNC_PTR(tf_close);\r
-extern DECL_FUNC_PTR(tf_get_cred);\r
-extern DECL_FUNC_PTR(tf_get_pname);\r
-extern DECL_FUNC_PTR(tf_get_pinst);\r
-extern DECL_FUNC_PTR(LocalHostAddr);\r
-extern DECL_FUNC_PTR(tkt_string);\r
-extern DECL_FUNC_PTR(krb_set_tkt_string);\r
-extern DECL_FUNC_PTR(initialize_krb_error_func);\r
-extern DECL_FUNC_PTR(initialize_kadm_error_table);\r
-extern DECL_FUNC_PTR(dest_tkt);\r
-extern DECL_FUNC_PTR(lsh_LoadKrb4LeashErrorTables); // XXX\r
-extern DECL_FUNC_PTR(krb_in_tkt);\r
-extern DECL_FUNC_PTR(krb_save_credentials);\r
-extern DECL_FUNC_PTR(krb_get_krbconf2);\r
-extern DECL_FUNC_PTR(krb_get_krbrealm2);\r
-extern DECL_FUNC_PTR(krb_life_to_time);\r
-\r
-// krb5 functions\r
-extern DECL_FUNC_PTR(krb5_change_password);\r
-extern DECL_FUNC_PTR(krb5_get_init_creds_opt_init);\r
-extern DECL_FUNC_PTR(krb5_get_init_creds_opt_set_tkt_life);\r
-extern DECL_FUNC_PTR(krb5_get_init_creds_opt_set_renew_life);\r
-extern DECL_FUNC_PTR(krb5_get_init_creds_opt_set_forwardable);\r
-extern DECL_FUNC_PTR(krb5_get_init_creds_opt_set_proxiable);\r
-extern DECL_FUNC_PTR(krb5_get_init_creds_opt_set_renew_life);\r
-extern DECL_FUNC_PTR(krb5_get_init_creds_opt_set_address_list);\r
-extern DECL_FUNC_PTR(krb5_get_init_creds_opt_set_change_password_prompt);\r
-extern DECL_FUNC_PTR(krb5_get_init_creds_password);\r
-extern DECL_FUNC_PTR(krb5_get_prompt_types);\r
-extern DECL_FUNC_PTR(krb5_build_principal_ext);\r
-extern DECL_FUNC_PTR(krb5_cc_get_name);\r
-extern DECL_FUNC_PTR(krb5_cc_get_type);\r
-extern DECL_FUNC_PTR(krb5_cc_resolve);\r
-extern DECL_FUNC_PTR(krb5_cc_default);\r
-extern DECL_FUNC_PTR(krb5_cc_default_name);\r
-extern DECL_FUNC_PTR(krb5_cc_set_default_name);\r
-extern DECL_FUNC_PTR(krb5_cc_initialize);\r
-extern DECL_FUNC_PTR(krb5_cc_destroy);\r
-extern DECL_FUNC_PTR(krb5_cc_close);\r
-extern DECL_FUNC_PTR(krb5_cc_copy_creds);\r
-extern DECL_FUNC_PTR(krb5_cc_store_cred);\r
-extern DECL_FUNC_PTR(krb5_cc_retrieve_cred);\r
-extern DECL_FUNC_PTR(krb5_cc_get_principal);\r
-extern DECL_FUNC_PTR(krb5_cc_start_seq_get);\r
-extern DECL_FUNC_PTR(krb5_cc_next_cred);\r
-extern DECL_FUNC_PTR(krb5_cc_end_seq_get);\r
-extern DECL_FUNC_PTR(krb5_cc_remove_cred);\r
-extern DECL_FUNC_PTR(krb5_cc_set_flags);\r
-// extern DECL_FUNC_PTR(krb5_cc_get_type);\r
-extern DECL_FUNC_PTR(krb5_free_context);\r
-extern DECL_FUNC_PTR(krb5_free_cred_contents);\r
-extern DECL_FUNC_PTR(krb5_free_principal);\r
-extern DECL_FUNC_PTR(krb5_get_in_tkt_with_password);\r
-extern DECL_FUNC_PTR(krb5_init_context);\r
-extern DECL_FUNC_PTR(krb5_parse_name);\r
-extern DECL_FUNC_PTR(krb5_timeofday);\r
-extern DECL_FUNC_PTR(krb5_timestamp_to_sfstring);\r
-extern DECL_FUNC_PTR(krb5_unparse_name);\r
-extern DECL_FUNC_PTR(krb5_get_credentials);\r
-extern DECL_FUNC_PTR(krb5_mk_req);\r
-extern DECL_FUNC_PTR(krb5_sname_to_principal);\r
-extern DECL_FUNC_PTR(krb5_get_credentials_renew);\r
-extern DECL_FUNC_PTR(krb5_free_data);\r
-extern DECL_FUNC_PTR(krb5_free_data_contents);\r
-// extern DECL_FUNC_PTR(krb5_get_realm_domain);\r
-extern DECL_FUNC_PTR(krb5_free_unparsed_name);\r
-extern DECL_FUNC_PTR(krb5_os_localaddr);\r
-extern DECL_FUNC_PTR(krb5_copy_keyblock_contents);\r
-extern DECL_FUNC_PTR(krb5_copy_data);\r
-extern DECL_FUNC_PTR(krb5_free_creds);\r
-extern DECL_FUNC_PTR(krb5_build_principal);\r
-extern DECL_FUNC_PTR(krb5_get_renewed_creds);\r
-extern DECL_FUNC_PTR(krb5_free_addresses);\r
-extern DECL_FUNC_PTR(krb5_get_default_config_files);\r
-extern DECL_FUNC_PTR(krb5_free_config_files);\r
-extern DECL_FUNC_PTR(krb5_get_default_realm);\r
-extern DECL_FUNC_PTR(krb5_set_default_realm);\r
-extern DECL_FUNC_PTR(krb5_free_ticket);\r
-extern DECL_FUNC_PTR(krb5_decode_ticket);\r
-extern DECL_FUNC_PTR(krb5_get_host_realm);\r
-extern DECL_FUNC_PTR(krb5_free_host_realm);\r
-extern DECL_FUNC_PTR(krb5_c_random_make_octets);\r
-extern DECL_FUNC_PTR(krb5_free_default_realm);\r
-extern DECL_FUNC_PTR(krb5_string_to_deltat);\r
-\r
-// Krb524 functions\r
-extern DECL_FUNC_PTR(krb524_init_ets);\r
-extern DECL_FUNC_PTR(krb524_convert_creds_kdc);\r
-\r
-// ComErr functions\r
-extern DECL_FUNC_PTR(com_err);\r
-extern DECL_FUNC_PTR(error_message);\r
-\r
-// Profile functions\r
-extern DECL_FUNC_PTR(profile_init);\r
-extern DECL_FUNC_PTR(profile_flush);\r
-extern DECL_FUNC_PTR(profile_release);\r
-extern DECL_FUNC_PTR(profile_get_subsection_names);\r
-extern DECL_FUNC_PTR(profile_free_list);\r
-extern DECL_FUNC_PTR(profile_get_string);\r
-extern DECL_FUNC_PTR(profile_get_integer);\r
-extern DECL_FUNC_PTR(profile_get_values);\r
-extern DECL_FUNC_PTR(profile_get_relation_names);\r
-extern DECL_FUNC_PTR(profile_clear_relation);\r
-extern DECL_FUNC_PTR(profile_add_relation);\r
-extern DECL_FUNC_PTR(profile_update_relation);\r
-extern DECL_FUNC_PTR(profile_release_string);\r
-extern DECL_FUNC_PTR(profile_rename_section);\r
-\r
-// Service functions\r
-extern DECL_FUNC_PTR(OpenSCManagerA);\r
-extern DECL_FUNC_PTR(OpenServiceA);\r
-extern DECL_FUNC_PTR(QueryServiceStatus);\r
-extern DECL_FUNC_PTR(CloseServiceHandle);\r
-extern DECL_FUNC_PTR(LsaNtStatusToWinError);\r
-\r
-// LSA Functions\r
-extern DECL_FUNC_PTR(LsaConnectUntrusted);\r
-extern DECL_FUNC_PTR(LsaLookupAuthenticationPackage);\r
-extern DECL_FUNC_PTR(LsaCallAuthenticationPackage);\r
-extern DECL_FUNC_PTR(LsaFreeReturnBuffer);\r
-extern DECL_FUNC_PTR(LsaGetLogonSessionData);\r
-\r
-// toolhelp functions\r
-TYPEDEF_FUNC(\r
-    HANDLE,\r
-    WINAPI,\r
-    CreateToolhelp32Snapshot,\r
-    (DWORD, DWORD)\r
-    );\r
-TYPEDEF_FUNC(\r
-    BOOL,\r
-    WINAPI,\r
-    Module32First,\r
-    (HANDLE, LPMODULEENTRY32)\r
-    );\r
-TYPEDEF_FUNC(\r
-    BOOL,\r
-    WINAPI,\r
-    Module32Next,\r
-    (HANDLE, LPMODULEENTRY32)\r
-    );\r
-\r
-// psapi functions\r
-TYPEDEF_FUNC(\r
-    DWORD,\r
-    WINAPI,\r
-    GetModuleFileNameExA,\r
-    (HANDLE, HMODULE, LPSTR, DWORD)\r
-    );\r
-\r
-TYPEDEF_FUNC(\r
-    BOOL,\r
-    WINAPI,\r
-    EnumProcessModules,\r
-    (HANDLE, HMODULE*, DWORD, LPDWORD)\r
-    );\r
-\r
-#define pGetModuleFileNameEx pGetModuleFileNameExA\r
-#define TOOLHELPDLL "kernel32.dll"\r
-#define PSAPIDLL "psapi.dll"\r
-\r
-// psapi functions\r
-extern DECL_FUNC_PTR(GetModuleFileNameExA);\r
-extern DECL_FUNC_PTR(EnumProcessModules);\r
-\r
-// toolhelp functions\r
-extern DECL_FUNC_PTR(CreateToolhelp32Snapshot);\r
-extern DECL_FUNC_PTR(Module32First);\r
-extern DECL_FUNC_PTR(Module32Next);\r
-\r
-khm_int32 init_imports(void);\r
-khm_int32 exit_imports(void);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+
+#if _WIN32_WINNT < 0x0501
+#define KHM_SAVE_WIN32_WINNT _WIN32_WINNT
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501
+#endif
+#include<ntsecapi.h>
+#ifdef KHM_SAVE_WIN32_WINNT
+#undef _WIN32_WINNT
+#define _WIN32_WINNT KHM_SAVE_WIN32_WINNT
+#undef KHM_SAVE_WIN32_WINNT
+#endif
+
+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_opt_set_change_password_prompt);
+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_get_type);
+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_set_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);
+extern DECL_FUNC_PTR(krb5_string_to_deltat);
+
+// 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_flush);
+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_get_integer);
+extern DECL_FUNC_PTR(profile_get_values);
+extern DECL_FUNC_PTR(profile_get_relation_names);
+extern DECL_FUNC_PTR(profile_clear_relation);
+extern DECL_FUNC_PTR(profile_add_relation);
+extern DECL_FUNC_PTR(profile_update_relation);
+extern DECL_FUNC_PTR(profile_release_string);
+extern DECL_FUNC_PTR(profile_rename_section);
+
+// 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
index 6c39586945f3dc6490096d8a8f44e5d477fbb8d9..1278fbcfa7d8c7c5eeca67baee3262dd0324f6f2 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<windows.h>\r
-#include<netidmgr.h>\r
-#include<dynimport.h>\r
-#include<krb5common.h>\r
-#ifdef DEBUG\r
-#include<assert.h>\r
-#endif\r
-#include<strsafe.h>\r
-\r
-/**************************************/\r
-/* khm_krb5_error():           */\r
-/**************************************/\r
-int \r
-khm_krb5_error(krb5_error_code rc, LPCSTR FailedFunctionName, \r
-                 int FreeContextFlag, krb5_context * ctx, \r
-                 krb5_ccache * cache)\r
-{\r
-#ifdef NO_KRB5\r
-    return 0;\r
-#else\r
-\r
-#ifdef SHOW_MESSAGE_IN_AN_ANNOYING_WAY\r
-    char message[256];\r
-    const char *errText;\r
-    int krb5Error = ((int)(rc & 255));  \r
-\r
-    errText = perror_message(rc);   \r
-    _snprintf(message, sizeof(message), \r
-        "%s\n(Kerberos error %ld)\n\n%s failed", \r
-        errText, \r
-        krb5Error, \r
-        FailedFunctionName);\r
-\r
-    MessageBoxA(NULL, message, "Kerberos Five", MB_OK | MB_ICONERROR | \r
-        MB_TASKMODAL | \r
-        MB_SETFOREGROUND);\r
-#endif\r
-\r
-    if (FreeContextFlag == 1)\r
-    {\r
-        if (*ctx != NULL)\r
-        {\r
-            if (*cache != NULL) {\r
-                pkrb5_cc_close(*ctx, *cache);\r
-                *cache = NULL;\r
-            }\r
-\r
-            pkrb5_free_context(*ctx);\r
-            *ctx = NULL;\r
-        }\r
-    }\r
-\r
-    return rc;\r
-\r
-#endif //!NO_KRB5\r
-}\r
-\r
-int \r
-khm_krb5_initialize(khm_handle ident, \r
-                    krb5_context *ctx, \r
-                    krb5_ccache *cache)\r
-{\r
-#ifdef NO_KRB5\r
-    return(0);\r
-#else\r
-\r
-    LPCSTR          functionName = NULL;\r
-    int             freeContextFlag = 0;\r
-    krb5_error_code    rc = 0;\r
-    krb5_flags          flags = 0;\r
-\r
-    if (pkrb5_init_context == NULL)\r
-        return 1;\r
-\r
-    if (*ctx == 0 && (rc = (*pkrb5_init_context)(ctx))) {\r
-        functionName = "krb5_init_context()";\r
-        freeContextFlag = 0;\r
-        goto on_error;\r
-    }\r
-\r
-    if(*cache == 0) {\r
-        wchar_t wccname[MAX_PATH];\r
-        khm_size cbwccname;\r
-\r
-        if(ident != NULL) {\r
-            cbwccname = sizeof(wccname);\r
-            do {\r
-                char ccname[256];\r
-\r
-                if(KHM_FAILED(kcdb_identity_get_attrib(ident, L"Krb5CCName",\r
-                                                       NULL, wccname,\r
-                                                       &cbwccname))) {\r
-                    cbwccname = sizeof(wccname);\r
-                    if (KHM_FAILED\r
-                        (khm_krb5_find_ccache_for_identity(ident,\r
-                                                           ctx,\r
-                                                           wccname,\r
-                                                           &cbwccname))) {\r
-#ifdef DEBUG_LIKE_A_MADMAN\r
-                        assert(FALSE);\r
-#endif\r
-                        break;\r
-                    }\r
-                }\r
-\r
-                if(UnicodeStrToAnsi(ccname, sizeof(ccname), wccname) == 0)\r
-                    break;\r
-\r
-                if((*pkrb5_cc_resolve)(*ctx, ccname, cache)) {\r
-                    functionName = "krb5_cc_resolve()";\r
-                    freeContextFlag = 1;\r
-                    goto on_error;\r
-                }\r
-            } while(FALSE);\r
-        }\r
-\r
-#ifndef FAILOVER_TO_DEFAULT_CCACHE\r
-       rc = 1;\r
-#endif\r
-        if (*cache == 0\r
-#ifdef FAILOVER_TO_DEFAULT_CCACHE\r
-            && (rc = (*pkrb5_cc_default)(*ctx, cache))\r
-#endif\r
-            ) {\r
-            functionName = "krb5_cc_default()";\r
-            freeContextFlag = 1;\r
-            goto on_error;\r
-        }\r
-    }\r
-\r
-#ifdef KRB5_TC_NOTICKET\r
-    flags = KRB5_TC_NOTICKET;\r
-#endif\r
-\r
-    if ((rc = (*pkrb5_cc_set_flags)(*ctx, *cache, flags)))\r
-    {\r
-        if (rc != KRB5_FCC_NOFILE && rc != KRB5_CC_NOTFOUND)\r
-            khm_krb5_error(rc, "krb5_cc_set_flags()", 0, ctx, \r
-            cache);\r
-        else if ((rc == KRB5_FCC_NOFILE || rc == KRB5_CC_NOTFOUND) && *ctx != NULL) {\r
-            if (*cache != NULL) {\r
-                (*pkrb5_cc_close)(*ctx, *cache);\r
-                *cache = NULL;\r
-            }\r
-        }\r
-        return rc;\r
-    }\r
-    return 0;\r
-\r
-on_error:\r
-    return khm_krb5_error(rc, functionName, freeContextFlag, ctx, cache);\r
-#endif //!NO_KRB5\r
-}\r
-\r
-#define TIMET_TOLERANCE (60*5)\r
-\r
-khm_int32 KHMAPI\r
-khm_get_identity_expiration_time(krb5_context ctx, krb5_ccache cc, \r
-                                 khm_handle ident, \r
-                                 krb5_timestamp * pexpiration)\r
-{\r
-    krb5_principal principal = 0;\r
-    char * princ_name = NULL;\r
-    krb5_creds creds;\r
-    krb5_error_code code;\r
-    krb5_error_code cc_code;\r
-    krb5_cc_cursor cur;\r
-    krb5_timestamp now, expiration = 0;\r
-\r
-    wchar_t w_ident_name[KCDB_IDENT_MAXCCH_NAME];\r
-    char    ident_name[KCDB_IDENT_MAXCCH_NAME];\r
-    khm_size cb;\r
-\r
-    khm_int32 rv = KHM_ERROR_NOT_FOUND;\r
-\r
-    if (!ctx || !cc || !ident || !pexpiration)\r
-        return KHM_ERROR_GENERAL;\r
-\r
-    code = pkrb5_cc_get_principal(ctx, cc, &principal);\r
-\r
-    if ( code )\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cb = sizeof(w_ident_name);\r
-    kcdb_identity_get_name(ident, w_ident_name, &cb);\r
-    UnicodeStrToAnsi(ident_name, sizeof(ident_name), w_ident_name);\r
-\r
-    code = pkrb5_unparse_name(ctx, principal, &princ_name);\r
-\r
-    /* compare principal to ident. */\r
-\r
-    if ( code || !princ_name ||\r
-         strcmp(princ_name, ident_name) ) {\r
-        if (princ_name)\r
-            pkrb5_free_unparsed_name(ctx, princ_name);\r
-        pkrb5_free_principal(ctx, principal);\r
-        return KHM_ERROR_UNKNOWN;\r
-    }\r
-\r
-    pkrb5_free_unparsed_name(ctx, princ_name);\r
-    pkrb5_free_principal(ctx, principal);\r
-\r
-    code = pkrb5_timeofday(ctx, &now);\r
-\r
-    if (code)\r
-        return KHM_ERROR_UNKNOWN;\r
-\r
-    cc_code = pkrb5_cc_start_seq_get(ctx, cc, &cur);\r
-\r
-    while (!(cc_code = pkrb5_cc_next_cred(ctx, cc, &cur, &creds))) {\r
-        krb5_data * c0 = krb5_princ_name(ctx, creds.server);\r
-        krb5_data * c1  = krb5_princ_component(ctx, creds.server, 1);\r
-        krb5_data * r = krb5_princ_realm(ctx, creds.server);\r
-\r
-        if ( c0 && c1 && r && c1->length == r->length && \r
-             !strncmp(c1->data,r->data,r->length) &&\r
-             !strncmp("krbtgt",c0->data,c0->length) ) {\r
-\r
-            /* we have a TGT, check for the expiration time.\r
-             * if it is valid and renewable, use the renew time \r
-             */\r
-\r
-            if (!(creds.ticket_flags & TKT_FLG_INVALID) &&\r
-                creds.times.starttime < (now + TIMET_TOLERANCE) && \r
-                (creds.times.endtime + TIMET_TOLERANCE) > now) {\r
-                expiration = creds.times.endtime;\r
-\r
-                if ((creds.ticket_flags & TKT_FLG_RENEWABLE) && \r
-                    (creds.times.renew_till > creds.times.endtime)) {\r
-                    expiration = creds.times.renew_till;\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-    if (cc_code == KRB5_CC_END) {\r
-        cc_code = pkrb5_cc_end_seq_get(ctx, cc, &cur);\r
-        rv = KHM_ERROR_SUCCESS;\r
-        *pexpiration = expiration;\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-khm_int32 KHMAPI\r
-khm_krb5_find_ccache_for_identity(khm_handle ident, krb5_context *pctx,\r
-                                  void * buffer, khm_size * pcbbuf)\r
-{\r
-    krb5_context        ctx = 0;\r
-    krb5_ccache         cache = 0;\r
-    krb5_error_code     code;\r
-    apiCB *             cc_ctx = 0;\r
-    struct _infoNC **   pNCi = NULL;\r
-    int                 i;\r
-    khm_int32           t;\r
-    wchar_t *           ms = NULL;\r
-    khm_size            cb;\r
-    krb5_timestamp      expiration = 0;\r
-    krb5_timestamp      best_match_expiration = 0;\r
-    char                best_match_ccname[256] = "";\r
-    khm_handle          csp_params = NULL;\r
-    khm_handle          csp_plugins = NULL;\r
-\r
-    if (!buffer || !pcbbuf)\r
-    return KHM_ERROR_GENERAL;\r
-\r
-    ctx = *pctx;\r
-\r
-    if (!pcc_initialize ||\r
-        !pcc_get_NC_info ||\r
-        !pcc_free_NC_info ||\r
-        !pcc_shutdown)\r
-        goto _skip_cc_iter;\r
-\r
-    code = pcc_initialize(&cc_ctx, CC_API_VER_2, NULL, NULL);\r
-    if (code)\r
-        goto _exit;\r
-\r
-    code = pcc_get_NC_info(cc_ctx, &pNCi);\r
-\r
-    if (code) \r
-        goto _exit;\r
-\r
-    for(i=0; pNCi[i]; i++) {\r
-        if (pNCi[i]->vers != CC_CRED_V5)\r
-            continue;\r
-\r
-        code = (*pkrb5_cc_resolve)(ctx, pNCi[i]->name, &cache);\r
-        if (code)\r
-            continue;\r
-\r
-        /* need a function to check the cache for the identity\r
-         * and determine if it has valid tickets.  If it has \r
-         * the right identity and valid tickets, store the \r
-         * expiration time and the cache name.  If it has the\r
-         * right identity but no valid tickets, store the ccache\r
-         * name and an expiration time of zero.  if it does not\r
-         * have the right identity don't save the name.\r
-         * \r
-         * Keep searching to find the best cache available.\r
-         */\r
-\r
-        if (KHM_SUCCEEDED(khm_get_identity_expiration_time(ctx, cache, \r
-                                                           ident, \r
-                                                           &expiration))) {\r
-            if ( expiration > best_match_expiration ) {\r
-                best_match_expiration = expiration;\r
-                StringCbCopyA(best_match_ccname, \r
-                              sizeof(best_match_ccname),\r
-                              "API:");\r
-                StringCbCatA(best_match_ccname,\r
-                             sizeof(best_match_ccname),\r
-                             pNCi[i]->name);\r
-                expiration = 0;\r
-            }\r
-        }\r
-\r
-        if(ctx != NULL && cache != NULL)\r
-            (*pkrb5_cc_close)(ctx, cache);\r
-        cache = 0;\r
-    }\r
-\r
- _skip_cc_iter:\r
-\r
-    if (KHM_SUCCEEDED(kmm_get_plugins_config(0, &csp_plugins))) {\r
-        khc_open_space(csp_plugins, L"Krb5Cred\\Parameters",  0, &csp_params);\r
-        khc_close_space(csp_plugins);\r
-        csp_plugins = NULL;\r
-    }\r
-\r
-#ifdef DEBUG\r
-    if (csp_params == NULL) {\r
-        assert(FALSE);\r
-    }\r
-#endif\r
-\r
-    if (csp_params &&\r
-        KHM_SUCCEEDED(khc_read_int32(csp_params, L"MsLsaList", &t)) && t) {\r
-        code = (*pkrb5_cc_resolve)(ctx, "MSLSA:", &cache);\r
-        if (code == 0 && cache) {\r
-            if (KHM_SUCCEEDED(khm_get_identity_expiration_time(ctx, cache, \r
-                                                               ident, \r
-                                                               &expiration))) {\r
-                if ( expiration > best_match_expiration ) {\r
-                    best_match_expiration = expiration;\r
-                    StringCbCopyA(best_match_ccname, sizeof(best_match_ccname),\r
-                                  "MSLSA:");\r
-                    expiration = 0;\r
-                }\r
-            }\r
-        }\r
-\r
-        if (ctx != NULL && cache != NULL)\r
-            (*pkrb5_cc_close)(ctx, cache);\r
-\r
-        cache = 0;\r
-    }\r
-\r
-    if (csp_params &&\r
-        khc_read_multi_string(csp_params, L"FileCCList", NULL, &cb)\r
-        == KHM_ERROR_TOO_LONG &&\r
-        cb > sizeof(wchar_t) * 2) {\r
-\r
-        wchar_t * t;\r
-        char ccname[MAX_PATH + 6];\r
-\r
-        ms = PMALLOC(cb);\r
-\r
-#ifdef DEBUG\r
-        assert(ms);\r
-#endif\r
-\r
-        khc_read_multi_string(csp_params, L"FileCCList", ms, &cb);\r
-        for(t = ms; t && *t; t = multi_string_next(t)) {\r
-            StringCchPrintfA(ccname, ARRAYLENGTH(ccname),\r
-                             "FILE:%S", t);\r
-\r
-            code = (*pkrb5_cc_resolve)(ctx, ccname, &cache);\r
-            if (code)\r
-                continue;\r
-\r
-            if (KHM_SUCCEEDED(khm_get_identity_expiration_time(ctx, cache, \r
-                                                               ident, \r
-                                                               &expiration))) {\r
-                if ( expiration > best_match_expiration ) {\r
-                    best_match_expiration = expiration;\r
-                    StringCbCopyA(best_match_ccname,\r
-                                  sizeof(best_match_ccname),\r
-                                  ccname);\r
-                    expiration = 0;\r
-                }\r
-            }\r
-\r
-            if (ctx != NULL && cache != NULL)\r
-                (*pkrb5_cc_close)(ctx, cache);\r
-            cache = 0;\r
-        }\r
-\r
-        PFREE(ms);\r
-    }\r
- _exit:\r
-    if (csp_params)\r
-        khc_close_space(csp_params);\r
-\r
-    if (pNCi)\r
-        (*pcc_free_NC_info)(cc_ctx, &pNCi);\r
-\r
-    if (cc_ctx)\r
-        (*pcc_shutdown)(&cc_ctx);\r
-\r
-    if (best_match_ccname[0]) {\r
-        \r
-        if (*pcbbuf = AnsiStrToUnicode((wchar_t *)buffer, \r
-                                       *pcbbuf,\r
-                                       best_match_ccname)) {\r
-\r
-            *pcbbuf = (*pcbbuf + 1) * sizeof(wchar_t);\r
-\r
-            return KHM_ERROR_SUCCESS;\r
-        }\r
-\r
-    }\r
-\r
-    return KHM_ERROR_GENERAL;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<netidmgr.h>
+#include<dynimport.h>
+#include<krb5common.h>
+#ifdef DEBUG
+#include<assert.h>
+#endif
+#include<strsafe.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 = NULL;
+    int             freeContextFlag = 0;
+    krb5_error_code    rc = 0;
+    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[MAX_PATH];
+        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))) {
+                    cbwccname = sizeof(wccname);
+                    if (KHM_FAILED
+                        (khm_krb5_find_ccache_for_identity(ident,
+                                                           ctx,
+                                                           wccname,
+                                                           &cbwccname))) {
+#ifdef DEBUG_LIKE_A_MADMAN
+                        assert(FALSE);
+#endif
+                        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);
+        }
+
+#ifndef FAILOVER_TO_DEFAULT_CCACHE
+       rc = 1;
+#endif
+        if (*cache == 0
+#ifdef FAILOVER_TO_DEFAULT_CCACHE
+            && (rc = (*pkrb5_cc_default)(*ctx, cache))
+#endif
+            ) {
+            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);
+                *cache = NULL;
+            }
+        }
+        return rc;
+    }
+    return 0;
+
+on_error:
+    return khm_krb5_error(rc, functionName, freeContextFlag, ctx, cache);
+#endif //!NO_KRB5
+}
+
+#define TIMET_TOLERANCE (60*5)
+
+khm_int32 KHMAPI
+khm_get_identity_expiration_time(krb5_context ctx, krb5_ccache cc, 
+                                 khm_handle ident, 
+                                 krb5_timestamp * pexpiration)
+{
+    krb5_principal principal = 0;
+    char * princ_name = NULL;
+    krb5_creds creds;
+    krb5_error_code code;
+    krb5_error_code cc_code;
+    krb5_cc_cursor cur;
+    krb5_timestamp now, expiration = 0;
+
+    wchar_t w_ident_name[KCDB_IDENT_MAXCCH_NAME];
+    char    ident_name[KCDB_IDENT_MAXCCH_NAME];
+    khm_size cb;
+
+    khm_int32 rv = KHM_ERROR_NOT_FOUND;
+
+    if (!ctx || !cc || !ident || !pexpiration)
+        return KHM_ERROR_GENERAL;
+
+    code = pkrb5_cc_get_principal(ctx, cc, &principal);
+
+    if ( code )
+        return KHM_ERROR_INVALID_PARAM;
+
+    cb = sizeof(w_ident_name);
+    kcdb_identity_get_name(ident, w_ident_name, &cb);
+    UnicodeStrToAnsi(ident_name, sizeof(ident_name), w_ident_name);
+
+    code = pkrb5_unparse_name(ctx, principal, &princ_name);
+
+    /* compare principal to ident. */
+
+    if ( code || !princ_name ||
+         strcmp(princ_name, ident_name) ) {
+        if (princ_name)
+            pkrb5_free_unparsed_name(ctx, princ_name);
+        pkrb5_free_principal(ctx, principal);
+        return KHM_ERROR_UNKNOWN;
+    }
+
+    pkrb5_free_unparsed_name(ctx, princ_name);
+    pkrb5_free_principal(ctx, principal);
+
+    code = pkrb5_timeofday(ctx, &now);
+
+    if (code)
+        return KHM_ERROR_UNKNOWN;
+
+    cc_code = pkrb5_cc_start_seq_get(ctx, cc, &cur);
+
+    while (!(cc_code = pkrb5_cc_next_cred(ctx, cc, &cur, &creds))) {
+        krb5_data * c0 = krb5_princ_name(ctx, creds.server);
+        krb5_data * c1  = krb5_princ_component(ctx, creds.server, 1);
+        krb5_data * r = krb5_princ_realm(ctx, creds.server);
+
+        if ( c0 && c1 && r && c1->length == r->length && 
+             !strncmp(c1->data,r->data,r->length) &&
+             !strncmp("krbtgt",c0->data,c0->length) ) {
+
+            /* we have a TGT, check for the expiration time.
+             * if it is valid and renewable, use the renew time 
+             */
+
+            if (!(creds.ticket_flags & TKT_FLG_INVALID) &&
+                creds.times.starttime < (now + TIMET_TOLERANCE) && 
+                (creds.times.endtime + TIMET_TOLERANCE) > now) {
+                expiration = creds.times.endtime;
+
+                if ((creds.ticket_flags & TKT_FLG_RENEWABLE) && 
+                    (creds.times.renew_till > creds.times.endtime)) {
+                    expiration = creds.times.renew_till;
+                }
+            }
+        }
+    }
+
+    if (cc_code == KRB5_CC_END) {
+        cc_code = pkrb5_cc_end_seq_get(ctx, cc, &cur);
+        rv = KHM_ERROR_SUCCESS;
+        *pexpiration = expiration;
+    }
+
+    return rv;
+}
+
+khm_int32 KHMAPI
+khm_krb5_find_ccache_for_identity(khm_handle ident, krb5_context *pctx,
+                                  void * buffer, khm_size * pcbbuf)
+{
+    krb5_context        ctx = 0;
+    krb5_ccache         cache = 0;
+    krb5_error_code     code;
+    apiCB *             cc_ctx = 0;
+    struct _infoNC **   pNCi = NULL;
+    int                 i;
+    khm_int32           t;
+    wchar_t *           ms = NULL;
+    khm_size            cb;
+    krb5_timestamp      expiration = 0;
+    krb5_timestamp      best_match_expiration = 0;
+    char                best_match_ccname[256] = "";
+    khm_handle          csp_params = NULL;
+    khm_handle          csp_plugins = NULL;
+
+    if (!buffer || !pcbbuf)
+    return KHM_ERROR_GENERAL;
+
+    ctx = *pctx;
+
+    if (!pcc_initialize ||
+        !pcc_get_NC_info ||
+        !pcc_free_NC_info ||
+        !pcc_shutdown)
+        goto _skip_cc_iter;
+
+    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;
+
+    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;
+
+        /* need a function to check the cache for the identity
+         * and determine if it has valid tickets.  If it has 
+         * the right identity and valid tickets, store the 
+         * expiration time and the cache name.  If it has the
+         * right identity but no valid tickets, store the ccache
+         * name and an expiration time of zero.  if it does not
+         * have the right identity don't save the name.
+         * 
+         * Keep searching to find the best cache available.
+         */
+
+        if (KHM_SUCCEEDED(khm_get_identity_expiration_time(ctx, cache, 
+                                                           ident, 
+                                                           &expiration))) {
+            if ( expiration > best_match_expiration ) {
+                best_match_expiration = expiration;
+                StringCbCopyA(best_match_ccname, 
+                              sizeof(best_match_ccname),
+                              "API:");
+                StringCbCatA(best_match_ccname,
+                             sizeof(best_match_ccname),
+                             pNCi[i]->name);
+                expiration = 0;
+            }
+        }
+
+        if(ctx != NULL && cache != NULL)
+            (*pkrb5_cc_close)(ctx, cache);
+        cache = 0;
+    }
+
+ _skip_cc_iter:
+
+    if (KHM_SUCCEEDED(kmm_get_plugins_config(0, &csp_plugins))) {
+        khc_open_space(csp_plugins, L"Krb5Cred\\Parameters",  0, &csp_params);
+        khc_close_space(csp_plugins);
+        csp_plugins = NULL;
+    }
+
+#ifdef DEBUG
+    if (csp_params == NULL) {
+        assert(FALSE);
+    }
+#endif
+
+    if (csp_params &&
+        KHM_SUCCEEDED(khc_read_int32(csp_params, L"MsLsaList", &t)) && t) {
+        code = (*pkrb5_cc_resolve)(ctx, "MSLSA:", &cache);
+        if (code == 0 && cache) {
+            if (KHM_SUCCEEDED(khm_get_identity_expiration_time(ctx, cache, 
+                                                               ident, 
+                                                               &expiration))) {
+                if ( expiration > best_match_expiration ) {
+                    best_match_expiration = expiration;
+                    StringCbCopyA(best_match_ccname, sizeof(best_match_ccname),
+                                  "MSLSA:");
+                    expiration = 0;
+                }
+            }
+        }
+
+        if (ctx != NULL && cache != NULL)
+            (*pkrb5_cc_close)(ctx, cache);
+
+        cache = 0;
+    }
+
+    if (csp_params &&
+        khc_read_multi_string(csp_params, L"FileCCList", NULL, &cb)
+        == KHM_ERROR_TOO_LONG &&
+        cb > sizeof(wchar_t) * 2) {
+
+        wchar_t * t;
+        char ccname[MAX_PATH + 6];
+
+        ms = PMALLOC(cb);
+
+#ifdef DEBUG
+        assert(ms);
+#endif
+
+        khc_read_multi_string(csp_params, L"FileCCList", ms, &cb);
+        for(t = ms; t && *t; t = multi_string_next(t)) {
+            StringCchPrintfA(ccname, ARRAYLENGTH(ccname),
+                             "FILE:%S", t);
+
+            code = (*pkrb5_cc_resolve)(ctx, ccname, &cache);
+            if (code)
+                continue;
+
+            if (KHM_SUCCEEDED(khm_get_identity_expiration_time(ctx, cache, 
+                                                               ident, 
+                                                               &expiration))) {
+                if ( expiration > best_match_expiration ) {
+                    best_match_expiration = expiration;
+                    StringCbCopyA(best_match_ccname,
+                                  sizeof(best_match_ccname),
+                                  ccname);
+                    expiration = 0;
+                }
+            }
+
+            if (ctx != NULL && cache != NULL)
+                (*pkrb5_cc_close)(ctx, cache);
+            cache = 0;
+        }
+
+        PFREE(ms);
+    }
+ _exit:
+    if (csp_params)
+        khc_close_space(csp_params);
+
+    if (pNCi)
+        (*pcc_free_NC_info)(cc_ctx, &pNCi);
+
+    if (cc_ctx)
+        (*pcc_shutdown)(&cc_ctx);
+
+    if (best_match_ccname[0]) {
+        
+        if (*pcbbuf = AnsiStrToUnicode((wchar_t *)buffer, 
+                                       *pcbbuf,
+                                       best_match_ccname)) {
+
+            *pcbbuf = (*pcbbuf + 1) * sizeof(wchar_t);
+
+            return KHM_ERROR_SUCCESS;
+        }
+
+    }
+
+    return KHM_ERROR_GENERAL;
+}
index df3db93ae14f5a67fd3915188bbbd0f8a9ccf171..29cae71ee98cd4277533469c00cf23f1e8f98b6c 100644 (file)
@@ -1,56 +1,56 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/* Adapted from multiple Leash header files */\r
-\r
-#ifndef __KHIMAIRA_KRB5COMMON_H\r
-#define __KHIMAIRA_KRB5COMMON_H\r
-\r
-#include<krb5.h>\r
-\r
-#ifndef NO_KRB5\r
-int khm_krb5_error(krb5_error_code rc, LPCSTR FailedFunctionName, \r
-                   int FreeContextFlag, krb5_context *ctx,\r
-                   krb5_ccache *cache);\r
-\r
-int\r
-khm_krb5_get_error_string(krb5_error_code rc,\r
-                          wchar_t * buffer,\r
-                          khm_size cb_buffer);\r
-\r
-int khm_krb5_initialize(khm_handle ident, krb5_context *, krb5_ccache *);\r
-\r
-khm_int32 KHMAPI\r
-khm_krb5_find_ccache_for_identity(khm_handle ident, krb5_context *pctx,\r
-                                  void * buffer, khm_size * pcbbuf);\r
-\r
-khm_int32 KHMAPI\r
-khm_get_identity_expiration_time(krb5_context ctx, krb5_ccache cc, \r
-                                 khm_handle ident, \r
-                                 krb5_timestamp * pexpiration);\r
-#endif /* NO_KRB5 */\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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_get_error_string(krb5_error_code rc,
+                          wchar_t * buffer,
+                          khm_size cb_buffer);
+
+int khm_krb5_initialize(khm_handle ident, krb5_context *, krb5_ccache *);
+
+khm_int32 KHMAPI
+khm_krb5_find_ccache_for_identity(khm_handle ident, krb5_context *pctx,
+                                  void * buffer, khm_size * pcbbuf);
+
+khm_int32 KHMAPI
+khm_get_identity_expiration_time(krb5_context ctx, krb5_ccache cc, 
+                                 khm_handle ident, 
+                                 krb5_timestamp * pexpiration);
+#endif /* NO_KRB5 */
+
+#endif
index f1aa63d88b87806c173e4b97e7966524e02f94c7..f436a40a7e455d2aa8839817bf6f573e17483347 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<krbcred.h>\r
-#include<kherror.h>\r
-\r
-#include<strsafe.h>\r
-\r
-extern void (__cdecl *pinitialize_krb_error_func)();\r
-extern void (__cdecl *pinitialize_kadm_error_table)();\r
-\r
-\r
-khm_int32 init_error_funcs()\r
-{\r
-\r
-#if 0\r
-    /*TODO: Do something about this */\r
-    if (plsh_LoadKrb4LeashErrorTables)\r
-            plsh_LoadKrb4LeashErrorTables(hLeashInst, 0);\r
-#endif\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-khm_int32 exit_error_funcs()\r
-{\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-// Global Variables.\r
-static long lsh_errno;\r
-static char *err_context;       /* error context */\r
-extern int (*Lcom_err)(LPSTR,long,LPSTR,...);\r
-extern LPSTR (*Lerror_message)(long);\r
-extern LPSTR (*Lerror_table_name)(long);\r
-\r
-#ifdef WIN16\r
-#define UNDERSCORE "_"\r
-#else\r
-#define UNDERSCORE\r
-#endif\r
-\r
-HWND GetRootParent (HWND Child)\r
-{\r
-    HWND Last = NULL;\r
-    while (Child)\r
-    {\r
-        Last = Child;\r
-        Child = GetParent (Child);\r
-    }\r
-    return Last;\r
-}\r
-\r
-\r
-LPSTR err_describe(LPSTR buf, size_t len, long code)\r
-{\r
-    LPSTR cp, com_err_msg;\r
-    int offset;\r
-    long table_num;\r
-    char *etype;\r
-\r
-    offset = (int) (code & 255);\r
-    table_num = code - offset;\r
-    com_err_msg = Lerror_message(code);\r
-\r
-    switch(table_num)\r
-    {\r
-    case krb_err_base:\r
-    case kadm_err_base:\r
-       break;\r
-    default:\r
-        StringCbCopyA(buf, len, com_err_msg);\r
-       return buf;\r
-    }\r
-\r
-    cp = buf;\r
-    if (table_num == krb_err_base)\r
-        switch(offset)\r
-        {\r
-        case KDC_NAME_EXP:           /* 001 Principal expired */\r
-        case KDC_SERVICE_EXP:        /* 002 Service expired */\r
-        case KDC_AUTH_EXP:           /* 003 Auth expired */\r
-        case KDC_PKT_VER:            /* 004 Protocol version unknown */\r
-        case KDC_P_MKEY_VER:         /* 005 Wrong master key version */\r
-        case KDC_S_MKEY_VER:         /* 006 Wrong master key version */\r
-        case KDC_BYTE_ORDER:         /* 007 Byte order unknown */\r
-        case KDC_PR_N_UNIQUE:        /* 009 Principal not unique */\r
-        case KDC_NULL_KEY:           /* 010 Principal has null key */\r
-        case KDC_GEN_ERR:            /* 011 Generic error from KDC */\r
-        case INTK_W_NOTALL   :       /* 061 Not ALL tickets returned */\r
-        case INTK_PROT       :       /* 063 Protocol Error */\r
-        case INTK_ERR        :       /* 070 Other error */\r
-            com_err_msg = "Something weird happened... try again, and if Leash"\r
-                " continues to fail, contact Network Services as listed in the "\r
-                "About box.";\r
-            break;\r
-        case KDC_PR_UNKNOWN:         /* 008 Principal unknown */\r
-            com_err_msg = "You have entered an unknown username/instance/realm"\r
-                " combination.";\r
-            break;\r
-        case GC_TKFIL                :       /* 021 Can't read ticket file */\r
-        case GC_NOTKT                :       /* 022 Can't find ticket or TGT */\r
-            com_err_msg = "Something is wrong with the memory where your "\r
-                "tickets are stored. Try exiting Windows and restarting your "\r
-                "computer.";\r
-            break;\r
-        case MK_AP_TGTEXP    :       /* 026 TGT Expired */\r
-            /* no extra error msg */\r
-            break;\r
-        case RD_AP_TIME              :       /* 037 delta_t too big */\r
-            com_err_msg = "Your computer's clock is out of sync with the "\r
-                "Kerberos server.  Please see the help file about correcting "\r
-                "your clock.";\r
-            break;\r
-\r
-        case RD_AP_UNDEC             :       /* 031 Can't decode authenticator */\r
-        case RD_AP_EXP               :       /* 032 Ticket expired */\r
-        case RD_AP_NYV               :       /* 033 Ticket not yet valid */\r
-        case RD_AP_REPEAT    :       /* 034 Repeated request */\r
-        case RD_AP_NOT_US    :       /* 035 The ticket isn't for us */\r
-        case RD_AP_INCON             :       /* 036 Request is inconsistent */\r
-        case RD_AP_BADD              :       /* 038 Incorrect net address */\r
-        case RD_AP_VERSION   :       /* 039 protocol version mismatch */\r
-        case RD_AP_MSG_TYPE  :       /* 040 invalid msg type */\r
-        case RD_AP_MODIFIED  :       /* 041 message stream modified */\r
-        case RD_AP_ORDER             :       /* 042 message out of order */\r
-        case RD_AP_UNAUTHOR  :       /* 043 unauthorized request */\r
-            /* no extra error msg */\r
-            break;\r
-        case GT_PW_NULL:     /* 51    Current PW is null */\r
-        case GT_PW_BADPW:    /* 52    Incorrect current password */\r
-        case GT_PW_PROT:     /* 53    Protocol Error */\r
-        case GT_PW_KDCERR:   /* 54    Error returned by KDC */\r
-        case GT_PW_NULLTKT:  /* 55    Null tkt returned by KDC */\r
-            /* no error msg yet */\r
-            break;\r
-         \r
-            /* Values returned by send_to_kdc */\r
-        case SKDC_RETRY   :  /* 56    Retry count exceeded */\r
-        case SKDC_CANT    :  /* 57    Can't send request */\r
-            com_err_msg = "Cannot contact the kerberos server for the selected realm.";\r
-            break;\r
-            /* no error message on purpose: */\r
-        case INTK_BADPW      :       /* 062 Incorrect password */\r
-            break;\r
-        default:\r
-            /* no extra error msg */\r
-            break;\r
-        }\r
-    else\r
-        switch(code)\r
-        {\r
-        case KADM_INSECURE_PW:\r
-            /* if( kadm_info != NULL ){\r
-             * wsprintf(buf, "%s\n%s", com_err_msg, kadm_info);\r
-             * } else {\r
-             * wsprintf(buf, "%s\nPlease see the help file for information "\r
-             * "about secure passwords.", com_err_msg);\r
-             * }\r
-             * com_err_msg = buf;\r
-             */\r
-\r
-            /* The above code would be preferred since it allows site specific\r
-             * information to be delivered from the Kerberos server. However the\r
-             * message box is too small for VGA screens.\r
-             * It does work well if we only have to support 1024x768\r
-             */\r
-       \r
-            com_err_msg = "You have entered an insecure or weak password.";\r
-       \r
-        default:\r
-            /* no extra error msg */\r
-            break;\r
-        }\r
-    if(com_err_msg != buf) {\r
-        StringCbCopyA(buf, len, com_err_msg);\r
-    }\r
-    cp = buf + strlen(buf);\r
-    *cp++ = '\n';\r
-    switch(table_num) {\r
-    case krb_err_base:\r
-        etype = "Kerberos";\r
-        break;\r
-    case kadm_err_base:\r
-        etype = "Kerberos supplemental";\r
-        break;\r
-    default:\r
-        etype = Lerror_table_name(table_num);\r
-        break;\r
-    }\r
-    StringCbPrintfA((LPSTR) cp, len - (cp-buf), (LPSTR) "(%s error %d"\r
-#ifdef DEBUG_COM_ERR\r
-             " (absolute error %ld)"\r
-#endif\r
-             ")", etype, offset\r
-             //")\nPress F1 for help on this error.", etype, offset\r
-#ifdef DEBUG_COM_ERR \r
-             , code\r
-#endif\r
-        );\r
-  \r
-    return (LPSTR)buf;\r
-}\r
-\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<strsafe.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 = NULL;
+    while (Child)
+    {
+        Last = Child;
+        Child = GetParent (Child);
+    }
+    return Last;
+}
+
+
+LPSTR err_describe(LPSTR buf, size_t len, 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:
+        StringCbCopyA(buf, len, 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) {
+        StringCbCopyA(buf, len, 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;
+    }
+    StringCbPrintfA((LPSTR) cp, len - (cp-buf), (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;
+}
+
index e339eca4f749d77b88558daa963fac9af38bb536..d760c62595b51d706021636fda821b22713e495f 100644 (file)
@@ -1,65 +1,65 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_ERR_H\r
-#define __KHIMAIRA_ERR_H\r
-\r
-/* All error handling and reporting related functions for the krb4/5\r
-   and AFS plugins */\r
-\r
-#include <errno.h>\r
-#include <com_err.h>\r
-/*\r
- * This is a hack needed because the real com_err.h does\r
- * not define err_func.  We need it in the case where\r
- * we pull in the real com_err instead of the krb4 \r
- * impostor.\r
- */\r
-#ifndef _DCNS_MIT_COM_ERR_H\r
-typedef LPSTR (*err_func)(int, long);\r
-#endif\r
-\r
-#include <krberr.h>\r
-#include <kadm_err.h>\r
-\r
-#define kadm_err_base ERROR_TABLE_BASE_kadm\r
-\r
-#include <stdarg.h>\r
-\r
-#ifndef KRBERR\r
-#define KRBERR(code) (code + krb_err_base)\r
-#endif\r
-\r
-LPSTR err_describe(LPSTR buf, size_t len, long code);\r
-\r
-\r
-/* */\r
-khm_int32 init_error_funcs();\r
-\r
-khm_int32 exit_error_funcs();\r
-\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
+
+LPSTR err_describe(LPSTR buf, size_t len, long code);
+
+
+/* */
+khm_int32 init_error_funcs();
+
+khm_int32 exit_error_funcs();
+
+
+#endif
index 523fbac5e0cf68585a8bed1fcb6fea13d63af4e5..338cf7fa884394b06dca8e38f393159aa3f53118 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<krbcred.h>\r
-#include<kherror.h>\r
-#include<khuidefs.h>\r
-#include<strsafe.h>\r
-#include<assert.h>\r
-\r
-typedef struct tag_k4_ids_data {\r
-    khui_config_init_data cfg;\r
-\r
-    khm_int32 get_tix;\r
-} k4_ids_data;\r
-\r
-static void\r
-k4_ids_read_params(k4_ids_data * d) {\r
-    khm_int32 t;\r
-#ifdef DEBUG\r
-    assert(csp_params);\r
-#endif\r
-\r
-    t = 1;\r
-    khc_read_int32(csp_params, L"Krb4NewCreds", &t);\r
-    d->get_tix = !!t;\r
-}\r
-\r
-static void\r
-k4_ids_write_params(HWND hw, k4_ids_data * d) {\r
-    khm_int32 nv;\r
-    khm_boolean applied = FALSE;\r
-\r
-    if (IsDlgButtonChecked(hw, IDC_CFG_GETTIX) == BST_CHECKED)\r
-        nv = TRUE;\r
-    else\r
-        nv = FALSE;\r
-\r
-    if (!!nv != !!d->get_tix) {\r
-        d->get_tix = !!nv;\r
-        khc_write_int32(csp_params, L"Krb4NewCreds", d->get_tix);\r
-        applied = TRUE;\r
-    }\r
-\r
-    khui_cfg_set_flags_inst(&d->cfg,\r
-                            (applied)?KHUI_CNFLAG_APPLIED:0,\r
-                            KHUI_CNFLAG_APPLIED | KHUI_CNFLAG_MODIFIED);\r
-}\r
-\r
-static void\r
-k4_ids_check_mod(HWND hw, k4_ids_data * d) {\r
-    khm_int32 nv;\r
-\r
-    if (IsDlgButtonChecked(hw, IDC_CFG_GETTIX) == BST_CHECKED)\r
-        nv = TRUE;\r
-    else\r
-        nv = FALSE;\r
-\r
-    khui_cfg_set_flags_inst(&d->cfg,\r
-                            (!!nv != !!d->get_tix)? KHUI_CNFLAG_MODIFIED: 0,\r
-                            KHUI_CNFLAG_MODIFIED);\r
-}\r
-\r
-INT_PTR CALLBACK\r
-krb4_ids_config_proc(HWND hwnd,\r
-                     UINT uMsg,\r
-                     WPARAM wParam,\r
-                     LPARAM lParam) {\r
-    k4_ids_data * d;\r
-\r
-    switch(uMsg) {\r
-    case WM_INITDIALOG:\r
-        d = PMALLOC(sizeof(*d));\r
-        ZeroMemory(d, sizeof(*d));\r
-\r
-        d->cfg = *((khui_config_init_data *) lParam);\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-        SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);\r
-#pragma warning(pop)\r
-\r
-        k4_ids_read_params(d);\r
-\r
-        CheckDlgButton(hwnd, IDC_CFG_GETTIX,\r
-                       (d->get_tix)? BST_CHECKED: BST_UNCHECKED);\r
-\r
-        break;\r
-\r
-    case WM_COMMAND:\r
-        d = (k4_ids_data *) (LONG_PTR)\r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        if (HIWORD(wParam) == BN_CLICKED) {\r
-            k4_ids_check_mod(hwnd, d);\r
-        }\r
-        break;\r
-\r
-    case KHUI_WM_CFG_NOTIFY:\r
-        d = (k4_ids_data *) (LONG_PTR)\r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        if (HIWORD(wParam) == WMCFG_APPLY) {\r
-            k4_ids_write_params(hwnd, d);\r
-        }\r
-        break;\r
-\r
-    case WM_DESTROY:\r
-        d = (k4_ids_data *) (LONG_PTR)\r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        PFREE(d);\r
-        break;\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-typedef struct tag_k4_id_data {\r
-    khui_config_init_data cfg;\r
-    khm_int32 gettix;           /* get tickets? */\r
-    khm_boolean is_default_ident;\r
-} k4_id_data;\r
-\r
-void\r
-k4_id_read_params(k4_id_data * d) {\r
-    wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-    khm_size cb;\r
-    khm_handle ident = NULL;\r
-    khm_handle csp_ident = NULL;\r
-    khm_handle csp_idk4 = NULL;\r
-    khm_int32 flags = 0;\r
-    khm_int32 t;\r
-\r
-    khc_read_int32(csp_params, L"Krb4NewCreds", &d->gettix);\r
-\r
-    *idname = 0;\r
-    cb = sizeof(idname);\r
-    khui_cfg_get_name(d->cfg.ctx_node, idname, &cb);\r
-\r
-    kcdb_identity_create(idname, 0, &ident);\r
-\r
-    if (ident == NULL) {\r
-        d->gettix = 0;\r
-        goto done;\r
-    }\r
-\r
-    kcdb_identity_get_flags(ident, &flags);\r
-\r
-    if (!(flags & KCDB_IDENT_FLAG_DEFAULT)) {\r
-        d->gettix = 0;\r
-        goto done;\r
-    }\r
-\r
-    d->is_default_ident = TRUE;\r
-\r
-    if (d->gettix == 0)\r
-        goto done;\r
-\r
-    if (KHM_FAILED(kcdb_identity_get_config(ident, 0, &csp_ident)))\r
-        goto done;\r
-\r
-    if (KHM_FAILED(khc_open_space(csp_ident, CSNAME_KRB4CRED,\r
-                                  0, &csp_idk4)))\r
-        goto close_config;\r
-\r
-    if (KHM_SUCCEEDED(khc_read_int32(csp_idk4, L"Krb4NewCreds", &t)) &&\r
-        !t)\r
-        d->gettix = 1;\r
-\r
- close_config:\r
-    if (csp_ident)\r
-        khc_close_space(csp_ident);\r
-\r
-    if (csp_idk4)\r
-        khc_close_space(csp_idk4);\r
-\r
- done:\r
-    if (ident)\r
-        kcdb_identity_release(ident);\r
-\r
-    return;\r
-}\r
-\r
-khm_boolean\r
-k4_id_write_params(HWND hwnd, k4_id_data * d) {\r
-    wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-    khm_size cb_idname = sizeof(idname);\r
-    khm_handle ident = NULL;\r
-    khm_int32 flags = 0;\r
-    khm_handle csp_ident = NULL;\r
-    khm_handle csp_idk4 = NULL;\r
-    khm_int32 gettix = 0;\r
-    khm_boolean applied = FALSE;\r
-\r
-    khui_cfg_get_name(d->cfg.ctx_node, idname, &cb_idname);\r
-\r
-    kcdb_identity_create(idname, 0, &ident);\r
-\r
-    if (ident == NULL)\r
-        return FALSE;\r
-\r
-    kcdb_identity_get_flags(ident, &flags);\r
-\r
-    if (!(flags & KCDB_IDENT_FLAG_DEFAULT))\r
-        goto done_apply;\r
-\r
-    if (IsDlgButtonChecked(hwnd, IDC_CFG_GETTIX) == BST_CHECKED)\r
-        gettix = TRUE;\r
-\r
-    if (KHM_FAILED(kcdb_identity_get_config(ident, KHM_FLAG_CREATE,\r
-                                            &csp_ident)))\r
-        goto done_apply;\r
-\r
-    if (KHM_FAILED(khc_open_space(csp_ident, CSNAME_KRB4CRED,\r
-                                  KHM_FLAG_CREATE | KCONF_FLAG_WRITEIFMOD,\r
-                                  &csp_idk4)))\r
-        goto done_apply;\r
-\r
-    khc_write_int32(csp_idk4, L"Krb4NewCreds", gettix);\r
-\r
-    applied = TRUE;\r
-\r
- done_apply:\r
-    if (ident)\r
-        kcdb_identity_release(ident);\r
-\r
-    if (csp_ident)\r
-        khc_close_space(csp_ident);\r
-    \r
-    if (csp_idk4)\r
-        khc_close_space(csp_idk4);\r
-\r
-    return applied;\r
-}\r
-\r
-INT_PTR CALLBACK\r
-krb4_id_config_proc(HWND hwnd,\r
-                    UINT uMsg,\r
-                    WPARAM wParam,\r
-                    LPARAM lParam) {\r
-    switch(uMsg) {\r
-    case WM_INITDIALOG:\r
-        {\r
-            k4_id_data * d;\r
-\r
-            d = PMALLOC(sizeof(k4_id_data));\r
-\r
-            if (!d)\r
-                break;\r
-\r
-            ZeroMemory(d, sizeof(*d));\r
-\r
-            d->cfg = *((khui_config_init_data *) lParam);\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-            SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);\r
-#pragma warning(pop)\r
-\r
-            k4_id_read_params(d);\r
-\r
-            CheckDlgButton(hwnd, IDC_CFG_GETTIX,\r
-                           (d->gettix)?BST_CHECKED: BST_UNCHECKED);\r
-            EnableWindow(GetDlgItem(hwnd, IDC_CFG_GETTIX),\r
-                         d->is_default_ident);\r
-\r
-        }\r
-        break;\r
-\r
-    case WM_COMMAND:\r
-        {\r
-            k4_id_data * d;\r
-\r
-            d = (k4_id_data *) (LONG_PTR)\r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-            if (wParam == MAKEWPARAM(IDC_CFG_GETTIX,\r
-                                     BN_CLICKED)) {\r
-                int gettix = 0;\r
-                int modified = 0;\r
-\r
-                gettix = (IsDlgButtonChecked(hwnd, IDC_CFG_GETTIX) ==\r
-                          BST_CHECKED);\r
-\r
-                modified = (!!gettix != !!d->gettix);\r
-\r
-                khui_cfg_set_flags_inst(&d->cfg,\r
-                                        ((modified)?KHUI_CNFLAG_MODIFIED: 0),\r
-                                        KHUI_CNFLAG_MODIFIED);\r
-            }\r
-        }\r
-        break;\r
-\r
-    case KHUI_WM_CFG_NOTIFY:\r
-        {\r
-            k4_id_data * d;\r
-\r
-            d = (k4_id_data *) (LONG_PTR)\r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-            if (!d)\r
-                break;\r
-\r
-            if (HIWORD(wParam) == WMCFG_APPLY) {\r
-                khm_int32 applied;\r
-\r
-                applied = k4_id_write_params(hwnd, d);\r
-\r
-                khui_cfg_set_flags_inst(&d->cfg,\r
-                                        ((applied)? KHUI_CNFLAG_APPLIED: 0),\r
-                                        (KHUI_CNFLAG_APPLIED | KHUI_CNFLAG_MODIFIED));\r
-            }\r
-        }\r
-        break;\r
-\r
-    case WM_DESTROY:\r
-        {\r
-            k4_id_data * d;\r
-\r
-            d = (k4_id_data *) (LONG_PTR)\r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-            if (!d)\r
-                break;\r
-\r
-            PFREE(d);\r
-        }\r
-        break;\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-typedef struct tag_k4_config_dlg_data {\r
-    khui_config_node node;\r
-    char             krb_path[MAX_PATH];\r
-    char             krbrealm_path[MAX_PATH];\r
-    char             tkt_string[MAX_PATH];\r
-} k4_config_dlg_data;\r
-\r
-INT_PTR CALLBACK\r
-krb4_confg_proc(HWND hwnd,\r
-                UINT uMsg,\r
-                WPARAM wParam,\r
-                LPARAM lParam) {\r
-\r
-    static BOOL in_init = FALSE;\r
-    k4_config_dlg_data * d;\r
-\r
-    switch(uMsg) {\r
-    case WM_INITDIALOG:\r
-        {\r
-            wchar_t wbuf[MAX_PATH];\r
-            CHAR krb_path[MAX_PATH];\r
-            CHAR krbrealm_path[MAX_PATH];\r
-            CHAR ticketName[MAX_PATH];\r
-            char * pticketName;\r
-            unsigned int krb_path_sz = sizeof(krb_path);\r
-            unsigned int krbrealm_path_sz = sizeof(krbrealm_path);\r
-            khm_size cbsize;\r
-\r
-            d = PMALLOC(sizeof(*d));\r
-            ZeroMemory(d, sizeof(*d));\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-            SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);\r
-#pragma warning(pop)\r
-\r
-            d->node = (khui_config_node) lParam;\r
-\r
-            in_init = TRUE;\r
-\r
-            // Set KRB.CON \r
-            memset(krb_path, '\0', sizeof(krb_path));\r
-            if (!pkrb_get_krbconf2(krb_path, &krb_path_sz)) {\r
-                // Error has happened\r
-            } else { // normal find\r
-                AnsiStrToUnicode(wbuf, sizeof(wbuf), krb_path);\r
-                SetDlgItemText(hwnd, IDC_CFG_CFGPATH, wbuf);\r
-                StringCbCopyA(d->krb_path, sizeof(d->krb_path), krb_path);\r
-            }\r
-\r
-            // Set KRBREALM.CON \r
-            memset(krbrealm_path, '\0', sizeof(krbrealm_path));\r
-            if (!pkrb_get_krbrealm2(krbrealm_path, &krbrealm_path_sz)) {   \r
-                // Error has happened\r
-            } else {\r
-                AnsiStrToUnicode(wbuf, sizeof(wbuf), krbrealm_path);\r
-                SetDlgItemText(hwnd, IDC_CFG_RLMPATH, wbuf);\r
-                StringCbCopyA(d->krbrealm_path, sizeof(d->krbrealm_path),\r
-                              krbrealm_path);\r
-            }\r
-\r
-            cbsize = sizeof(wbuf);\r
-            if (KHM_SUCCEEDED(khc_read_string(csp_params, L"TktString",\r
-                                              wbuf, &cbsize)) &&\r
-                wbuf[0] != L'\0') {\r
-\r
-                UnicodeStrToAnsi(ticketName, sizeof(ticketName), wbuf);\r
-\r
-            } else {\r
-\r
-                // Set TICKET.KRB file Editbox\r
-                *ticketName = 0;\r
-                pkrb_set_tkt_string(0);\r
-    \r
-                pticketName = ptkt_string(); \r
-                if (pticketName)\r
-                    StringCbCopyA(ticketName, sizeof(ticketName), pticketName);\r
-\r
-            }\r
-       \r
-            if (!*ticketName) {\r
-                // error\r
-            } else {\r
-                AnsiStrToUnicode(wbuf, sizeof(wbuf), ticketName);\r
-                SetDlgItemText(hwnd, IDC_CFG_CACHE, wbuf);\r
-                StringCbCopyA(d->tkt_string, sizeof(d->tkt_string),\r
-                              ticketName);\r
-            }\r
-\r
-            in_init = FALSE;\r
-\r
-        }\r
-        break;\r
-\r
-    case WM_COMMAND:\r
-        if (MAKEWPARAM(IDC_CFG_CACHE, EN_CHANGE)) {\r
-            char tkt_string[MAX_PATH];\r
-            wchar_t wtkt_string[MAX_PATH];\r
-\r
-            if (in_init) {\r
-                return TRUE;\r
-            }\r
-\r
-            d = (k4_config_dlg_data *) (LONG_PTR)\r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-            if (d == NULL)\r
-                return TRUE;\r
-\r
-            tkt_string[0] = 0;\r
-            wtkt_string[0] = 0;\r
-\r
-            GetDlgItemText(hwnd, IDC_CFG_CACHE,\r
-                           wtkt_string, ARRAYLENGTH(wtkt_string));\r
-            UnicodeStrToAnsi(tkt_string, sizeof(tkt_string),\r
-                             wtkt_string);\r
-\r
-            if (_stricmp(tkt_string, d->tkt_string)) {\r
-                khui_cfg_set_flags(d->node,\r
-                                   KHUI_CNFLAG_MODIFIED,\r
-                                   KHUI_CNFLAG_MODIFIED);\r
-            } else {\r
-                khui_cfg_set_flags(d->node,\r
-                                   0,\r
-                                   KHUI_CNFLAG_MODIFIED);\r
-            }\r
-\r
-            return TRUE;\r
-        }\r
-        break;\r
-\r
-    case KHUI_WM_CFG_NOTIFY:\r
-        if (HIWORD(wParam) == WMCFG_APPLY) {\r
-            wchar_t wtkt_string[MAX_PATH];\r
-            char tkt_string[MAX_PATH];\r
-            int t;\r
-\r
-            d = (k4_config_dlg_data *) (LONG_PTR)\r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-            if (d == NULL)\r
-                return TRUE;\r
-\r
-            t = GetDlgItemText(hwnd, IDC_CFG_CACHE,\r
-                               wtkt_string, ARRAYLENGTH(wtkt_string));\r
-            if (t == 0)\r
-                return TRUE;\r
-\r
-            UnicodeStrToAnsi(tkt_string, sizeof(tkt_string), wtkt_string);\r
-\r
-            if (_stricmp(tkt_string, d->tkt_string)) {\r
-\r
-                pkrb_set_tkt_string(tkt_string);\r
-\r
-                khc_write_string(csp_params, L"TktString", wtkt_string);\r
-\r
-                khui_cfg_set_flags(d->node,\r
-                                   KHUI_CNFLAG_APPLIED,\r
-                                   KHUI_CNFLAG_APPLIED |\r
-                                   KHUI_CNFLAG_MODIFIED);\r
-                khm_krb4_list_tickets();\r
-            } else {\r
-                khui_cfg_set_flags(d->node,\r
-                                   0,\r
-                                   KHUI_CNFLAG_MODIFIED);\r
-            }\r
-\r
-            return TRUE;\r
-        }\r
-        break;\r
-\r
-    case WM_DESTROY:\r
-        d = (k4_config_dlg_data *) (LONG_PTR)\r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        if (d) {\r
-            PFREE(d);\r
-        }\r
-\r
-        break;\r
-    }\r
-    return FALSE;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+#include<assert.h>
+
+typedef struct tag_k4_ids_data {
+    khui_config_init_data cfg;
+
+    khm_int32 get_tix;
+} k4_ids_data;
+
+static void
+k4_ids_read_params(k4_ids_data * d) {
+    khm_int32 t;
+#ifdef DEBUG
+    assert(csp_params);
+#endif
+
+    t = 1;
+    khc_read_int32(csp_params, L"Krb4NewCreds", &t);
+    d->get_tix = !!t;
+}
+
+static void
+k4_ids_write_params(HWND hw, k4_ids_data * d) {
+    khm_int32 nv;
+    khm_boolean applied = FALSE;
+
+    if (IsDlgButtonChecked(hw, IDC_CFG_GETTIX) == BST_CHECKED)
+        nv = TRUE;
+    else
+        nv = FALSE;
+
+    if (!!nv != !!d->get_tix) {
+        d->get_tix = !!nv;
+        khc_write_int32(csp_params, L"Krb4NewCreds", d->get_tix);
+        applied = TRUE;
+    }
+
+    khui_cfg_set_flags_inst(&d->cfg,
+                            (applied)?KHUI_CNFLAG_APPLIED:0,
+                            KHUI_CNFLAG_APPLIED | KHUI_CNFLAG_MODIFIED);
+}
+
+static void
+k4_ids_check_mod(HWND hw, k4_ids_data * d) {
+    khm_int32 nv;
+
+    if (IsDlgButtonChecked(hw, IDC_CFG_GETTIX) == BST_CHECKED)
+        nv = TRUE;
+    else
+        nv = FALSE;
+
+    khui_cfg_set_flags_inst(&d->cfg,
+                            (!!nv != !!d->get_tix)? KHUI_CNFLAG_MODIFIED: 0,
+                            KHUI_CNFLAG_MODIFIED);
+}
+
+INT_PTR CALLBACK
+krb4_ids_config_proc(HWND hwnd,
+                     UINT uMsg,
+                     WPARAM wParam,
+                     LPARAM lParam) {
+    k4_ids_data * d;
+
+    switch(uMsg) {
+    case WM_INITDIALOG:
+        d = PMALLOC(sizeof(*d));
+        ZeroMemory(d, sizeof(*d));
+
+        d->cfg = *((khui_config_init_data *) lParam);
+
+#pragma warning(push)
+#pragma warning(disable: 4244)
+        SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);
+#pragma warning(pop)
+
+        k4_ids_read_params(d);
+
+        CheckDlgButton(hwnd, IDC_CFG_GETTIX,
+                       (d->get_tix)? BST_CHECKED: BST_UNCHECKED);
+
+        break;
+
+    case WM_COMMAND:
+        d = (k4_ids_data *) (LONG_PTR)
+            GetWindowLongPtr(hwnd, DWLP_USER);
+
+        if (HIWORD(wParam) == BN_CLICKED) {
+            k4_ids_check_mod(hwnd, d);
+        }
+        break;
+
+    case KHUI_WM_CFG_NOTIFY:
+        d = (k4_ids_data *) (LONG_PTR)
+            GetWindowLongPtr(hwnd, DWLP_USER);
+
+        if (HIWORD(wParam) == WMCFG_APPLY) {
+            k4_ids_write_params(hwnd, d);
+        }
+        break;
+
+    case WM_DESTROY:
+        d = (k4_ids_data *) (LONG_PTR)
+            GetWindowLongPtr(hwnd, DWLP_USER);
+
+        PFREE(d);
+        break;
+    }
+
+    return FALSE;
+}
+
+typedef struct tag_k4_id_data {
+    khui_config_init_data cfg;
+    khm_int32 gettix;           /* get tickets? */
+    khm_boolean is_default_ident;
+} k4_id_data;
+
+void
+k4_id_read_params(k4_id_data * d) {
+    wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
+    khm_size cb;
+    khm_handle ident = NULL;
+    khm_handle csp_ident = NULL;
+    khm_handle csp_idk4 = NULL;
+    khm_int32 flags = 0;
+    khm_int32 t;
+
+    khc_read_int32(csp_params, L"Krb4NewCreds", &d->gettix);
+
+    *idname = 0;
+    cb = sizeof(idname);
+    khui_cfg_get_name(d->cfg.ctx_node, idname, &cb);
+
+    kcdb_identity_create(idname, 0, &ident);
+
+    if (ident == NULL) {
+        d->gettix = 0;
+        goto done;
+    }
+
+    kcdb_identity_get_flags(ident, &flags);
+
+    if (!(flags & KCDB_IDENT_FLAG_DEFAULT)) {
+        d->gettix = 0;
+        goto done;
+    }
+
+    d->is_default_ident = TRUE;
+
+    if (d->gettix == 0)
+        goto done;
+
+    if (KHM_FAILED(kcdb_identity_get_config(ident, 0, &csp_ident)))
+        goto done;
+
+    if (KHM_FAILED(khc_open_space(csp_ident, CSNAME_KRB4CRED,
+                                  0, &csp_idk4)))
+        goto close_config;
+
+    if (KHM_SUCCEEDED(khc_read_int32(csp_idk4, L"Krb4NewCreds", &t)) &&
+        !t)
+        d->gettix = 1;
+
+ close_config:
+    if (csp_ident)
+        khc_close_space(csp_ident);
+
+    if (csp_idk4)
+        khc_close_space(csp_idk4);
+
+ done:
+    if (ident)
+        kcdb_identity_release(ident);
+
+    return;
+}
+
+khm_boolean
+k4_id_write_params(HWND hwnd, k4_id_data * d) {
+    wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
+    khm_size cb_idname = sizeof(idname);
+    khm_handle ident = NULL;
+    khm_int32 flags = 0;
+    khm_handle csp_ident = NULL;
+    khm_handle csp_idk4 = NULL;
+    khm_int32 gettix = 0;
+    khm_boolean applied = FALSE;
+
+    khui_cfg_get_name(d->cfg.ctx_node, idname, &cb_idname);
+
+    kcdb_identity_create(idname, 0, &ident);
+
+    if (ident == NULL)
+        return FALSE;
+
+    kcdb_identity_get_flags(ident, &flags);
+
+    if (!(flags & KCDB_IDENT_FLAG_DEFAULT))
+        goto done_apply;
+
+    if (IsDlgButtonChecked(hwnd, IDC_CFG_GETTIX) == BST_CHECKED)
+        gettix = TRUE;
+
+    if (KHM_FAILED(kcdb_identity_get_config(ident, KHM_FLAG_CREATE,
+                                            &csp_ident)))
+        goto done_apply;
+
+    if (KHM_FAILED(khc_open_space(csp_ident, CSNAME_KRB4CRED,
+                                  KHM_FLAG_CREATE | KCONF_FLAG_WRITEIFMOD,
+                                  &csp_idk4)))
+        goto done_apply;
+
+    khc_write_int32(csp_idk4, L"Krb4NewCreds", gettix);
+
+    applied = TRUE;
+
+ done_apply:
+    if (ident)
+        kcdb_identity_release(ident);
+
+    if (csp_ident)
+        khc_close_space(csp_ident);
+    
+    if (csp_idk4)
+        khc_close_space(csp_idk4);
+
+    return applied;
+}
+
+INT_PTR CALLBACK
+krb4_id_config_proc(HWND hwnd,
+                    UINT uMsg,
+                    WPARAM wParam,
+                    LPARAM lParam) {
+    switch(uMsg) {
+    case WM_INITDIALOG:
+        {
+            k4_id_data * d;
+
+            d = PMALLOC(sizeof(k4_id_data));
+
+            if (!d)
+                break;
+
+            ZeroMemory(d, sizeof(*d));
+
+            d->cfg = *((khui_config_init_data *) lParam);
+
+#pragma warning(push)
+#pragma warning(disable: 4244)
+            SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);
+#pragma warning(pop)
+
+            k4_id_read_params(d);
+
+            CheckDlgButton(hwnd, IDC_CFG_GETTIX,
+                           (d->gettix)?BST_CHECKED: BST_UNCHECKED);
+            EnableWindow(GetDlgItem(hwnd, IDC_CFG_GETTIX),
+                         d->is_default_ident);
+
+        }
+        break;
+
+    case WM_COMMAND:
+        {
+            k4_id_data * d;
+
+            d = (k4_id_data *) (LONG_PTR)
+                GetWindowLongPtr(hwnd, DWLP_USER);
+
+            if (wParam == MAKEWPARAM(IDC_CFG_GETTIX,
+                                     BN_CLICKED)) {
+                int gettix = 0;
+                int modified = 0;
+
+                gettix = (IsDlgButtonChecked(hwnd, IDC_CFG_GETTIX) ==
+                          BST_CHECKED);
+
+                modified = (!!gettix != !!d->gettix);
+
+                khui_cfg_set_flags_inst(&d->cfg,
+                                        ((modified)?KHUI_CNFLAG_MODIFIED: 0),
+                                        KHUI_CNFLAG_MODIFIED);
+            }
+        }
+        break;
+
+    case KHUI_WM_CFG_NOTIFY:
+        {
+            k4_id_data * d;
+
+            d = (k4_id_data *) (LONG_PTR)
+                GetWindowLongPtr(hwnd, DWLP_USER);
+
+            if (!d)
+                break;
+
+            if (HIWORD(wParam) == WMCFG_APPLY) {
+                khm_int32 applied;
+
+                applied = k4_id_write_params(hwnd, d);
+
+                khui_cfg_set_flags_inst(&d->cfg,
+                                        ((applied)? KHUI_CNFLAG_APPLIED: 0),
+                                        (KHUI_CNFLAG_APPLIED | KHUI_CNFLAG_MODIFIED));
+            }
+        }
+        break;
+
+    case WM_DESTROY:
+        {
+            k4_id_data * d;
+
+            d = (k4_id_data *) (LONG_PTR)
+                GetWindowLongPtr(hwnd, DWLP_USER);
+
+            if (!d)
+                break;
+
+            PFREE(d);
+        }
+        break;
+    }
+
+    return FALSE;
+}
+
+typedef struct tag_k4_config_dlg_data {
+    khui_config_node node;
+    char             krb_path[MAX_PATH];
+    char             krbrealm_path[MAX_PATH];
+    char             tkt_string[MAX_PATH];
+} k4_config_dlg_data;
+
+INT_PTR CALLBACK
+krb4_confg_proc(HWND hwnd,
+                UINT uMsg,
+                WPARAM wParam,
+                LPARAM lParam) {
+
+    static BOOL in_init = FALSE;
+    k4_config_dlg_data * d;
+
+    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);
+            khm_size cbsize;
+
+            d = PMALLOC(sizeof(*d));
+            ZeroMemory(d, sizeof(*d));
+
+#pragma warning(push)
+#pragma warning(disable: 4244)
+            SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);
+#pragma warning(pop)
+
+            d->node = (khui_config_node) lParam;
+
+            in_init = TRUE;
+
+            // 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);
+                StringCbCopyA(d->krb_path, sizeof(d->krb_path), krb_path);
+            }
+
+            // 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);
+                StringCbCopyA(d->krbrealm_path, sizeof(d->krbrealm_path),
+                              krbrealm_path);
+            }
+
+            cbsize = sizeof(wbuf);
+            if (KHM_SUCCEEDED(khc_read_string(csp_params, L"TktString",
+                                              wbuf, &cbsize)) &&
+                wbuf[0] != L'\0') {
+
+                UnicodeStrToAnsi(ticketName, sizeof(ticketName), wbuf);
+
+            } else {
+
+                // 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);
+                StringCbCopyA(d->tkt_string, sizeof(d->tkt_string),
+                              ticketName);
+            }
+
+            in_init = FALSE;
+
+        }
+        break;
+
+    case WM_COMMAND:
+        if (MAKEWPARAM(IDC_CFG_CACHE, EN_CHANGE)) {
+            char tkt_string[MAX_PATH];
+            wchar_t wtkt_string[MAX_PATH];
+
+            if (in_init) {
+                return TRUE;
+            }
+
+            d = (k4_config_dlg_data *) (LONG_PTR)
+                GetWindowLongPtr(hwnd, DWLP_USER);
+
+            if (d == NULL)
+                return TRUE;
+
+            tkt_string[0] = 0;
+            wtkt_string[0] = 0;
+
+            GetDlgItemText(hwnd, IDC_CFG_CACHE,
+                           wtkt_string, ARRAYLENGTH(wtkt_string));
+            UnicodeStrToAnsi(tkt_string, sizeof(tkt_string),
+                             wtkt_string);
+
+            if (_stricmp(tkt_string, d->tkt_string)) {
+                khui_cfg_set_flags(d->node,
+                                   KHUI_CNFLAG_MODIFIED,
+                                   KHUI_CNFLAG_MODIFIED);
+            } else {
+                khui_cfg_set_flags(d->node,
+                                   0,
+                                   KHUI_CNFLAG_MODIFIED);
+            }
+
+            return TRUE;
+        }
+        break;
+
+    case KHUI_WM_CFG_NOTIFY:
+        if (HIWORD(wParam) == WMCFG_APPLY) {
+            wchar_t wtkt_string[MAX_PATH];
+            char tkt_string[MAX_PATH];
+            int t;
+
+            d = (k4_config_dlg_data *) (LONG_PTR)
+                GetWindowLongPtr(hwnd, DWLP_USER);
+
+            if (d == NULL)
+                return TRUE;
+
+            t = GetDlgItemText(hwnd, IDC_CFG_CACHE,
+                               wtkt_string, ARRAYLENGTH(wtkt_string));
+            if (t == 0)
+                return TRUE;
+
+            UnicodeStrToAnsi(tkt_string, sizeof(tkt_string), wtkt_string);
+
+            if (_stricmp(tkt_string, d->tkt_string)) {
+
+                pkrb_set_tkt_string(tkt_string);
+
+                khc_write_string(csp_params, L"TktString", wtkt_string);
+
+                khui_cfg_set_flags(d->node,
+                                   KHUI_CNFLAG_APPLIED,
+                                   KHUI_CNFLAG_APPLIED |
+                                   KHUI_CNFLAG_MODIFIED);
+                khm_krb4_list_tickets();
+            } else {
+                khui_cfg_set_flags(d->node,
+                                   0,
+                                   KHUI_CNFLAG_MODIFIED);
+            }
+
+            return TRUE;
+        }
+        break;
+
+    case WM_DESTROY:
+        d = (k4_config_dlg_data *) (LONG_PTR)
+            GetWindowLongPtr(hwnd, DWLP_USER);
+
+        if (d) {
+            PFREE(d);
+        }
+
+        break;
+    }
+    return FALSE;
+}
index b2b5fef4e7924b83e8ae22c86d94fefdcec66f8e..9e9ba42d252573011e06ab13d0be8be65436d348 100644 (file)
-/*\r
-* Copyright (c) 2005 Massachusetts Institute of Technology\r
-*\r
-* Permission is hereby granted, free of charge, to any person\r
-* obtaining a copy of this software and associated documentation\r
-* files (the "Software"), to deal in the Software without\r
-* restriction, including without limitation the rights to use, copy,\r
-* modify, merge, publish, distribute, sublicense, and/or sell copies\r
-* of the Software, and to permit persons to whom the Software is\r
-* furnished to do so, subject to the following conditions:\r
-*\r
-* The above copyright notice and this permission notice shall be\r
-* included in all copies or substantial portions of the Software.\r
-*\r
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
-* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
-* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
-* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
-* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
-* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
-* SOFTWARE.\r
-*/\r
-\r
-/* $Id$ */\r
-\r
-/* Originally this was krb5routines.c in Leash sources.  Subsequently\r
-modified and adapted for NetIDMgr */\r
-\r
-#include<krbcred.h>\r
-#include<kherror.h>\r
-\r
-#define SECURITY_WIN32\r
-#include <security.h>\r
-\r
-#include <string.h>\r
-#include <time.h>\r
-#include <assert.h>\r
-#include <strsafe.h>\r
-\r
-\r
-\r
-int com_addr(void)\r
-{\r
-    long ipAddr;\r
-    char loc_addr[ADDR_SZ];\r
-    CREDENTIALS cred;    \r
-    char service[40];\r
-    char instance[40];\r
-    //    char addr[40];\r
-    char realm[40];\r
-    struct in_addr LocAddr;\r
-    int k_errno;\r
-\r
-    if (pkrb_get_cred == NULL)\r
-        return(KSUCCESS);\r
-\r
-    k_errno = (*pkrb_get_cred)(service,instance,realm,&cred);\r
-    if (k_errno)\r
-        return KRBERR(k_errno);\r
-\r
-    while(1) {\r
-        ipAddr = (*pLocalHostAddr)();\r
-        LocAddr.s_addr = ipAddr;\r
-        StringCbCopyA(loc_addr, sizeof(loc_addr), inet_ntoa(LocAddr));\r
-        if ( strcmp(cred.address,loc_addr) != 0) {\r
-            /* TODO: do something about this */\r
-            //Leash_kdestroy ();\r
-            break;\r
-        }\r
-        break;\r
-    } // while()\r
-    return 0;\r
-} \r
-\r
-\r
-long \r
-khm_krb4_list_tickets(void) \r
-{\r
-    char    ptktname[MAX_PATH + 5];\r
-    char    pname[ANAME_SZ];\r
-    char    pinst[INST_SZ];\r
-    char    prealm[REALM_SZ];\r
-    wchar_t wbuf[256];\r
-    int     k_errno = 0;\r
-    CREDENTIALS c;\r
-    int newtickets = 0;\r
-    int open = 0;\r
-    khm_handle ident = NULL;\r
-    khm_handle cred = NULL;\r
-    time_t tt;\r
-    FILETIME ft;\r
-\r
-    kcdb_credset_flush(krb4_credset);\r
-\r
-    // Since krb_get_tf_realm will return a ticket_file error,\r
-    // we will call tf_init and tf_close first to filter out\r
-    // things like no ticket file.  Otherwise, the error that\r
-    // the user would see would be\r
-    // klist: can't find realm of ticket file: No ticket file (tf_util)\r
-    // instead of klist: No ticket file (tf_util)\r
-    if (ptf_init == NULL)\r
-        goto collect;\r
-\r
-    com_addr();\r
-    \r
-    // Open ticket file\r
-    if ((k_errno = (*ptf_init)((*ptkt_string)(), R_TKT_FIL)))\r
-    {\r
-        goto cleanup;\r
-    }\r
-    // Close ticket file \r
-    (void) (*ptf_close)();\r
-    \r
-    // We must find the realm of the ticket file here before calling\r
-    // tf_init because since the realm of the ticket file is not\r
-    // really stored in the principal section of the file, the\r
-    // routine we use must itself call tf_init and tf_close.\r
-\r
-    if ((k_errno = (*pkrb_get_tf_realm)((*ptkt_string)(), prealm)) != KSUCCESS)\r
-    {\r
-        goto cleanup;\r
-    }\r
-       \r
-    // Open ticket file \r
-    if (k_errno = (*ptf_init)((*ptkt_string)(), R_TKT_FIL)) \r
-    {\r
-        goto cleanup;\r
-    }\r
-\r
-    StringCchCopyA(ptktname, ARRAYLENGTH(ptktname), (*ptkt_string)());\r
-\r
-    open = 1;\r
-\r
-    // Get principal name and instance \r
-    if ((k_errno = (*ptf_get_pname)(pname)) || (k_errno = (*ptf_get_pinst)(pinst))) \r
-    {\r
-        goto cleanup;\r
-    }\r
-       \r
-    // You may think that this is the obvious place to get the\r
-    // realm of the ticket file, but it can't be done here as the\r
-    // routine to do this must open the ticket file.  This is why\r
-    // it was done before tf_init.\r
-    StringCbPrintf(wbuf, sizeof(wbuf), L"%S%S%S%S%S", (LPSTR)pname,\r
-             (LPSTR)(pinst[0] ? "." : ""), (LPSTR)pinst,\r
-             (LPSTR)(prealm[0] ? "@" : ""), (LPSTR)prealm);\r
-\r
-    if(KHM_FAILED(kcdb_identity_create(wbuf, KCDB_IDENT_FLAG_CREATE, &ident)))\r
-    {\r
-        goto cleanup;\r
-    }\r
-\r
-    // Get KRB4 tickets\r
-    while ((k_errno = (*ptf_get_cred)(&c)) == KSUCCESS)\r
-    {\r
-        StringCbPrintf(wbuf, sizeof(wbuf), L"%S%S%S%S%S",\r
-            c.service,\r
-            (c.instance[0] ? "." : ""),\r
-            c.instance,\r
-            (c.realm[0] ? "@" : ""),\r
-            c.realm);\r
-\r
-        if(KHM_FAILED(kcdb_cred_create(wbuf, ident, credtype_id_krb4, &cred)))\r
-            continue;\r
-\r
-        tt = c.issue_date + c.lifetime * 5L * 60L;\r
-        TimetToFileTime(tt, &ft);\r
-        kcdb_cred_set_attr(cred, KCDB_ATTR_EXPIRE, &ft, sizeof(ft));\r
-\r
-        tt = c.issue_date;\r
-        TimetToFileTime(tt, &ft);\r
-        kcdb_cred_set_attr(cred, KCDB_ATTR_ISSUE, &ft, sizeof(ft));\r
-\r
-        tt = c.lifetime * 5L * 60L;\r
-        TimetToFileTimeInterval(tt, &ft);\r
-        kcdb_cred_set_attr(cred, KCDB_ATTR_LIFETIME, &ft, sizeof(ft));\r
-\r
-        AnsiStrToUnicode(wbuf, sizeof(wbuf), ptktname);\r
-        kcdb_cred_set_attr(cred, KCDB_ATTR_LOCATION, wbuf, KCDB_CBSIZE_AUTO);\r
-\r
-        kcdb_credset_add_cred(krb4_credset, cred, -1);\r
-\r
-       kcdb_cred_release(cred);\r
-    } // while\r
-\r
- cleanup:\r
-    if (ptf_close == NULL)\r
-        return(KSUCCESS);\r
-\r
-    if (open)\r
-        (*ptf_close)(); //close ticket file \r
-\r
-    if (k_errno == EOF)\r
-        k_errno = 0;\r
-\r
-    // XXX the if statement directly below was inserted to eliminate\r
-    // an error NO_TKT_FIL on Leash startup. The error occurs from an\r
-    // error number thrown from krb_get_tf_realm.  We believe this\r
-    // change does not eliminate other errors, but it may.\r
-\r
-    if (k_errno == NO_TKT_FIL)\r
-        k_errno = 0;\r
-\r
-    if(ident)\r
-        kcdb_identity_release(ident);\r
-\r
-#if 0\r
-    /*TODO: Handle errors here */\r
-    if (k_errno)\r
-    {\r
-        CHAR message[256];\r
-        CHAR errBuf[256];\r
-        LPCSTR errText; \r
-\r
-        if (!Lerror_message)\r
-            return -1;\r
-\r
-        errText = err_describe(errBuf, KRBERR(k_errno));\r
-\r
-        sprintf(message, "%s\n\n%s failed", errText, functionName);\r
-        MessageBox(NULL, message, "Kerberos Four", \r
-                   MB_OK | MB_ICONERROR | MB_TASKMODAL | MB_SETFOREGROUND);\r
-    }\r
-#endif\r
-\r
- collect:\r
-    kcdb_credset_collect(NULL, krb4_credset, ident, credtype_id_krb4, NULL);\r
-\r
-    return k_errno;\r
-}\r
-\r
-#define KRB_FILE                "KRB.CON"\r
-#define KRBREALM_FILE           "KRBREALM.CON"\r
-#define KRB5_FILE               "KRB5.INI"\r
-\r
-BOOL \r
-khm_krb5_get_profile_file(LPSTR confname, UINT szConfname)\r
-{\r
-    char **configFile = NULL;\r
-    if (pkrb5_get_default_config_files(&configFile)) \r
-    {\r
-        GetWindowsDirectoryA(confname,szConfname);\r
-        confname[szConfname-1] = '\0';\r
-\r
-        StringCchCatA(confname, szConfname, "\\");\r
-        StringCchCatA(confname, szConfname, KRB5_FILE);\r
-\r
-        return FALSE;\r
-    }\r
-    \r
-    *confname = 0;\r
-    \r
-    if (configFile)\r
-    {\r
-        StringCchCopyA(confname, szConfname, *configFile);\r
-        pkrb5_free_config_files(configFile); \r
-    }\r
-    \r
-    if (!*confname)\r
-    {\r
-        GetWindowsDirectoryA(confname,szConfname);\r
-        confname[szConfname-1] = '\0';\r
-\r
-        StringCchCatA(confname, szConfname, "\\");\r
-        StringCchCatA(confname, szConfname, KRB5_FILE);\r
-    }\r
-    \r
-    return FALSE;\r
-}\r
-\r
-BOOL\r
-khm_get_krb4_con_file(LPSTR confname, UINT szConfname)\r
-{\r
-    if (hKrb5 && !hKrb4) {\r
-        // hold krb.con where krb5.ini is located\r
-        CHAR krbConFile[MAX_PATH]="";\r
-        LPSTR pFind;\r
-\r
-        if (khm_krb5_get_profile_file(krbConFile, sizeof(krbConFile))) {\r
-            GetWindowsDirectoryA(krbConFile,sizeof(krbConFile));\r
-            krbConFile[MAX_PATH-1] = '\0';\r
-\r
-            StringCbCatA(krbConFile, sizeof(krbConFile), "\\");\r
-        }\r
-\r
-        pFind = strrchr(krbConFile, '\\');\r
-\r
-        if (pFind) {\r
-            *pFind = '\0';\r
-\r
-            StringCbCatA(krbConFile, sizeof(krbConFile), "\\");\r
-            StringCbCatA(krbConFile, sizeof(krbConFile), KRB_FILE);\r
-        } else {\r
-            krbConFile[0] = '\0';\r
-        }\r
-\r
-        StringCchCopyA(confname, szConfname, krbConFile);\r
-    } else if (hKrb4) { \r
-        unsigned int size = szConfname;\r
-        memset(confname, '\0', szConfname);\r
-        if (!pkrb_get_krbconf2(confname, &size)) {\r
-            GetWindowsDirectoryA(confname,szConfname);\r
-            confname[szConfname-1] = '\0';\r
-            StringCchCatA(confname, szConfname, "\\");\r
-            StringCchCatA(confname, szConfname, KRB_FILE);\r
-        }\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-int\r
-readstring(FILE * file, char * buf, int len)\r
-{\r
-       int  c,i;\r
-       memset(buf, '\0', sizeof(buf));\r
-       for (i=0, c=fgetc(file); c != EOF ; c=fgetc(file), i++)\r
-       {       \r
-               if (i < sizeof(buf)) {\r
-                       if (c == '\n') {\r
-                               buf[i] = '\0';\r
-                               return i;\r
-                       } else {\r
-                               buf[i] = c;\r
-                       }\r
-               } else {\r
-                       if (c == '\n') {\r
-                               buf[len-1] = '\0';\r
-                               return(i);\r
-                       }\r
-               }\r
-       }\r
-       if (c == EOF) {\r
-               if (i > 0 && i < len) {\r
-                       buf[i] = '\0';\r
-                       return(i);\r
-               } else {\r
-                       buf[len-1] = '\0';\r
-                       return(-1);\r
-               }\r
-       }\r
-    return(-1);\r
-}\r
-\r
-/*! \internal\r
-    \brief Return a list of configured realms\r
-\r
-    The string that is returned is a set of null terminated unicode strings, \r
-    each of which denotes one realm.  The set is terminated by a zero length\r
-    null terminated string.\r
-\r
-    The caller should free the returned string using free()\r
-\r
-    \return The string with the list of realms or NULL if the operation fails.\r
-*/\r
-wchar_t * khm_krb5_get_realm_list(void) \r
-{\r
-    wchar_t * rlist = NULL;\r
-\r
-    if (pprofile_get_subsection_names && pprofile_free_list) {\r
-        const char*  rootSection[] = {"realms", NULL};\r
-        const char** rootsec = rootSection;\r
-        char **sections = NULL, **cpp = NULL, *value = NULL;\r
-\r
-        char krb5_conf[MAX_PATH+1];\r
-\r
-        if (!khm_krb5_get_profile_file(krb5_conf,sizeof(krb5_conf))) {\r
-            profile_t profile;\r
-            long retval;\r
-            const char *filenames[2];\r
-            wchar_t * d;\r
-            size_t cbsize;\r
-            size_t t;\r
-\r
-            filenames[0] = krb5_conf;\r
-            filenames[1] = NULL;\r
-            retval = pprofile_init(filenames, &profile);\r
-            if (!retval) {\r
-                retval = pprofile_get_subsection_names(profile,        rootsec, &sections);\r
-\r
-                if (!retval)\r
-                {\r
-                    /* first figure out how much space to allocate */\r
-                    cbsize = 0;\r
-                    for (cpp = sections; *cpp; cpp++) \r
-                    {\r
-                        cbsize += sizeof(wchar_t) * (strlen(*cpp) + 1);\r
-                    }\r
-                    cbsize += sizeof(wchar_t); /* double null terminated */\r
-\r
-                    rlist = PMALLOC(cbsize);\r
-                    d = rlist;\r
-                    for (cpp = sections; *cpp; cpp++)\r
-                    {\r
-                        AnsiStrToUnicode(d, cbsize, *cpp);\r
-                        t = wcslen(d) + 1;\r
-                        d += t;\r
-                        cbsize -= sizeof(wchar_t) * t;\r
-                    }\r
-                    *d = L'\0';\r
-                }\r
-\r
-                pprofile_free_list(sections);\r
-\r
-#if 0\r
-                retval = pprofile_get_string(profile, "libdefaults","noaddresses", 0, "true", &value);\r
-                if ( value ) {\r
-                    disable_noaddresses = config_boolean_to_int(value);\r
-                    pprofile_release_string(value);\r
-                }\r
-#endif\r
-                pprofile_release(profile);\r
-            }\r
-        }\r
-    } else {\r
-        FILE * file;\r
-        char krb_conf[MAX_PATH+1];\r
-        char * p;\r
-        size_t cbsize, t;\r
-        wchar_t * d;\r
-\r
-        if (!khm_get_krb4_con_file(krb_conf,sizeof(krb_conf)) && \r
-#if _MSC_VER >= 1400\r
-            !fopen_s(&file, krb_conf, "rt")\r
-#else\r
-            (file = fopen(krb_conf, "rt"))\r
-#endif\r
-            )\r
-        {\r
-            char lineBuf[256];\r
-\r
-            /*TODO: compute the actual required buffer size instead of hardcoding */\r
-            cbsize = 16384; // arbitrary\r
-            rlist = PMALLOC(cbsize);\r
-            d = rlist;\r
-\r
-            // Skip the default realm\r
-            readstring(file,lineBuf,sizeof(lineBuf));\r
-\r
-            // Read the defined realms\r
-            while (TRUE)\r
-            {\r
-                if (readstring(file,lineBuf,sizeof(lineBuf)) < 0)\r
-                    break;\r
-\r
-                if (*(lineBuf + strlen(lineBuf) - 1) == '\r')\r
-                    *(lineBuf + strlen(lineBuf) - 1) = 0;\r
-\r
-                for (p=lineBuf; *p ; p++)\r
-                {\r
-                    if (isspace(*p)) {\r
-                        *p = 0;\r
-                        break;\r
-                    }\r
-                }\r
-\r
-                if ( strncmp(".KERBEROS.OPTION.",lineBuf,17) ) {\r
-                    t = strlen(lineBuf) + 1;\r
-                    if(cbsize > (1 + t*sizeof(wchar_t))) {\r
-                        AnsiStrToUnicode(d, cbsize, lineBuf);\r
-                        d += t;\r
-                        cbsize -= t * sizeof(wchar_t);\r
-                    } else\r
-                        break;\r
-                }\r
-            }\r
-\r
-            *d = L'\0';\r
-\r
-            fclose(file);\r
-        }\r
-    }\r
-\r
-    return rlist;\r
-}\r
-\r
-/*! \internal\r
-    \brief Get the default realm\r
-\r
-    A string will be returned that specifies the default realm.  The caller\r
-    should free the string using free().\r
-\r
-    Returns NULL if the operation fails.\r
-*/\r
-wchar_t * khm_krb5_get_default_realm(void)\r
-{\r
-    wchar_t * realm;\r
-    size_t cch;\r
-    krb5_context ctx=0;\r
-    char * def = 0;\r
-\r
-    pkrb5_init_context(&ctx);\r
-    pkrb5_get_default_realm(ctx,&def);\r
-    \r
-    if (def) {\r
-        cch = strlen(def) + 1;\r
-        realm = PMALLOC(sizeof(wchar_t) * cch);\r
-        AnsiStrToUnicode(realm, sizeof(wchar_t) * cch, def);\r
-        pkrb5_free_default_realm(ctx, def);\r
-    } else\r
-        realm = NULL;\r
-\r
-    pkrb5_free_context(ctx);\r
-\r
-    return realm;\r
-}\r
-\r
-static\r
-char *\r
-make_postfix(const char * base,\r
-             const char * postfix,\r
-             char ** rcopy)\r
-{\r
-    size_t base_size;\r
-    size_t ret_size;\r
-    char * copy = 0;\r
-    char * ret = 0;\r
-    size_t t;\r
-\r
-    if (FAILED(StringCbLengthA(base, STRSAFE_MAX_CCH * sizeof(char), &t)))\r
-        goto cleanup;\r
-\r
-    base_size = t + 1;\r
-\r
-    if (FAILED(StringCbLengthA(postfix, STRSAFE_MAX_CCH * sizeof(char), &t)))\r
-        goto cleanup;\r
-\r
-    ret_size = base_size + t + 1;\r
-\r
-    copy = malloc(base_size);\r
-    ret = malloc(ret_size);\r
-\r
-    if (!copy || !ret)\r
-        goto cleanup;\r
-\r
-    StringCbCopyNA(copy, base_size, base, base_size);\r
-    StringCbCopyNA(ret, ret_size, base, base_size);\r
-    StringCbCopyNA(ret + (base_size - 1), ret_size - (base_size - 1),\r
-                   postfix, ret_size - (base_size - 1));\r
-\r
- cleanup:\r
-    if (!copy || !ret) {\r
-        if (copy)\r
-            free(copy);\r
-        if (ret)\r
-            free(ret);\r
-        copy = ret = 0;\r
-    }\r
-    // INVARIANT: (ret ==> copy) && (copy ==> ret)\r
-    *rcopy = copy;\r
-    return ret;\r
-}\r
-\r
-void\r
-khm_krb4_set_def_tkt_string(void) {\r
-    wchar_t wtkt_string[MAX_PATH];\r
-    char tkt_string[MAX_PATH];\r
-    khm_size cb;\r
-\r
-    cb = sizeof(wtkt_string);\r
-\r
-    if (KHM_FAILED(khc_read_string(csp_params, L"TktString",\r
-                                   wtkt_string, &cb)) ||\r
-        wtkt_string[0] == L'\0') {\r
-\r
-        pkrb_set_tkt_string(0);\r
-\r
-    } else {\r
-\r
-        UnicodeStrToAnsi(tkt_string, sizeof(tkt_string),\r
-                         wtkt_string);\r
-        pkrb_set_tkt_string(tkt_string);        \r
-    }\r
-}\r
-\r
-\r
-static\r
-long\r
-make_temp_cache_v4(const char * postfix)\r
-{\r
-    static char * old_cache = 0;\r
-\r
-    if (!pkrb_set_tkt_string || !ptkt_string || !pdest_tkt)\r
-        return 0; // XXX - is this appropriate?\r
-\r
-    if (old_cache) {\r
-        pdest_tkt();\r
-        pkrb_set_tkt_string(old_cache);\r
-        free(old_cache);\r
-        old_cache = 0;\r
-    }\r
-\r
-    if (postfix)\r
-    {\r
-        char * tmp_cache = make_postfix(ptkt_string(), postfix, &old_cache);\r
-\r
-        if (!tmp_cache)\r
-            return KFAILURE;\r
-\r
-        pkrb_set_tkt_string(tmp_cache);\r
-        free(tmp_cache);\r
-    }\r
-    return 0;\r
-}\r
-\r
-long\r
-khm_krb4_changepwd(char * principal,\r
-                   char * password,\r
-                   char * newpassword,\r
-                   char** error_str)\r
-{\r
-    long k_errno;\r
-\r
-    if (!pkrb_set_tkt_string || !ptkt_string || !pkadm_change_your_password ||\r
-        !pdest_tkt)\r
-        return KFAILURE;\r
-\r
-    k_errno = make_temp_cache_v4("_chgpwd");\r
-    if (k_errno) return k_errno;\r
-    k_errno = pkadm_change_your_password(principal, password, newpassword, \r
-                                         error_str);\r
-    make_temp_cache_v4(0);\r
-    return k_errno;\r
-}\r
-\r
-struct tgt_filter_data {\r
-    khm_handle identity;\r
-    wchar_t realm[KCDB_IDENT_MAXCCH_NAME];\r
-};\r
-\r
-khm_int32 KHMAPI\r
-krb4_tgt_filter(khm_handle cred, khm_int32 flags, void * rock) {\r
-    struct tgt_filter_data * pdata;\r
-    wchar_t credname[KCDB_MAXCCH_NAME];\r
-    wchar_t * t;\r
-    khm_size cb;\r
-    khm_int32 ctype;\r
-\r
-    pdata = (struct tgt_filter_data *) rock;\r
-    cb = sizeof(credname);\r
-\r
-    if (KHM_FAILED(kcdb_cred_get_type(cred, &ctype)) ||\r
-        ctype != credtype_id_krb4)\r
-        return 0;\r
-\r
-    if (KHM_FAILED(kcdb_cred_get_name(cred, credname, &cb)))\r
-        return 0;\r
-\r
-    if (wcsncmp(credname, L"krbtgt.", 7))\r
-        return 0;\r
-\r
-    t = wcsrchr(credname, L'@');\r
-    if (t == NULL)\r
-        return 0;\r
-\r
-    if (wcscmp(t+1, pdata->realm))\r
-        return 0;\r
-\r
-    return 1;\r
-}\r
-\r
-khm_handle\r
-khm_krb4_find_tgt(khm_handle credset, khm_handle identity) {\r
-    khm_handle result = NULL;\r
-    wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-    wchar_t * t;\r
-    khm_size cb;\r
-    struct tgt_filter_data filter_data;\r
-\r
-    cb = sizeof(idname);\r
-\r
-    if (KHM_FAILED(kcdb_identity_get_name(identity,\r
-                                          idname,\r
-                                          &cb)))\r
-        return NULL;\r
-    \r
-    t = wcsrchr(idname, L'@');\r
-    if (t == NULL)\r
-        return NULL;\r
-\r
-    StringCbCopy(filter_data.realm, sizeof(filter_data.realm),\r
-                 t + 1);\r
-    filter_data.identity = identity;\r
-\r
-    if (KHM_FAILED(kcdb_credset_find_filtered(credset,\r
-                                              -1,\r
-                                              krb4_tgt_filter,\r
-                                              &filter_data,\r
-                                              &result,\r
-                                              NULL)))\r
-        return NULL;\r
-    else\r
-        return result;\r
-}\r
-\r
-long\r
-khm_convert524(khm_handle identity)\r
-{\r
-#ifdef NO_KRB5\r
-    return(0);\r
-#else\r
-    krb5_context ctx = 0;\r
-    krb5_error_code code = 0;\r
-    int icode = 0;\r
-    krb5_principal me = 0;\r
-    krb5_principal server = 0;\r
-    krb5_creds *v5creds = 0;\r
-    krb5_creds increds;\r
-    krb5_ccache cc = 0;\r
-    CREDENTIALS * v4creds = NULL;\r
-    static int init_ets = 1;\r
-\r
-    if (!pkrb5_init_context ||\r
-        !pkrb_in_tkt ||\r
-        !pkrb524_init_ets ||\r
-        !pkrb524_convert_creds_kdc)\r
-        return 0;\r
-\r
-    v4creds = (CREDENTIALS *) malloc(sizeof(CREDENTIALS));\r
-    memset((char *) v4creds, 0, sizeof(CREDENTIALS));\r
-\r
-    memset((char *) &increds, 0, sizeof(increds));\r
-    /*\r
-      From this point on, we can goto cleanup because increds is\r
-      initialized.\r
-    */\r
-\r
-    code = khm_krb5_initialize(identity, &ctx, &cc);\r
-    if (code)\r
-        goto cleanup;\r
-\r
-    if ( init_ets ) {\r
-        pkrb524_init_ets(ctx);\r
-        init_ets = 0;\r
-    }\r
-\r
-    if (code = pkrb5_cc_get_principal(ctx, cc, &me))\r
-        goto cleanup;\r
-\r
-    if ((code = pkrb5_build_principal(ctx,\r
-                                      &server,\r
-                                      krb5_princ_realm(ctx, me)->length,\r
-                                      krb5_princ_realm(ctx, me)->data,\r
-                                      "krbtgt",\r
-                                      krb5_princ_realm(ctx, me)->data,\r
-                                      NULL))) {\r
-        goto cleanup;\r
-    }\r
-    \r
-    increds.client = me;\r
-    increds.server = server;\r
-    increds.times.endtime = 0;\r
-    increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC;\r
-    if ((code = pkrb5_get_credentials(ctx, 0,\r
-                                      cc,\r
-                                      &increds,\r
-                                      &v5creds))) {\r
-        goto cleanup;\r
-    }\r
-\r
-    if ((icode = pkrb524_convert_creds_kdc(ctx,\r
-                                           v5creds,\r
-                                           v4creds))) {\r
-        goto cleanup;\r
-    }\r
-\r
-    /* initialize ticket cache */\r
-    if ((icode = pkrb_in_tkt(v4creds->pname, v4creds->pinst, v4creds->realm)\r
-         != KSUCCESS)) {\r
-        goto cleanup;\r
-    }\r
-\r
-    /* stash ticket, session key, etc. for future use */\r
-    if ((icode = pkrb_save_credentials(v4creds->service,\r
-                                       v4creds->instance,\r
-                                       v4creds->realm,\r
-                                       v4creds->session,\r
-                                       v4creds->lifetime,\r
-                                       v4creds->kvno,\r
-                                       &(v4creds->ticket_st),\r
-                                       v4creds->issue_date))) {\r
-        goto cleanup;\r
-    }\r
-\r
- cleanup:\r
-    memset(v4creds, 0, sizeof(v4creds));\r
-    free(v4creds);\r
-\r
-    if (v5creds) {\r
-        pkrb5_free_creds(ctx, v5creds);\r
-    }\r
-    if (increds.client == me)\r
-        me = 0;\r
-    if (increds.server == server)\r
-        server = 0;\r
-\r
-    if (ctx)\r
-        pkrb5_free_cred_contents(ctx, &increds);\r
-\r
-    if (server) {\r
-        pkrb5_free_principal(ctx, server);\r
-    }\r
-\r
-    if (me) {\r
-        pkrb5_free_principal(ctx, me);\r
-    }\r
-\r
-    if (ctx && cc)\r
-        pkrb5_cc_close(ctx, cc);\r
-\r
-    if (ctx) {\r
-        pkrb5_free_context(ctx);\r
-    }\r
-\r
-    return (code || icode);\r
-#endif /* NO_KRB5 */    \r
-}\r
-\r
-long\r
-khm_krb4_kinit(char * aname,\r
-               char * inst,\r
-               char * realm,\r
-               long lifetime,\r
-               char * password) {\r
-\r
-    wchar_t * functionName = NULL;\r
-    wchar_t * err_context = NULL;\r
-    int rc4 = 0;\r
-    int msg = 0;\r
-\r
-    if (pkname_parse == NULL) {\r
-        goto cleanup;\r
-    }\r
-\r
-    err_context = L"getting realm";\r
-    if (!*realm && (rc4  = (int)(*pkrb_get_lrealm)(realm, 1))) {\r
-        functionName = L"krb_get_lrealm()";\r
-        msg = IDS_ERR_REALM;\r
-        goto cleanup;\r
-    }\r
-\r
-    err_context = L"checking principal";\r
-    if ((!*aname) || (!(rc4  = (int)(*pk_isname)(aname)))) {\r
-        functionName = L"krb_get_lrealm()";\r
-        msg = IDS_ERR_PRINCIPAL;\r
-        goto cleanup;\r
-    }\r
-\r
-    /* optional instance */\r
-    if (!(rc4 = (int)(*pk_isinst)(inst))) {\r
-        functionName = L"k_isinst()";\r
-        msg = IDS_ERR_INVINST;\r
-        goto cleanup;\r
-    }\r
-\r
-    if (!(rc4 = (int)(*pk_isrealm)(realm))) {\r
-        functionName = L"k_isrealm()";\r
-        msg = IDS_ERR_REALM;\r
-        goto cleanup;\r
-    }\r
-\r
-    khm_krb4_set_def_tkt_string();\r
-\r
-    err_context = L"fetching ticket";  \r
-    rc4 = (*pkrb_get_pw_in_tkt)(aname, inst, realm, "krbtgt", realm, \r
-                                lifetime, password);\r
-\r
-    if (rc4) /* XXX: do we want: && (rc != NO_TKT_FIL) as well? */ { \r
-        functionName = L"krb_get_pw_in_tkt()";\r
-        msg = IDS_ERR_PWINTKT;\r
-        goto cleanup;\r
-    }\r
-\r
-    return 0;\r
-\r
- cleanup:\r
-    {\r
-        _report_sr0(KHERR_ERROR, msg);\r
-        _location(functionName);\r
-    }\r
-    return rc4;\r
-}\r
-\r
-\r
-int khm_krb4_kdestroy(void) {\r
-    int k_errno = 0;\r
-\r
-    if (pdest_tkt != NULL)\r
-    {\r
-        k_errno = (*pdest_tkt)();\r
-        if (k_errno && (k_errno != RET_TKFIL))\r
-            return KRBERR(k_errno);\r
-    }\r
-\r
-    return k_errno;\r
-}\r
+/*
+* Copyright (c) 2005 Massachusetts Institute of Technology
+*
+* Permission is hereby granted, free of charge, to any person
+* obtaining a copy of this software and associated documentation
+* files (the "Software"), to deal in the Software without
+* restriction, including without limitation the rights to use, copy,
+* modify, merge, publish, distribute, sublicense, and/or sell copies
+* of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+* ACTION OF 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 <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    ptktname[MAX_PATH + 5];
+    char    pname[ANAME_SZ];
+    char    pinst[INST_SZ];
+    char    prealm[REALM_SZ];
+    wchar_t wbuf[256];
+    int     k_errno = 0;
+    CREDENTIALS c;
+    int newtickets = 0;
+    int open = 0;
+    khm_handle ident = NULL;
+    khm_handle cred = NULL;
+    time_t tt;
+    FILETIME ft;
+
+    kcdb_credset_flush(krb4_credset);
+
+    // 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)
+        goto collect;
+
+    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;
+    }
+
+    StringCchCopyA(ptktname, ARRAYLENGTH(ptktname), (*ptkt_string)());
+
+    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;
+    }
+
+    // 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));
+
+        AnsiStrToUnicode(wbuf, sizeof(wbuf), ptktname);
+        kcdb_cred_set_attr(cred, KCDB_ATTR_LOCATION, wbuf, KCDB_CBSIZE_AUTO);
+
+        kcdb_credset_add_cred(krb4_credset, cred, -1);
+
+       kcdb_cred_release(cred);
+    } // while
+
+ 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
+
+ collect:
+    kcdb_credset_collect(NULL, krb4_credset, ident, credtype_id_krb4, NULL);
+
+    return k_errno;
+}
+
+#define KRB_FILE                "KRB.CON"
+#define KRBREALM_FILE           "KRBREALM.CON"
+#define KRB5_FILE               "KRB5.INI"
+
+BOOL 
+khm_krb5_get_profile_file(LPSTR confname, UINT szConfname)
+{
+    char **configFile = NULL;
+    if (pkrb5_get_default_config_files(&configFile)) 
+    {
+        GetWindowsDirectoryA(confname,szConfname);
+        confname[szConfname-1] = '\0';
+
+        StringCchCatA(confname, szConfname, "\\");
+        StringCchCatA(confname, szConfname, KRB5_FILE);
+
+        return FALSE;
+    }
+    
+    *confname = 0;
+    
+    if (configFile)
+    {
+        StringCchCopyA(confname, szConfname, *configFile);
+        pkrb5_free_config_files(configFile); 
+    }
+    
+    if (!*confname)
+    {
+        GetWindowsDirectoryA(confname,szConfname);
+        confname[szConfname-1] = '\0';
+
+        StringCchCatA(confname, szConfname, "\\");
+        StringCchCatA(confname, szConfname, KRB5_FILE);
+    }
+    
+    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;
+
+        if (khm_krb5_get_profile_file(krbConFile, sizeof(krbConFile))) {
+            GetWindowsDirectoryA(krbConFile,sizeof(krbConFile));
+            krbConFile[MAX_PATH-1] = '\0';
+
+            StringCbCatA(krbConFile, sizeof(krbConFile), "\\");
+        }
+
+        pFind = strrchr(krbConFile, '\\');
+
+        if (pFind) {
+            *pFind = '\0';
+
+            StringCbCatA(krbConFile, sizeof(krbConFile), "\\");
+            StringCbCatA(krbConFile, sizeof(krbConFile), KRB_FILE);
+        } else {
+            krbConFile[0] = '\0';
+        }
+
+        StringCchCopyA(confname, szConfname, krbConFile);
+    } else if (hKrb4) { 
+        unsigned int size = szConfname;
+        memset(confname, '\0', szConfname);
+        if (!pkrb_get_krbconf2(confname, &size)) {
+            GetWindowsDirectoryA(confname,szConfname);
+            confname[szConfname-1] = '\0';
+            StringCchCatA(confname, szConfname, "\\");
+            StringCchCatA(confname, szConfname, KRB_FILE);
+        }
+    }
+
+    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_krb5_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 = PMALLOC(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)) && 
+#if _MSC_VER >= 1400
+            !fopen_s(&file, krb_conf, "rt")
+#else
+            (file = fopen(krb_conf, "rt"))
+#endif
+            )
+        {
+            char lineBuf[256];
+
+            /*TODO: compute the actual required buffer size instead of hardcoding */
+            cbsize = 16384; // arbitrary
+            rlist = PMALLOC(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 = PMALLOC(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;
+}
+
+static
+char *
+make_postfix(const char * base,
+             const char * postfix,
+             char ** rcopy)
+{
+    size_t base_size;
+    size_t ret_size;
+    char * copy = 0;
+    char * ret = 0;
+    size_t t;
+
+    if (FAILED(StringCbLengthA(base, STRSAFE_MAX_CCH * sizeof(char), &t)))
+        goto cleanup;
+
+    base_size = t + 1;
+
+    if (FAILED(StringCbLengthA(postfix, STRSAFE_MAX_CCH * sizeof(char), &t)))
+        goto cleanup;
+
+    ret_size = base_size + t + 1;
+
+    copy = malloc(base_size);
+    ret = malloc(ret_size);
+
+    if (!copy || !ret)
+        goto cleanup;
+
+    StringCbCopyNA(copy, base_size, base, base_size);
+    StringCbCopyNA(ret, ret_size, base, base_size);
+    StringCbCopyNA(ret + (base_size - 1), ret_size - (base_size - 1),
+                   postfix, ret_size - (base_size - 1));
+
+ cleanup:
+    if (!copy || !ret) {
+        if (copy)
+            free(copy);
+        if (ret)
+            free(ret);
+        copy = ret = 0;
+    }
+    // INVARIANT: (ret ==> copy) && (copy ==> ret)
+    *rcopy = copy;
+    return ret;
+}
+
+void
+khm_krb4_set_def_tkt_string(void) {
+    wchar_t wtkt_string[MAX_PATH];
+    char tkt_string[MAX_PATH];
+    khm_size cb;
+
+    cb = sizeof(wtkt_string);
+
+    if (KHM_FAILED(khc_read_string(csp_params, L"TktString",
+                                   wtkt_string, &cb)) ||
+        wtkt_string[0] == L'\0') {
+
+        pkrb_set_tkt_string(0);
+
+    } else {
+
+        UnicodeStrToAnsi(tkt_string, sizeof(tkt_string),
+                         wtkt_string);
+        pkrb_set_tkt_string(tkt_string);        
+    }
+}
+
+
+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;
+}
+
+long
+khm_krb4_changepwd(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;
+}
+
+struct tgt_filter_data {
+    khm_handle identity;
+    wchar_t realm[KCDB_IDENT_MAXCCH_NAME];
+};
+
+khm_int32 KHMAPI
+krb4_tgt_filter(khm_handle cred, khm_int32 flags, void * rock) {
+    struct tgt_filter_data * pdata;
+    wchar_t credname[KCDB_MAXCCH_NAME];
+    wchar_t * t;
+    khm_size cb;
+    khm_int32 ctype;
+
+    pdata = (struct tgt_filter_data *) rock;
+    cb = sizeof(credname);
+
+    if (KHM_FAILED(kcdb_cred_get_type(cred, &ctype)) ||
+        ctype != credtype_id_krb4)
+        return 0;
+
+    if (KHM_FAILED(kcdb_cred_get_name(cred, credname, &cb)))
+        return 0;
+
+    if (wcsncmp(credname, L"krbtgt.", 7))
+        return 0;
+
+    t = wcsrchr(credname, L'@');
+    if (t == NULL)
+        return 0;
+
+    if (wcscmp(t+1, pdata->realm))
+        return 0;
+
+    return 1;
+}
+
+khm_handle
+khm_krb4_find_tgt(khm_handle credset, khm_handle identity) {
+    khm_handle result = NULL;
+    wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
+    wchar_t * t;
+    khm_size cb;
+    struct tgt_filter_data filter_data;
+
+    cb = sizeof(idname);
+
+    if (KHM_FAILED(kcdb_identity_get_name(identity,
+                                          idname,
+                                          &cb)))
+        return NULL;
+    
+    t = wcsrchr(idname, L'@');
+    if (t == NULL)
+        return NULL;
+
+    StringCbCopy(filter_data.realm, sizeof(filter_data.realm),
+                 t + 1);
+    filter_data.identity = identity;
+
+    if (KHM_FAILED(kcdb_credset_find_filtered(credset,
+                                              -1,
+                                              krb4_tgt_filter,
+                                              &filter_data,
+                                              &result,
+                                              NULL)))
+        return NULL;
+    else
+        return result;
+}
+
+long
+khm_convert524(khm_handle identity)
+{
+#ifdef NO_KRB5
+    return(0);
+#else
+    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.
+    */
+
+    code = khm_krb5_initialize(identity, &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;
+
+    if (ctx)
+        pkrb5_free_cred_contents(ctx, &increds);
+
+    if (server) {
+        pkrb5_free_principal(ctx, server);
+    }
+
+    if (me) {
+        pkrb5_free_principal(ctx, me);
+    }
+
+    if (ctx && cc)
+        pkrb5_cc_close(ctx, cc);
+
+    if (ctx) {
+        pkrb5_free_context(ctx);
+    }
+
+    return (code || icode);
+#endif /* NO_KRB5 */    
+}
+
+long
+khm_krb4_kinit(char * aname,
+               char * inst,
+               char * realm,
+               long lifetime,
+               char * password) {
+
+    wchar_t * functionName = NULL;
+    wchar_t * err_context = NULL;
+    int rc4 = 0;
+    int msg = 0;
+
+    if (pkname_parse == NULL) {
+        goto cleanup;
+    }
+
+    err_context = L"getting realm";
+    if (!*realm && (rc4  = (int)(*pkrb_get_lrealm)(realm, 1))) {
+        functionName = L"krb_get_lrealm()";
+        msg = IDS_ERR_REALM;
+        goto cleanup;
+    }
+
+    err_context = L"checking principal";
+    if ((!*aname) || (!(rc4  = (int)(*pk_isname)(aname)))) {
+        functionName = L"krb_get_lrealm()";
+        msg = IDS_ERR_PRINCIPAL;
+        goto cleanup;
+    }
+
+    /* optional instance */
+    if (!(rc4 = (int)(*pk_isinst)(inst))) {
+        functionName = L"k_isinst()";
+        msg = IDS_ERR_INVINST;
+        goto cleanup;
+    }
+
+    if (!(rc4 = (int)(*pk_isrealm)(realm))) {
+        functionName = L"k_isrealm()";
+        msg = IDS_ERR_REALM;
+        goto cleanup;
+    }
+
+    khm_krb4_set_def_tkt_string();
+
+    err_context = L"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 = L"krb_get_pw_in_tkt()";
+        msg = IDS_ERR_PWINTKT;
+        goto cleanup;
+    }
+
+    return 0;
+
+ cleanup:
+    {
+        _report_sr0(KHERR_ERROR, msg);
+        _location(functionName);
+    }
+    return rc4;
+}
+
+
+int khm_krb4_kdestroy(void) {
+    int k_errno = 0;
+
+    if (pdest_tkt != NULL)
+    {
+        k_errno = (*pdest_tkt)();
+        if (k_errno && (k_errno != RET_TKFIL))
+            return KRBERR(k_errno);
+    }
+
+    return k_errno;
+}
index 5ec11cc63318e67412693ba0fa2a1ed404f89caf..05ed3e75dc00021cff84ca2dd6eb6a50ce7cde82 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/* Adapted from multiple Leash header files */\r
-\r
-#ifndef __KHIMAIRA_KRB5FUNCS_H\r
-#define __KHIMAIRA_KRB5FUNCS_H\r
-\r
-#include<stdlib.h>\r
-#include<krb5.h>\r
-\r
-#include <windows.h>\r
-#define SECURITY_WIN32\r
-#include <security.h>\r
-\r
-#if _WIN32_WINNT < 0x0501\r
-#define KHM_SAVE_WIN32_WINNT _WIN32_WINNT\r
-#undef _WIN32_WINNT\r
-#define _WIN32_WINNT 0x0501\r
-#endif\r
-#include<ntsecapi.h>\r
-#ifdef KHM_SAVE_WIN32_WINNT\r
-#undef _WIN32_WINNT\r
-#define _WIN32_WINNT KHM_SAVE_WIN32_WINNT\r
-#undef KHM_SAVE_WIN32_WINNT\r
-#endif\r
-\r
-#include <krb5common.h>\r
-\r
-#define LEASH_DEBUG_CLASS_GENERIC   0\r
-#define LEASH_DEBUG_CLASS_KRB4      1\r
-#define LEASH_DEBUG_CLASS_KRB4_APP  2\r
-\r
-#define LEASH_PRIORITY_LOW  0\r
-#define LEASH_PRIORITY_HIGH 1\r
-\r
-#define KRB5_DEFAULT_LIFE            60*60*10 /* 10 hours */\r
-\r
-\r
-long\r
-khm_convert524(khm_handle identity);\r
-\r
-long\r
-khm_krb4_kinit(char * aname,\r
-               char * inst,\r
-               char * realm,\r
-               long lifetime,\r
-               char * password);    \r
-\r
-long \r
-khm_krb4_list_tickets(void);\r
-\r
-int khm_krb4_kdestroy(void);\r
-\r
-khm_handle\r
-khm_krb4_find_tgt(khm_handle credset, \r
-                  khm_handle identity);\r
-\r
-LONG\r
-write_registry_setting(\r
-    char* setting,\r
-    DWORD type,\r
-    void* buffer,\r
-    size_t size\r
-    );\r
-\r
-LONG\r
-read_registry_setting_user(\r
-    char* setting,\r
-    void* buffer,\r
-    size_t size\r
-    );\r
-\r
-LONG\r
-read_registry_setting(\r
-    char* setting,\r
-    void* buffer,\r
-    size_t size\r
-    );\r
-\r
-BOOL\r
-get_STRING_from_registry(\r
-    HKEY hBaseKey,\r
-    char * key,\r
-    char * value,\r
-    char * outbuf,\r
-    DWORD  outlen\r
-    );\r
-\r
-BOOL\r
-get_DWORD_from_registry(\r
-    HKEY hBaseKey,\r
-    char * key,\r
-    char * value,\r
-    DWORD * result\r
-    );\r
-\r
-int\r
-config_boolean_to_int(\r
-    const char *s\r
-    );\r
-\r
-void\r
-khm_krb4_set_def_tkt_string(void);\r
-\r
-wchar_t * khm_krb5_get_default_realm(void);\r
-wchar_t * khm_krb5_get_realm_list(void);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+
+#if _WIN32_WINNT < 0x0501
+#define KHM_SAVE_WIN32_WINNT _WIN32_WINNT
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501
+#endif
+#include<ntsecapi.h>
+#ifdef KHM_SAVE_WIN32_WINNT
+#undef _WIN32_WINNT
+#define _WIN32_WINNT KHM_SAVE_WIN32_WINNT
+#undef KHM_SAVE_WIN32_WINNT
+#endif
+
+#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 */
+
+
+long
+khm_convert524(khm_handle identity);
+
+long
+khm_krb4_kinit(char * aname,
+               char * inst,
+               char * realm,
+               long lifetime,
+               char * password);    
+
+long 
+khm_krb4_list_tickets(void);
+
+int khm_krb4_kdestroy(void);
+
+khm_handle
+khm_krb4_find_tgt(khm_handle credset, 
+                  khm_handle identity);
+
+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
+    );
+
+void
+khm_krb4_set_def_tkt_string(void);
+
+wchar_t * khm_krb5_get_default_realm(void);
+wchar_t * khm_krb5_get_realm_list(void);
+
+#endif
index 57e33a8d8b98ea4633da7b08eec4208970188ddb..7ab2d71f32550c024d1b239e5b1cdefbb5e6a025 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<krbcred.h>\r
-#include<kherror.h>\r
-\r
-kmm_module h_khModule; /* KMM's handle to this module */\r
-HINSTANCE hInstance;\r
-HMODULE hResModule;    /* HMODULE to the resource library */\r
-\r
-khm_int32 type_id_enctype       = -1;\r
-khm_int32 type_id_addr_list     = -1;\r
-khm_int32 type_id_krb5_flags    = -1;\r
-\r
-khm_int32 attr_id_key_enctype   = -1;\r
-khm_int32 attr_id_tkt_enctype   = -1;\r
-khm_int32 attr_id_addr_list     = -1;\r
-khm_int32 attr_id_krb5_flags    = -1;\r
-\r
-khm_handle csp_plugins          = NULL;\r
-khm_handle csp_krbcred          = NULL;\r
-khm_handle csp_params           = NULL;\r
-\r
-kmm_module_locale locales[] = {\r
-    LOCALE_DEF(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US), L"krb4cred_en_us.dll", KMM_MLOC_FLAG_DEFAULT)\r
-};\r
-\r
-int n_locales = ARRAYLENGTH(locales);\r
-\r
-/* These two probably should not do anything */\r
-void init_krb() {\r
-}\r
-\r
-void exit_krb() {\r
-}\r
-\r
-/* called by the NetIDMgr module manager */\r
-KHMEXP khm_int32 KHMAPI init_module(kmm_module h_module) {\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    kmm_plugin_reg pi;\r
-    wchar_t buf[256];\r
-\r
-    h_khModule = h_module;\r
-\r
-    rv = kmm_set_locale_info(h_module, locales, n_locales);\r
-    if(KHM_SUCCEEDED(rv)) {\r
-        hResModule = kmm_get_resource_hmodule(h_module);\r
-    } else\r
-        goto _exit;\r
-\r
-    ZeroMemory(&pi, sizeof(pi));\r
-    pi.name = KRB4_PLUGIN_NAME;\r
-    pi.type = KHM_PITYPE_CRED;\r
-    pi.icon = LoadImage(hResModule, MAKEINTRESOURCE(IDI_PLUGIN),\r
-                        IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE);\r
-    pi.flags = 0;\r
-    pi.msg_proc = krb4_cb;\r
-    pi.dependencies = KRB4_PLUGIN_DEPS;\r
-    pi.description = buf;\r
-    LoadString(hResModule, IDS_PLUGIN_DESC,\r
-               buf, ARRAYLENGTH(buf));\r
-    kmm_provide_plugin(h_module, &pi);\r
-\r
-    if(KHM_FAILED(rv = init_imports()))\r
-        goto _exit;\r
-\r
-    if(KHM_FAILED(rv = init_error_funcs()))\r
-        goto _exit;\r
-\r
-    rv = kmm_get_plugins_config(0, &csp_plugins);\r
-    if(KHM_FAILED(rv)) goto _exit;\r
-\r
-    rv = khc_load_schema(csp_plugins, schema_krbconfig);\r
-    if(KHM_FAILED(rv)) goto _exit;\r
-\r
-    rv = khc_open_space(csp_plugins, CSNAME_KRB4CRED, 0, &csp_krbcred);\r
-    if(KHM_FAILED(rv)) goto _exit;\r
-\r
-    rv = khc_open_space(csp_krbcred, CSNAME_PARAMS, 0, &csp_params);\r
-    if(KHM_FAILED(rv)) goto _exit;\r
-\r
- _exit:\r
-    return rv;\r
-}\r
-\r
-/* called by the NetIDMgr module manager */\r
-KHMEXP khm_int32 KHMAPI exit_module(kmm_module h_module) {\r
-    exit_imports();\r
-    exit_error_funcs();\r
-\r
-    if(csp_params) {\r
-        khc_close_space(csp_params);\r
-        csp_params = NULL;\r
-    }\r
-\r
-    if(csp_krbcred) {\r
-        khc_close_space(csp_krbcred);\r
-        csp_krbcred = NULL;\r
-    }\r
-\r
-    if(csp_plugins) {\r
-        khc_unload_schema(csp_plugins, schema_krbconfig);\r
-        khc_close_space(csp_plugins);\r
-        csp_plugins = NULL;\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;  /* the return code is ignored */\r
-}\r
-\r
-BOOL WINAPI DllMain(\r
-  HINSTANCE hinstDLL,\r
-  DWORD fdwReason,\r
-  LPVOID lpvReserved\r
-)\r
-{\r
-    switch(fdwReason) {\r
-        case DLL_PROCESS_ATTACH:\r
-            hInstance = hinstDLL;\r
-            init_krb();\r
-            break;\r
-\r
-        case DLL_PROCESS_DETACH:\r
-            exit_krb();\r
-            break;\r
-\r
-        case DLL_THREAD_ATTACH:\r
-            break;\r
-\r
-        case DLL_THREAD_DETACH:\r
-            break;\r
-    }\r
-\r
-    return TRUE;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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_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 = LoadImage(hResModule, MAKEINTRESOURCE(IDI_PLUGIN),
+                        IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE);
+    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;
+
+    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;
+}
index 54feae5b3690c6eee9fe7d039abc19f1ef588fb8..c2d477e0ee33e8b0304247f841427459654cbdea 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<krbcred.h>\r
-#include<kherror.h>\r
-#include<khmsgtypes.h>\r
-#include<khuidefs.h>\r
-#include<utils.h>\r
-#include<commctrl.h>\r
-#include<strsafe.h>\r
-#include<krb5.h>\r
-#include<assert.h>\r
-\r
-/* method identifiers should be contiguous */\r
-#define K4_METHOD_AUTO     0\r
-#define K4_METHOD_PASSWORD 1\r
-#define K4_METHOD_K524     2\r
-\r
-int method_to_id[] = {\r
-    IDC_NCK4_AUTO,\r
-    IDC_NCK4_PWD,\r
-    IDC_NCK4_K524\r
-};\r
-\r
-typedef struct tag_k4_dlg_data {\r
-    HWND hwnd;\r
-    khui_new_creds * nc;\r
-    khui_new_creds_by_type * nct;\r
-\r
-    khm_boolean      k4_enabled;\r
-    khm_int32        method;\r
-    time_t           lifetime;\r
-} k4_dlg_data;\r
-\r
-void k4_update_display(k4_dlg_data * d, BOOL update_methods) {\r
-    CheckDlgButton(d->hwnd, IDC_NCK4_OBTAIN,\r
-                   (d->k4_enabled)?BST_CHECKED: BST_UNCHECKED);\r
-\r
-    if (d->k4_enabled) {\r
-        EnableWindow(GetDlgItem(d->hwnd, IDC_NCK4_AUTO), TRUE);\r
-        EnableWindow(GetDlgItem(d->hwnd, IDC_NCK4_K524), TRUE);\r
-        EnableWindow(GetDlgItem(d->hwnd, IDC_NCK4_PWD ), TRUE);\r
-    } else {\r
-        EnableWindow(GetDlgItem(d->hwnd, IDC_NCK4_AUTO), FALSE);\r
-        EnableWindow(GetDlgItem(d->hwnd, IDC_NCK4_K524), FALSE);\r
-        EnableWindow(GetDlgItem(d->hwnd, IDC_NCK4_PWD ), FALSE);\r
-    }\r
-\r
-#ifdef DEBUG\r
-    assert(d->method >= 0 && d->method < ARRAYLENGTH(method_to_id));\r
-#endif\r
-\r
-    CheckRadioButton(d->hwnd, IDC_NCK4_AUTO, IDC_NCK4_PWD, method_to_id[d->method]);\r
-\r
-    khui_cw_enable_type(d->nc, credtype_id_krb4, d->k4_enabled);\r
-}\r
-\r
-void k4_update_data(k4_dlg_data * d) {\r
-    int i;\r
-    khm_boolean oldstate;\r
-\r
-    oldstate = d->k4_enabled;\r
-\r
-    if (IsDlgButtonChecked(d->hwnd, IDC_NCK4_OBTAIN) == BST_CHECKED)\r
-        d->k4_enabled = TRUE;\r
-    else\r
-        d->k4_enabled = FALSE;\r
-\r
-    if ((oldstate && !d->k4_enabled) ||\r
-        (!oldstate && d->k4_enabled)) {\r
-\r
-        khui_cw_enable_type(d->nc, credtype_id_krb4, d->k4_enabled);\r
-    }\r
-\r
-    d->method = K4_METHOD_AUTO;\r
-\r
-    for (i=K4_METHOD_AUTO; i<=K4_METHOD_K524; i++) {\r
-        if (IsDlgButtonChecked(d->hwnd, method_to_id[i]) == BST_CHECKED) {\r
-            d->method = i;\r
-            break;\r
-        }\r
-    }\r
-}\r
-\r
-khm_boolean k4_should_identity_get_k4(khm_handle ident) {\r
-    khm_int32 idflags = 0;\r
-    khm_int32 t = TRUE;\r
-    khm_handle csp_ident = NULL;\r
-    khm_handle csp_k4 = NULL;\r
-    khm_boolean get_k4 = TRUE;\r
-    khm_boolean id_spec = FALSE;\r
-\r
-    if (KHM_FAILED(kcdb_identity_get_flags(ident, &idflags)))\r
-        return FALSE;\r
-\r
-    if (!(idflags & KCDB_IDENT_FLAG_DEFAULT)) {\r
-        /* we only support k4 for one identity, and that is the\r
-           default identity.  If we are trying to get tickets for a\r
-           non-default identity, then we start off as disabled unless\r
-           there is no default identity. */\r
-\r
-        khm_handle defident = NULL;\r
-\r
-        if (KHM_SUCCEEDED(kcdb_identity_get_default(&defident))) {\r
-            kcdb_identity_release(defident);\r
-\r
-            return FALSE;\r
-        }\r
-    }\r
-\r
-    if (KHM_SUCCEEDED(kcdb_identity_get_config(ident, 0, &csp_ident))) {\r
-        if (KHM_SUCCEEDED(khc_open_space(csp_ident, CSNAME_KRB4CRED, 0,\r
-                                         &csp_k4))) {\r
-            khm_int32 t = 0;\r
-\r
-            if (KHM_SUCCEEDED(khc_read_int32(csp_k4, L"Krb4NewCreds", &t))) {\r
-                get_k4 = !!t;\r
-                id_spec = TRUE;\r
-            }\r
-\r
-            khc_close_space(csp_k4);\r
-        }\r
-        khc_close_space(csp_ident);\r
-    }\r
-\r
-    /* if there was a value specified for the identity, then that\r
-       takes precedence. */\r
-    if (id_spec || !get_k4)\r
-        return get_k4;\r
-\r
-    if (KHM_SUCCEEDED(khc_read_int32(csp_params, L"Krb4NewCreds", &t)) &&\r
-        !t)\r
-        return FALSE;\r
-\r
-    return TRUE;\r
-}\r
-\r
-void k4_read_identity_data(k4_dlg_data * d) {\r
-    khm_handle csp_ident = NULL;\r
-    khm_handle csp_k4 = NULL;\r
-\r
-    khm_int32 idflags = 0;\r
-    khm_int32 t;\r
-\r
-    if (KHM_SUCCEEDED(khc_read_int32(csp_params, L"Krb4NewCreds", &t)))\r
-        d->k4_enabled = !!t;\r
-    else\r
-        d->k4_enabled = TRUE;\r
-\r
-    if (KHM_SUCCEEDED(khc_read_int32(csp_params, L"Krb4Method", &t)))\r
-        d->method = t;\r
-    else\r
-        d->method = K4_METHOD_AUTO;\r
-\r
-    if (KHM_SUCCEEDED(khc_read_int32(csp_params, L"DefaultLifetime", &t)))\r
-        d->lifetime = t;\r
-    else\r
-        d->lifetime = 10 * 60 * 60; /* 10 hours */\r
-\r
-    if (d->nc->n_identities > 0 &&\r
-        d->nc->identities[0]) {\r
-\r
-        if (KHM_SUCCEEDED(kcdb_identity_get_config(d->nc->identities[0],\r
-                                                   0,\r
-                                                   &csp_ident))) {\r
-\r
-            khc_open_space(csp_ident, CSNAME_KRB4CRED, 0, &csp_k4);\r
-            \r
-            if (csp_k4) {\r
-                if (KHM_SUCCEEDED(khc_read_int32(csp_k4, L"Krb4NewCreds", &t)))\r
-                    d->k4_enabled = !!t;\r
-                if (KHM_SUCCEEDED(khc_read_int32(csp_k4, L"Krb4Method", &t)))\r
-                    d->method = t;\r
-                khc_close_space(csp_k4);\r
-            }\r
-\r
-            khc_close_space(csp_ident);\r
-        }\r
-\r
-        if (d->k4_enabled) {\r
-            d->k4_enabled = k4_should_identity_get_k4(d->nc->identities[0]);\r
-        }\r
-    } else {\r
-        d->k4_enabled = FALSE;\r
-    }\r
-\r
-    if (d->method < 0 || d->method > K4_METHOD_K524)\r
-        d->method = K4_METHOD_AUTO;\r
-}\r
-\r
-void k4_write_identity_data(k4_dlg_data * d) {\r
-    khm_handle csp_ident = NULL;\r
-    khm_handle csp_k4 = NULL;\r
-\r
-    if (d->nc->n_identities > 0 &&\r
-        d->nc->identities[0] &&\r
-        KHM_SUCCEEDED(kcdb_identity_get_config(d->nc->identities[0],\r
-                                               KHM_FLAG_CREATE,\r
-                                               &csp_ident))) {\r
-        khc_open_space(csp_ident, CSNAME_KRB4CRED,\r
-                       KHM_FLAG_CREATE | KCONF_FLAG_WRITEIFMOD,\r
-                       &csp_k4);\r
-\r
-        if (csp_k4) {\r
-            khc_write_int32(csp_k4, L"Krb4NewCreds", !!d->k4_enabled);\r
-            khc_write_int32(csp_k4, L"Krb4Method", d->method);\r
-\r
-            khc_close_space(csp_k4);\r
-        }\r
-\r
-        khc_close_space(csp_ident);\r
-    }\r
-}\r
-\r
-void k4_handle_wmnc_notify(k4_dlg_data * d,\r
-                           WPARAM wParam,\r
-                           LPARAM lParam) {\r
-    switch(HIWORD(wParam)) {\r
-    case WMNC_UPDATE_CREDTEXT:\r
-        {\r
-            if (d->nct->credtext) {\r
-                PFREE(d->nct->credtext);\r
-                d->nct->credtext = NULL;\r
-            }\r
-\r
-            if (d->nc->n_identities > 0 &&\r
-                d->nc->identities[0]) {\r
-\r
-                khm_int32 flags = 0;\r
-                wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-                wchar_t * atsign;\r
-                wchar_t * realm;\r
-                khm_size cb;\r
-\r
-                kcdb_identity_get_flags(d->nc->identities[0], &flags);\r
-\r
-                if (!(flags & KCDB_IDENT_FLAG_VALID)) {\r
-                    break;\r
-                }\r
-\r
-                cb = sizeof(idname);\r
-                kcdb_identity_get_name(d->nc->identities[0], idname,\r
-                                       &cb);\r
-\r
-                atsign = wcsrchr(idname, L'@');\r
-\r
-                if (atsign == NULL || !atsign[1])\r
-                    break;\r
-\r
-                realm = ++atsign;\r
-\r
-                if (d->k4_enabled) {\r
-                    wchar_t wmethod[128];\r
-                    wchar_t wfmt[128];\r
-                    wchar_t wct[512];\r
-\r
-                    LoadString(hResModule, IDS_CT_TGTFOR,\r
-                               wfmt, ARRAYLENGTH(wfmt));\r
-\r
-                    if (d->method == K4_METHOD_AUTO)\r
-                        LoadString(hResModule, IDS_METHOD_AUTO, wmethod,\r
-                                   ARRAYLENGTH(wmethod));\r
-                    else if (d->method == K4_METHOD_PASSWORD)\r
-                        LoadString(hResModule, IDS_METHOD_PWD, wmethod,\r
-                                   ARRAYLENGTH(wmethod));\r
-                    else if (d->method == K4_METHOD_K524)\r
-                        LoadString(hResModule, IDS_METHOD_K524, wmethod,\r
-                                   ARRAYLENGTH(wmethod));\r
-                    else {\r
-                        assert(FALSE);\r
-                    }\r
-\r
-                    StringCbPrintf(wct, sizeof(wct), wfmt, realm, wmethod);\r
-\r
-                    StringCbLength(wct, sizeof(wct), &cb);\r
-                    cb += sizeof(wchar_t);\r
-\r
-                    d->nct->credtext = PMALLOC(cb);\r
-\r
-                    StringCbCopy(d->nct->credtext, cb, wct);\r
-                } else {\r
-                    wchar_t wct[256];\r
-\r
-                    LoadString(hResModule, IDS_CT_DISABLED,\r
-                               wct, ARRAYLENGTH(wct));\r
-\r
-                    StringCbLength(wct, sizeof(wct), &cb);\r
-                    cb += sizeof(wchar_t);\r
-\r
-                    d->nct->credtext = PMALLOC(cb);\r
-\r
-                    StringCbCopy(d->nct->credtext, cb, wct);\r
-                }\r
-            }\r
-            /* no identities were selected.  it is not the\r
-               responsibility of krb4 to complain about this. */\r
-        }\r
-        break;\r
-\r
-    case WMNC_IDENTITY_CHANGE:\r
-        k4_read_identity_data(d);\r
-        k4_update_display(d, TRUE);\r
-        break;\r
-\r
-    case WMNC_CREDTEXT_LINK:\r
-        {\r
-            wchar_t wid[KHUI_MAXCCH_HTLINK_FIELD];\r
-            wchar_t * wids;\r
-            khui_htwnd_link * l;\r
-\r
-            l = (khui_htwnd_link *) lParam;\r
-\r
-            StringCchCopyN(wid, ARRAYLENGTH(wid), l->id, l->id_len);\r
-            wids = wcschr(wid, L':');\r
-\r
-            if (!wids)\r
-                break;\r
-            else\r
-                wids++;\r
-\r
-            if (!wcscmp(wids, L"Enable")) {\r
-                d->k4_enabled = TRUE;\r
-\r
-                k4_update_display(d, TRUE);\r
-                khui_cw_enable_type(d->nc, credtype_id_krb4, TRUE);\r
-            }\r
-        }\r
-        break;\r
-    }\r
-}\r
-\r
-INT_PTR CALLBACK k4_nc_dlg_proc(HWND hwnd,\r
-                                UINT uMsg,\r
-                                WPARAM wParam,\r
-                                LPARAM lParam) {\r
-\r
-    k4_dlg_data * d;\r
-\r
-    switch(uMsg) {\r
-    case WM_INITDIALOG:\r
-        {\r
-            d = PMALLOC(sizeof(*d));\r
-            ZeroMemory(d, sizeof(*d));\r
-\r
-            d->nc = (khui_new_creds *) lParam;\r
-            khui_cw_find_type(d->nc, credtype_id_krb4, &d->nct);\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-            SetWindowLongPtr(hwnd, DWLP_USER, (LPARAM) d);\r
-#pragma warning(pop)\r
-\r
-            d->nct->aux = (LPARAM) d;\r
-            d->hwnd = hwnd;\r
-\r
-            d->k4_enabled = TRUE;\r
-            d->method = K4_METHOD_AUTO;\r
-\r
-            k4_update_display(d, TRUE);\r
-        }\r
-        break;\r
-\r
-    case WM_COMMAND:\r
-        {\r
-            if (HIWORD(wParam) == BN_CLICKED) {\r
-                d = (k4_dlg_data *) (LONG_PTR)\r
-                    GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-                k4_update_data(d);\r
-\r
-                if (LOWORD(wParam) == IDC_NCK4_OBTAIN) {\r
-                    k4_update_display(d, TRUE);\r
-                }\r
-\r
-                return TRUE;\r
-            }\r
-        }\r
-        break;\r
-\r
-    case KHUI_WM_NC_NOTIFY:\r
-        {\r
-            d = (k4_dlg_data *) (LONG_PTR)\r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-            k4_handle_wmnc_notify(d, wParam, lParam);\r
-        }\r
-        break;\r
-\r
-    case WM_DESTROY:\r
-        {\r
-            d = (k4_dlg_data *) (LONG_PTR)\r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-            d->nct->aux = 0;\r
-\r
-            PFREE(d);\r
-        }\r
-        break;\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-khm_int32\r
-krb4_msg_newcred(khm_int32 msg_type, khm_int32 msg_subtype,\r
-                 khm_ui_4 uparam, void * vparam) {\r
-\r
-    switch(msg_subtype) {\r
-    case KMSG_CRED_NEW_CREDS:\r
-        {\r
-            khui_new_creds * nc;\r
-            khui_new_creds_by_type * nct;\r
-            khm_size cbsize;\r
-            wchar_t wbuf[256];\r
-\r
-            nc = (khui_new_creds *) vparam;\r
-\r
-            nct = PMALLOC(sizeof(*nct));\r
-#ifdef DEBUG\r
-            assert(nct);\r
-#endif\r
-            ZeroMemory(nct, sizeof(*nct));\r
-\r
-            nct->type = credtype_id_krb4;\r
-            nct->ordinal = 3;\r
-            LoadString(hResModule, IDS_NC_K4_SHORT,\r
-                       wbuf, ARRAYLENGTH(wbuf));\r
-            StringCbLength(wbuf, sizeof(wbuf), &cbsize);\r
-            cbsize += sizeof(wchar_t);\r
-\r
-            nct->name = PMALLOC(cbsize);\r
-            StringCbCopy(nct->name, cbsize, wbuf);\r
-\r
-            nct->type_deps[nct->n_type_deps++] = credtype_id_krb5;\r
-\r
-            nct->h_module = hResModule;\r
-            nct->dlg_proc = k4_nc_dlg_proc;\r
-            nct->dlg_template = MAKEINTRESOURCE(IDD_NC_KRB4);\r
-\r
-            khui_cw_add_type(nc, nct);\r
-        }\r
-        break;\r
-\r
-    case KMSG_CRED_RENEW_CREDS:\r
-        {\r
-            khui_new_creds * nc;\r
-            khui_new_creds_by_type * nct;\r
-            khm_size cbsize;\r
-            wchar_t wbuf[256];\r
-\r
-            nc = (khui_new_creds *) vparam;\r
-\r
-            if (!nc->ctx.identity)\r
-                break;\r
-\r
-            nct = PMALLOC(sizeof(*nct));\r
-#ifdef DEBUG\r
-            assert(nct);\r
-#endif\r
-\r
-            ZeroMemory(nct, sizeof(*nct));\r
-\r
-            nct->type = credtype_id_krb4;\r
-            nct->ordinal = 3;\r
-            LoadString(hResModule, IDS_NC_K4_SHORT,\r
-                       wbuf, ARRAYLENGTH(wbuf));\r
-            StringCbLength(wbuf, sizeof(wbuf), &cbsize);\r
-            cbsize += sizeof(wchar_t);\r
-\r
-            nct->name = PMALLOC(cbsize);\r
-            StringCbCopy(nct->name, cbsize, wbuf);\r
-\r
-            nct->type_deps[nct->n_type_deps++] = credtype_id_krb5;\r
-\r
-            khui_cw_add_type(nc, nct);\r
-        }\r
-        break;\r
-\r
-    case KMSG_CRED_DIALOG_SETUP:\r
-        break;\r
-\r
-    case KMSG_CRED_PROCESS:\r
-        {\r
-            khui_new_creds * nc;\r
-            khui_new_creds_by_type * nct = NULL;\r
-            khm_handle ident = NULL;\r
-            k4_dlg_data * d = NULL;\r
-            long code = 0;\r
-            wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-            khm_size cb;\r
-\r
-            nc = (khui_new_creds *) vparam;\r
-            if (KHM_FAILED(khui_cw_find_type(nc, credtype_id_krb4, &nct)))\r
-                break;\r
-\r
-            if (nc->subtype == KMSG_CRED_NEW_CREDS ||\r
-                nc->subtype == KMSG_CRED_RENEW_CREDS) {\r
-                khm_int32 method;\r
-\r
-                if (nc->subtype == KMSG_CRED_NEW_CREDS) {\r
-\r
-                    d = (k4_dlg_data *) nct->aux;\r
-                    if (!d ||\r
-                        nc->n_identities == 0 ||\r
-                        nc->identities[0] == NULL ||\r
-                        nc->result != KHUI_NC_RESULT_PROCESS) {\r
-                        khui_cw_set_response(nc, credtype_id_krb4,\r
-                                             KHUI_NC_RESPONSE_SUCCESS |\r
-                                             KHUI_NC_RESPONSE_EXIT);\r
-                        break;\r
-                    }\r
-\r
-                    if (!d->k4_enabled) {\r
-                        k4_write_identity_data(d);\r
-                        khui_cw_set_response(nc, credtype_id_krb4,\r
-                                             KHUI_NC_RESPONSE_SUCCESS |\r
-                                             KHUI_NC_RESPONSE_EXIT);\r
-                        break;\r
-                    }\r
-\r
-                    method = d->method;\r
-                    ident = nc->identities[0];\r
-\r
-                    cb = sizeof(idname);\r
-                    kcdb_identity_get_name(ident, idname, &cb);\r
-                    _begin_task(0);\r
-                    _report_mr2(KHERR_NONE, MSG_K4_NEW_CREDS,\r
-                                _cstr(ident), _int32(method));\r
-                    _resolve();\r
-                    _describe();\r
-\r
-                } else if (nc->subtype == KMSG_CRED_RENEW_CREDS) {\r
-\r
-                    if ((nc->ctx.scope == KHUI_SCOPE_IDENT &&\r
-                         nc->ctx.identity != NULL) ||\r
-\r
-                        (nc->ctx.scope == KHUI_SCOPE_CREDTYPE &&\r
-                         nc->ctx.cred_type == credtype_id_krb4 &&\r
-                         nc->ctx.identity != NULL) ||\r
-\r
-                        (nc->ctx.scope == KHUI_SCOPE_CRED &&\r
-                         nc->ctx.cred_type == credtype_id_krb4 &&\r
-                         nc->ctx.identity != NULL &&\r
-                         nc->ctx.cred != NULL)) {\r
-\r
-                        ident = nc->ctx.identity;\r
-\r
-                        if (!k4_should_identity_get_k4(ident)) {\r
-\r
-                            _reportf(L"Kerberos 4 is not enabled for this identity.  Skipping");\r
-\r
-                            khui_cw_set_response(nc, credtype_id_krb4,\r
-                                                 KHUI_NC_RESPONSE_FAILED |\r
-                                                 KHUI_NC_RESPONSE_EXIT);\r
-                            break;\r
-                        }\r
-\r
-                    } else {\r
-\r
-                        _reportf(L"Kerberos 4 is not within renewal scope. Skipping");\r
-\r
-                        khui_cw_set_response(nc, credtype_id_krb4,\r
-                                             KHUI_NC_RESPONSE_FAILED |\r
-                                             KHUI_NC_RESPONSE_EXIT);\r
-                        break;\r
-                    }\r
-\r
-                    method = K4_METHOD_K524; /* only k524 is supported\r
-                                                for renewals */\r
-\r
-                    _begin_task(0);\r
-                    cb = sizeof(idname);\r
-                    kcdb_identity_get_name(ident, idname, &cb);\r
-                    _report_mr2(KHERR_NONE, MSG_K4_RENEW_CREDS,\r
-                                _cstr(ident), _int32(method));\r
-                    _resolve();\r
-                    _describe();\r
-                } else {\r
-                    assert(FALSE);\r
-                    break;\r
-                }\r
-\r
-                if ((method == K4_METHOD_AUTO ||\r
-                     method == K4_METHOD_K524) &&\r
-                    khui_cw_type_succeeded(nc, credtype_id_krb5)) {\r
-\r
-                    khm_handle tgt;\r
-                    FILETIME ft_prev;\r
-                    FILETIME ft_new;\r
-                    khm_size cb;\r
-\r
-                    _report_mr0(KHERR_INFO, MSG_K4_TRY_K524);\r
-\r
-                    tgt = khm_krb4_find_tgt(NULL, ident);\r
-                    if (tgt) {\r
-                        cb = sizeof(ft_prev);\r
-                        if (KHM_FAILED(kcdb_cred_get_attr(tgt,\r
-                                                          KCDB_ATTR_EXPIRE,\r
-                                                          NULL,\r
-                                                          &ft_prev,\r
-                                                          &cb)))\r
-                            ZeroMemory(&ft_prev, sizeof(ft_prev));\r
-                        kcdb_cred_release(tgt);\r
-                    }\r
-\r
-                    code = khm_convert524(ident);\r
-\r
-                    _reportf(L"khm_convert524 returns code %d", code);\r
-\r
-                    if (code == 0) {\r
-                        khui_cw_set_response(nc, credtype_id_krb4,\r
-                                             KHUI_NC_RESPONSE_SUCCESS |\r
-                                             KHUI_NC_RESPONSE_EXIT);\r
-\r
-                        if (nc->subtype == KMSG_CRED_NEW_CREDS) {\r
-                            assert(d != NULL);\r
-\r
-                            k4_write_identity_data(d);\r
-\r
-                        } else if (nc->subtype == KMSG_CRED_RENEW_CREDS &&\r
-                                   (nc->ctx.scope == KHUI_SCOPE_CREDTYPE ||\r
-                                    nc->ctx.scope == KHUI_SCOPE_CRED)) {\r
-\r
-                            khm_krb4_list_tickets();\r
-\r
-                            tgt = khm_krb4_find_tgt(NULL, ident);\r
-\r
-                            if (tgt) {\r
-                                cb = sizeof(ft_new);\r
-                                ZeroMemory(&ft_new, sizeof(ft_new));\r
-\r
-                                kcdb_cred_get_attr(tgt,\r
-                                                   KCDB_ATTR_EXPIRE,\r
-                                                   NULL,\r
-                                                   &ft_new,\r
-                                                   &cb);\r
-\r
-                                kcdb_cred_release(tgt);\r
-                            }\r
-\r
-                            if (!tgt ||\r
-                                CompareFileTime(&ft_new,\r
-                                                &ft_prev) <= 0) {\r
-                                /* The new TGT wasn't much of an\r
-                                   improvement over what we already\r
-                                   had.  We should go out and try to\r
-                                   renew the identity now. */\r
-\r
-                                khui_action_context ctx;\r
-\r
-                                _reportf(L"Renewal of Krb4 creds failed to get a longer TGT.  Triggering identity renewal");\r
-\r
-                                khui_context_create(&ctx,\r
-                                                    KHUI_SCOPE_IDENT,\r
-                                                    nc->ctx.identity,\r
-                                                    KCDB_CREDTYPE_INVALID,\r
-                                                    NULL);\r
-                                khui_action_trigger(KHUI_ACTION_RENEW_CRED,\r
-                                                    &ctx);\r
-\r
-                                khui_context_release(&ctx);\r
-                            }\r
-                        }\r
-\r
-                        _end_task();\r
-                        break;\r
-\r
-                    } else if (method == K4_METHOD_K524) {\r
-                        khui_cw_set_response(nc, credtype_id_krb4,\r
-                                             KHUI_NC_RESPONSE_FAILED |\r
-                                             KHUI_NC_RESPONSE_EXIT);\r
-\r
-                       if (nc->subtype == KMSG_CRED_RENEW_CREDS &&\r
-                           (nc->ctx.scope == KHUI_SCOPE_CREDTYPE ||\r
-                            nc->ctx.scope == KHUI_SCOPE_CRED)) {\r
-                           /* We were trying to get a new Krb4 TGT\r
-                              for this identity.  Sometimes this\r
-                              fails because of restrictions placed on\r
-                              K524d regarding the lifetime of the\r
-                              issued K4 TGT.  In this case, we\r
-                              trigger a renewal of the identity in\r
-                              the hope that the new K5 TGT will allow\r
-                              us to successfully get a new K4 TGT\r
-                              next time over using the new K5 TGT. */\r
-\r
-                           khui_action_context ctx;\r
-\r
-                            _reportf(L"Renewal of Krb4 creds failed using k524.  Triggerring identity renewal.");\r
-\r
-                           khui_context_create(&ctx,\r
-                                               KHUI_SCOPE_IDENT,\r
-                                               nc->ctx.identity,\r
-                                               KCDB_CREDTYPE_INVALID,\r
-                                               NULL);\r
-\r
-                           khui_action_trigger(KHUI_ACTION_RENEW_CRED,\r
-                                               &ctx);\r
-\r
-                           khui_context_release(&ctx);\r
-                       }\r
-\r
-                        _end_task();\r
-                        break;\r
-\r
-                    }\r
-                }\r
-\r
-                /* only supported for new credentials */\r
-                if (method == K4_METHOD_AUTO ||\r
-                    method == K4_METHOD_PASSWORD) {\r
-                    \r
-                    khm_size n_prompts = 0;\r
-                    khm_size idx;\r
-                    khm_size cb;\r
-                    wchar_t wpwd[KHUI_MAXCCH_PROMPT_VALUE];\r
-                    char pwd[KHUI_MAXCCH_PROMPT_VALUE];\r
-                    wchar_t widname[KCDB_IDENT_MAXCCH_NAME];\r
-                    char idname[KCDB_IDENT_MAXCCH_NAME];\r
-\r
-                    char * aname = NULL;\r
-                    char * inst = NULL;\r
-                    char * realm = NULL;\r
-\r
-                    assert(nc->subtype == KMSG_CRED_NEW_CREDS);\r
-\r
-                    _report_mr0(KHERR_INFO, MSG_K4_TRY_PASSWORD);\r
-\r
-                    code = TRUE; /* just has to be non-zero */\r
-\r
-                    khui_cw_get_prompt_count(nc, &n_prompts);\r
-\r
-                    if (n_prompts == 0)\r
-                        goto _skip_pwd;\r
-\r
-                    for (idx = 0; idx < n_prompts; idx++) {\r
-                        khui_new_creds_prompt * p;\r
-\r
-                        if (KHM_FAILED(khui_cw_get_prompt(nc, idx, &p)))\r
-                            continue;\r
-\r
-                        if (p->type == KHUI_NCPROMPT_TYPE_PASSWORD)\r
-                            break;\r
-                    }\r
-\r
-                    if (idx >= n_prompts) {\r
-                        _reportf(L"Password prompt not found");\r
-                        goto _skip_pwd;\r
-                    }\r
-\r
-                    khui_cw_sync_prompt_values(nc);\r
-\r
-                    cb = sizeof(wpwd);\r
-                    if (KHM_FAILED(khui_cw_get_prompt_value(nc, idx,\r
-                                                            wpwd,\r
-                                                            &cb))) {\r
-                        _reportf(L"Failed to obtain password value");\r
-                        goto _skip_pwd;\r
-                    }\r
-\r
-                    UnicodeStrToAnsi(pwd, sizeof(pwd), wpwd);\r
-\r
-                    cb = sizeof(widname);\r
-                    kcdb_identity_get_name(ident,\r
-                                           widname,\r
-                                           &cb);\r
-\r
-                    UnicodeStrToAnsi(idname, sizeof(idname), widname);\r
-\r
-                    {\r
-                        char * atsign;\r
-\r
-                        atsign = strchr(idname, '@');\r
-                        if (atsign == NULL) {\r
-                            _reportf(L"Identity name does not contain an '@'");\r
-                            goto _skip_pwd;\r
-                        }\r
-\r
-                        *atsign++ = 0;\r
-\r
-                        realm = atsign;\r
-                    }\r
-\r
-                    {\r
-                        char * slash;\r
-\r
-                        slash = strchr(idname, '/');\r
-                        if (slash != NULL) {\r
-                            *slash++ = 0;\r
-                            inst = slash;\r
-                        } else {\r
-                            inst = "";\r
-                        }\r
-                    }\r
-\r
-                    aname = idname;\r
-\r
-                    code = khm_krb4_kinit(aname, inst, realm,\r
-                                          (long) d->lifetime, pwd);\r
-\r
-                    _reportf(L"khm_krb4_kinit returns code %d", code);\r
-\r
-                _skip_pwd:\r
-\r
-                    if (code) {\r
-                        khui_cw_set_response(nc, credtype_id_krb4,\r
-                                             KHUI_NC_RESPONSE_EXIT |\r
-                                             KHUI_NC_RESPONSE_FAILED);\r
-\r
-                    } else {\r
-                        khui_cw_set_response(nc, credtype_id_krb4,\r
-                                             KHUI_NC_RESPONSE_EXIT |\r
-                                             KHUI_NC_RESPONSE_SUCCESS);\r
-\r
-                        if (nc->subtype == KMSG_CRED_NEW_CREDS) {\r
-\r
-                            assert(d != NULL);\r
-                            k4_write_identity_data(d);\r
-\r
-                        }\r
-                    }\r
-                }\r
-\r
-                _end_task();\r
-            }\r
-        }\r
-        break;\r
-\r
-    case KMSG_CRED_END:\r
-        {\r
-            khui_new_creds * nc;\r
-            khui_new_creds_by_type * nct = NULL;\r
-\r
-            nc = (khui_new_creds *) vparam;\r
-            if (KHM_FAILED(khui_cw_find_type(nc, credtype_id_krb4, &nct)))\r
-                break;\r
-\r
-            khui_cw_del_type(nc, credtype_id_krb4);\r
-\r
-            if (nct->name)\r
-                PFREE(nct->name);\r
-\r
-            if (nct->credtext)\r
-                PFREE(nct->credtext);\r
-\r
-            PFREE(nct);\r
-        }\r
-        break;\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<utils.h>
+#include<commctrl.h>
+#include<strsafe.h>
+#include<krb5.h>
+#include<assert.h>
+
+/* method identifiers should be contiguous */
+#define K4_METHOD_AUTO     0
+#define K4_METHOD_PASSWORD 1
+#define K4_METHOD_K524     2
+
+int method_to_id[] = {
+    IDC_NCK4_AUTO,
+    IDC_NCK4_PWD,
+    IDC_NCK4_K524
+};
+
+typedef struct tag_k4_dlg_data {
+    HWND hwnd;
+    khui_new_creds * nc;
+    khui_new_creds_by_type * nct;
+
+    khm_boolean      k4_enabled;
+    khm_int32        method;
+    time_t           lifetime;
+} k4_dlg_data;
+
+void k4_update_display(k4_dlg_data * d, BOOL update_methods) {
+    CheckDlgButton(d->hwnd, IDC_NCK4_OBTAIN,
+                   (d->k4_enabled)?BST_CHECKED: BST_UNCHECKED);
+
+    if (d->k4_enabled) {
+        EnableWindow(GetDlgItem(d->hwnd, IDC_NCK4_AUTO), TRUE);
+        EnableWindow(GetDlgItem(d->hwnd, IDC_NCK4_K524), TRUE);
+        EnableWindow(GetDlgItem(d->hwnd, IDC_NCK4_PWD ), TRUE);
+    } else {
+        EnableWindow(GetDlgItem(d->hwnd, IDC_NCK4_AUTO), FALSE);
+        EnableWindow(GetDlgItem(d->hwnd, IDC_NCK4_K524), FALSE);
+        EnableWindow(GetDlgItem(d->hwnd, IDC_NCK4_PWD ), FALSE);
+    }
+
+#ifdef DEBUG
+    assert(d->method >= 0 && d->method < ARRAYLENGTH(method_to_id));
+#endif
+
+    CheckRadioButton(d->hwnd, IDC_NCK4_AUTO, IDC_NCK4_PWD, method_to_id[d->method]);
+
+    khui_cw_enable_type(d->nc, credtype_id_krb4, d->k4_enabled);
+}
+
+void k4_update_data(k4_dlg_data * d) {
+    int i;
+    khm_boolean oldstate;
+
+    oldstate = d->k4_enabled;
+
+    if (IsDlgButtonChecked(d->hwnd, IDC_NCK4_OBTAIN) == BST_CHECKED)
+        d->k4_enabled = TRUE;
+    else
+        d->k4_enabled = FALSE;
+
+    if ((oldstate && !d->k4_enabled) ||
+        (!oldstate && d->k4_enabled)) {
+
+        khui_cw_enable_type(d->nc, credtype_id_krb4, d->k4_enabled);
+    }
+
+    d->method = K4_METHOD_AUTO;
+
+    for (i=K4_METHOD_AUTO; i<=K4_METHOD_K524; i++) {
+        if (IsDlgButtonChecked(d->hwnd, method_to_id[i]) == BST_CHECKED) {
+            d->method = i;
+            break;
+        }
+    }
+}
+
+khm_boolean k4_should_identity_get_k4(khm_handle ident) {
+    khm_int32 idflags = 0;
+    khm_int32 t = TRUE;
+    khm_handle csp_ident = NULL;
+    khm_handle csp_k4 = NULL;
+    khm_boolean get_k4 = TRUE;
+    khm_boolean id_spec = FALSE;
+
+    if (KHM_FAILED(kcdb_identity_get_flags(ident, &idflags)))
+        return FALSE;
+
+    if (!(idflags & KCDB_IDENT_FLAG_DEFAULT)) {
+        /* we only support k4 for one identity, and that is the
+           default identity.  If we are trying to get tickets for a
+           non-default identity, then we start off as disabled unless
+           there is no default identity. */
+
+        khm_handle defident = NULL;
+
+        if (KHM_SUCCEEDED(kcdb_identity_get_default(&defident))) {
+            kcdb_identity_release(defident);
+
+            return FALSE;
+        }
+    }
+
+    if (KHM_SUCCEEDED(kcdb_identity_get_config(ident, 0, &csp_ident))) {
+        if (KHM_SUCCEEDED(khc_open_space(csp_ident, CSNAME_KRB4CRED, 0,
+                                         &csp_k4))) {
+            khm_int32 t = 0;
+
+            if (KHM_SUCCEEDED(khc_read_int32(csp_k4, L"Krb4NewCreds", &t))) {
+                get_k4 = !!t;
+                id_spec = TRUE;
+            }
+
+            khc_close_space(csp_k4);
+        }
+        khc_close_space(csp_ident);
+    }
+
+    /* if there was a value specified for the identity, then that
+       takes precedence. */
+    if (id_spec || !get_k4)
+        return get_k4;
+
+    if (KHM_SUCCEEDED(khc_read_int32(csp_params, L"Krb4NewCreds", &t)) &&
+        !t)
+        return FALSE;
+
+    return TRUE;
+}
+
+void k4_read_identity_data(k4_dlg_data * d) {
+    khm_handle csp_ident = NULL;
+    khm_handle csp_k4 = NULL;
+
+    khm_int32 idflags = 0;
+    khm_int32 t;
+
+    if (KHM_SUCCEEDED(khc_read_int32(csp_params, L"Krb4NewCreds", &t)))
+        d->k4_enabled = !!t;
+    else
+        d->k4_enabled = TRUE;
+
+    if (KHM_SUCCEEDED(khc_read_int32(csp_params, L"Krb4Method", &t)))
+        d->method = t;
+    else
+        d->method = K4_METHOD_AUTO;
+
+    if (KHM_SUCCEEDED(khc_read_int32(csp_params, L"DefaultLifetime", &t)))
+        d->lifetime = t;
+    else
+        d->lifetime = 10 * 60 * 60; /* 10 hours */
+
+    if (d->nc->n_identities > 0 &&
+        d->nc->identities[0]) {
+
+        if (KHM_SUCCEEDED(kcdb_identity_get_config(d->nc->identities[0],
+                                                   0,
+                                                   &csp_ident))) {
+
+            khc_open_space(csp_ident, CSNAME_KRB4CRED, 0, &csp_k4);
+            
+            if (csp_k4) {
+                if (KHM_SUCCEEDED(khc_read_int32(csp_k4, L"Krb4NewCreds", &t)))
+                    d->k4_enabled = !!t;
+                if (KHM_SUCCEEDED(khc_read_int32(csp_k4, L"Krb4Method", &t)))
+                    d->method = t;
+                khc_close_space(csp_k4);
+            }
+
+            khc_close_space(csp_ident);
+        }
+
+        if (d->k4_enabled) {
+            d->k4_enabled = k4_should_identity_get_k4(d->nc->identities[0]);
+        }
+    } else {
+        d->k4_enabled = FALSE;
+    }
+
+    if (d->method < 0 || d->method > K4_METHOD_K524)
+        d->method = K4_METHOD_AUTO;
+}
+
+void k4_write_identity_data(k4_dlg_data * d) {
+    khm_handle csp_ident = NULL;
+    khm_handle csp_k4 = NULL;
+
+    if (d->nc->n_identities > 0 &&
+        d->nc->identities[0] &&
+        KHM_SUCCEEDED(kcdb_identity_get_config(d->nc->identities[0],
+                                               KHM_FLAG_CREATE,
+                                               &csp_ident))) {
+        khc_open_space(csp_ident, CSNAME_KRB4CRED,
+                       KHM_FLAG_CREATE | KCONF_FLAG_WRITEIFMOD,
+                       &csp_k4);
+
+        if (csp_k4) {
+            khc_write_int32(csp_k4, L"Krb4NewCreds", !!d->k4_enabled);
+            khc_write_int32(csp_k4, L"Krb4Method", d->method);
+
+            khc_close_space(csp_k4);
+        }
+
+        khc_close_space(csp_ident);
+    }
+}
+
+void k4_handle_wmnc_notify(k4_dlg_data * d,
+                           WPARAM wParam,
+                           LPARAM lParam) {
+    switch(HIWORD(wParam)) {
+    case WMNC_UPDATE_CREDTEXT:
+        {
+            if (d->nct->credtext) {
+                PFREE(d->nct->credtext);
+                d->nct->credtext = NULL;
+            }
+
+            if (d->nc->n_identities > 0 &&
+                d->nc->identities[0]) {
+
+                khm_int32 flags = 0;
+                wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
+                wchar_t * atsign;
+                wchar_t * realm;
+                khm_size cb;
+
+                kcdb_identity_get_flags(d->nc->identities[0], &flags);
+
+                if (!(flags & KCDB_IDENT_FLAG_VALID)) {
+                    break;
+                }
+
+                cb = sizeof(idname);
+                kcdb_identity_get_name(d->nc->identities[0], idname,
+                                       &cb);
+
+                atsign = wcsrchr(idname, L'@');
+
+                if (atsign == NULL || !atsign[1])
+                    break;
+
+                realm = ++atsign;
+
+                if (d->k4_enabled) {
+                    wchar_t wmethod[128];
+                    wchar_t wfmt[128];
+                    wchar_t wct[512];
+
+                    LoadString(hResModule, IDS_CT_TGTFOR,
+                               wfmt, ARRAYLENGTH(wfmt));
+
+                    if (d->method == K4_METHOD_AUTO)
+                        LoadString(hResModule, IDS_METHOD_AUTO, wmethod,
+                                   ARRAYLENGTH(wmethod));
+                    else if (d->method == K4_METHOD_PASSWORD)
+                        LoadString(hResModule, IDS_METHOD_PWD, wmethod,
+                                   ARRAYLENGTH(wmethod));
+                    else if (d->method == K4_METHOD_K524)
+                        LoadString(hResModule, IDS_METHOD_K524, wmethod,
+                                   ARRAYLENGTH(wmethod));
+                    else {
+                        assert(FALSE);
+                    }
+
+                    StringCbPrintf(wct, sizeof(wct), wfmt, realm, wmethod);
+
+                    StringCbLength(wct, sizeof(wct), &cb);
+                    cb += sizeof(wchar_t);
+
+                    d->nct->credtext = PMALLOC(cb);
+
+                    StringCbCopy(d->nct->credtext, cb, wct);
+                } else {
+                    wchar_t wct[256];
+
+                    LoadString(hResModule, IDS_CT_DISABLED,
+                               wct, ARRAYLENGTH(wct));
+
+                    StringCbLength(wct, sizeof(wct), &cb);
+                    cb += sizeof(wchar_t);
+
+                    d->nct->credtext = PMALLOC(cb);
+
+                    StringCbCopy(d->nct->credtext, cb, wct);
+                }
+            }
+            /* no identities were selected.  it is not the
+               responsibility of krb4 to complain about this. */
+        }
+        break;
+
+    case WMNC_IDENTITY_CHANGE:
+        k4_read_identity_data(d);
+        k4_update_display(d, TRUE);
+        break;
+
+    case WMNC_CREDTEXT_LINK:
+        {
+            wchar_t wid[KHUI_MAXCCH_HTLINK_FIELD];
+            wchar_t * wids;
+            khui_htwnd_link * l;
+
+            l = (khui_htwnd_link *) lParam;
+
+            StringCchCopyN(wid, ARRAYLENGTH(wid), l->id, l->id_len);
+            wids = wcschr(wid, L':');
+
+            if (!wids)
+                break;
+            else
+                wids++;
+
+            if (!wcscmp(wids, L"Enable")) {
+                d->k4_enabled = TRUE;
+
+                k4_update_display(d, TRUE);
+                khui_cw_enable_type(d->nc, credtype_id_krb4, TRUE);
+            }
+        }
+        break;
+    }
+}
+
+INT_PTR CALLBACK k4_nc_dlg_proc(HWND hwnd,
+                                UINT uMsg,
+                                WPARAM wParam,
+                                LPARAM lParam) {
+
+    k4_dlg_data * d;
+
+    switch(uMsg) {
+    case WM_INITDIALOG:
+        {
+            d = PMALLOC(sizeof(*d));
+            ZeroMemory(d, sizeof(*d));
+
+            d->nc = (khui_new_creds *) lParam;
+            khui_cw_find_type(d->nc, credtype_id_krb4, &d->nct);
+
+#pragma warning(push)
+#pragma warning(disable: 4244)
+            SetWindowLongPtr(hwnd, DWLP_USER, (LPARAM) d);
+#pragma warning(pop)
+
+            d->nct->aux = (LPARAM) d;
+            d->hwnd = hwnd;
+
+            d->k4_enabled = TRUE;
+            d->method = K4_METHOD_AUTO;
+
+            k4_update_display(d, TRUE);
+        }
+        break;
+
+    case WM_COMMAND:
+        {
+            if (HIWORD(wParam) == BN_CLICKED) {
+                d = (k4_dlg_data *) (LONG_PTR)
+                    GetWindowLongPtr(hwnd, DWLP_USER);
+
+                k4_update_data(d);
+
+                if (LOWORD(wParam) == IDC_NCK4_OBTAIN) {
+                    k4_update_display(d, TRUE);
+                }
+
+                return TRUE;
+            }
+        }
+        break;
+
+    case KHUI_WM_NC_NOTIFY:
+        {
+            d = (k4_dlg_data *) (LONG_PTR)
+                GetWindowLongPtr(hwnd, DWLP_USER);
+            k4_handle_wmnc_notify(d, wParam, lParam);
+        }
+        break;
+
+    case WM_DESTROY:
+        {
+            d = (k4_dlg_data *) (LONG_PTR)
+                GetWindowLongPtr(hwnd, DWLP_USER);
+
+            d->nct->aux = 0;
+
+            PFREE(d);
+        }
+        break;
+    }
+
+    return FALSE;
+}
+
+khm_int32
+krb4_msg_newcred(khm_int32 msg_type, khm_int32 msg_subtype,
+                 khm_ui_4 uparam, void * vparam) {
+
+    switch(msg_subtype) {
+    case KMSG_CRED_NEW_CREDS:
+        {
+            khui_new_creds * nc;
+            khui_new_creds_by_type * nct;
+            khm_size cbsize;
+            wchar_t wbuf[256];
+
+            nc = (khui_new_creds *) vparam;
+
+            nct = PMALLOC(sizeof(*nct));
+#ifdef DEBUG
+            assert(nct);
+#endif
+            ZeroMemory(nct, sizeof(*nct));
+
+            nct->type = credtype_id_krb4;
+            nct->ordinal = 3;
+            LoadString(hResModule, IDS_NC_K4_SHORT,
+                       wbuf, ARRAYLENGTH(wbuf));
+            StringCbLength(wbuf, sizeof(wbuf), &cbsize);
+            cbsize += sizeof(wchar_t);
+
+            nct->name = PMALLOC(cbsize);
+            StringCbCopy(nct->name, cbsize, wbuf);
+
+            nct->type_deps[nct->n_type_deps++] = credtype_id_krb5;
+
+            nct->h_module = hResModule;
+            nct->dlg_proc = k4_nc_dlg_proc;
+            nct->dlg_template = MAKEINTRESOURCE(IDD_NC_KRB4);
+
+            khui_cw_add_type(nc, nct);
+        }
+        break;
+
+    case KMSG_CRED_RENEW_CREDS:
+        {
+            khui_new_creds * nc;
+            khui_new_creds_by_type * nct;
+            khm_size cbsize;
+            wchar_t wbuf[256];
+
+            nc = (khui_new_creds *) vparam;
+
+            if (!nc->ctx.identity)
+                break;
+
+            nct = PMALLOC(sizeof(*nct));
+#ifdef DEBUG
+            assert(nct);
+#endif
+
+            ZeroMemory(nct, sizeof(*nct));
+
+            nct->type = credtype_id_krb4;
+            nct->ordinal = 3;
+            LoadString(hResModule, IDS_NC_K4_SHORT,
+                       wbuf, ARRAYLENGTH(wbuf));
+            StringCbLength(wbuf, sizeof(wbuf), &cbsize);
+            cbsize += sizeof(wchar_t);
+
+            nct->name = PMALLOC(cbsize);
+            StringCbCopy(nct->name, cbsize, wbuf);
+
+            nct->type_deps[nct->n_type_deps++] = credtype_id_krb5;
+
+            khui_cw_add_type(nc, nct);
+        }
+        break;
+
+    case KMSG_CRED_DIALOG_SETUP:
+        break;
+
+    case KMSG_CRED_PROCESS:
+        {
+            khui_new_creds * nc;
+            khui_new_creds_by_type * nct = NULL;
+            khm_handle ident = NULL;
+            k4_dlg_data * d = NULL;
+            long code = 0;
+            wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
+            khm_size cb;
+
+            nc = (khui_new_creds *) vparam;
+            if (KHM_FAILED(khui_cw_find_type(nc, credtype_id_krb4, &nct)))
+                break;
+
+            if (nc->subtype == KMSG_CRED_NEW_CREDS ||
+                nc->subtype == KMSG_CRED_RENEW_CREDS) {
+                khm_int32 method;
+
+                if (nc->subtype == KMSG_CRED_NEW_CREDS) {
+
+                    d = (k4_dlg_data *) nct->aux;
+                    if (!d ||
+                        nc->n_identities == 0 ||
+                        nc->identities[0] == NULL ||
+                        nc->result != KHUI_NC_RESULT_PROCESS) {
+                        khui_cw_set_response(nc, credtype_id_krb4,
+                                             KHUI_NC_RESPONSE_SUCCESS |
+                                             KHUI_NC_RESPONSE_EXIT);
+                        break;
+                    }
+
+                    if (!d->k4_enabled) {
+                        k4_write_identity_data(d);
+                        khui_cw_set_response(nc, credtype_id_krb4,
+                                             KHUI_NC_RESPONSE_SUCCESS |
+                                             KHUI_NC_RESPONSE_EXIT);
+                        break;
+                    }
+
+                    method = d->method;
+                    ident = nc->identities[0];
+
+                    cb = sizeof(idname);
+                    kcdb_identity_get_name(ident, idname, &cb);
+                    _begin_task(0);
+                    _report_mr2(KHERR_NONE, MSG_K4_NEW_CREDS,
+                                _cstr(ident), _int32(method));
+                    _resolve();
+                    _describe();
+
+                } else if (nc->subtype == KMSG_CRED_RENEW_CREDS) {
+
+                    if ((nc->ctx.scope == KHUI_SCOPE_IDENT &&
+                         nc->ctx.identity != NULL) ||
+
+                        (nc->ctx.scope == KHUI_SCOPE_CREDTYPE &&
+                         nc->ctx.cred_type == credtype_id_krb4 &&
+                         nc->ctx.identity != NULL) ||
+
+                        (nc->ctx.scope == KHUI_SCOPE_CRED &&
+                         nc->ctx.cred_type == credtype_id_krb4 &&
+                         nc->ctx.identity != NULL &&
+                         nc->ctx.cred != NULL)) {
+
+                        ident = nc->ctx.identity;
+
+                        if (!k4_should_identity_get_k4(ident)) {
+
+                            _reportf(L"Kerberos 4 is not enabled for this identity.  Skipping");
+
+                            khui_cw_set_response(nc, credtype_id_krb4,
+                                                 KHUI_NC_RESPONSE_FAILED |
+                                                 KHUI_NC_RESPONSE_EXIT);
+                            break;
+                        }
+
+                    } else {
+
+                        _reportf(L"Kerberos 4 is not within renewal scope. Skipping");
+
+                        khui_cw_set_response(nc, credtype_id_krb4,
+                                             KHUI_NC_RESPONSE_FAILED |
+                                             KHUI_NC_RESPONSE_EXIT);
+                        break;
+                    }
+
+                    method = K4_METHOD_K524; /* only k524 is supported
+                                                for renewals */
+
+                    _begin_task(0);
+                    cb = sizeof(idname);
+                    kcdb_identity_get_name(ident, idname, &cb);
+                    _report_mr2(KHERR_NONE, MSG_K4_RENEW_CREDS,
+                                _cstr(ident), _int32(method));
+                    _resolve();
+                    _describe();
+                } else {
+                    assert(FALSE);
+                    break;
+                }
+
+                if ((method == K4_METHOD_AUTO ||
+                     method == K4_METHOD_K524) &&
+                    khui_cw_type_succeeded(nc, credtype_id_krb5)) {
+
+                    khm_handle tgt;
+                    FILETIME ft_prev;
+                    FILETIME ft_new;
+                    khm_size cb;
+
+                    _report_mr0(KHERR_INFO, MSG_K4_TRY_K524);
+
+                    tgt = khm_krb4_find_tgt(NULL, ident);
+                    if (tgt) {
+                        cb = sizeof(ft_prev);
+                        if (KHM_FAILED(kcdb_cred_get_attr(tgt,
+                                                          KCDB_ATTR_EXPIRE,
+                                                          NULL,
+                                                          &ft_prev,
+                                                          &cb)))
+                            ZeroMemory(&ft_prev, sizeof(ft_prev));
+                        kcdb_cred_release(tgt);
+                    }
+
+                    code = khm_convert524(ident);
+
+                    _reportf(L"khm_convert524 returns code %d", code);
+
+                    if (code == 0) {
+                        khui_cw_set_response(nc, credtype_id_krb4,
+                                             KHUI_NC_RESPONSE_SUCCESS |
+                                             KHUI_NC_RESPONSE_EXIT);
+
+                        if (nc->subtype == KMSG_CRED_NEW_CREDS) {
+                            assert(d != NULL);
+
+                            k4_write_identity_data(d);
+
+                        } else if (nc->subtype == KMSG_CRED_RENEW_CREDS &&
+                                   (nc->ctx.scope == KHUI_SCOPE_CREDTYPE ||
+                                    nc->ctx.scope == KHUI_SCOPE_CRED)) {
+
+                            khm_krb4_list_tickets();
+
+                            tgt = khm_krb4_find_tgt(NULL, ident);
+
+                            if (tgt) {
+                                cb = sizeof(ft_new);
+                                ZeroMemory(&ft_new, sizeof(ft_new));
+
+                                kcdb_cred_get_attr(tgt,
+                                                   KCDB_ATTR_EXPIRE,
+                                                   NULL,
+                                                   &ft_new,
+                                                   &cb);
+
+                                kcdb_cred_release(tgt);
+                            }
+
+                            if (!tgt ||
+                                CompareFileTime(&ft_new,
+                                                &ft_prev) <= 0) {
+                                /* The new TGT wasn't much of an
+                                   improvement over what we already
+                                   had.  We should go out and try to
+                                   renew the identity now. */
+
+                                khui_action_context ctx;
+
+                                _reportf(L"Renewal of Krb4 creds failed to get a longer TGT.  Triggering identity renewal");
+
+                                khui_context_create(&ctx,
+                                                    KHUI_SCOPE_IDENT,
+                                                    nc->ctx.identity,
+                                                    KCDB_CREDTYPE_INVALID,
+                                                    NULL);
+                                khui_action_trigger(KHUI_ACTION_RENEW_CRED,
+                                                    &ctx);
+
+                                khui_context_release(&ctx);
+                            }
+                        }
+
+                        _end_task();
+                        break;
+
+                    } else if (method == K4_METHOD_K524) {
+                        khui_cw_set_response(nc, credtype_id_krb4,
+                                             KHUI_NC_RESPONSE_FAILED |
+                                             KHUI_NC_RESPONSE_EXIT);
+
+                       if (nc->subtype == KMSG_CRED_RENEW_CREDS &&
+                           (nc->ctx.scope == KHUI_SCOPE_CREDTYPE ||
+                            nc->ctx.scope == KHUI_SCOPE_CRED)) {
+                           /* We were trying to get a new Krb4 TGT
+                              for this identity.  Sometimes this
+                              fails because of restrictions placed on
+                              K524d regarding the lifetime of the
+                              issued K4 TGT.  In this case, we
+                              trigger a renewal of the identity in
+                              the hope that the new K5 TGT will allow
+                              us to successfully get a new K4 TGT
+                              next time over using the new K5 TGT. */
+
+                           khui_action_context ctx;
+
+                            _reportf(L"Renewal of Krb4 creds failed using k524.  Triggerring identity renewal.");
+
+                           khui_context_create(&ctx,
+                                               KHUI_SCOPE_IDENT,
+                                               nc->ctx.identity,
+                                               KCDB_CREDTYPE_INVALID,
+                                               NULL);
+
+                           khui_action_trigger(KHUI_ACTION_RENEW_CRED,
+                                               &ctx);
+
+                           khui_context_release(&ctx);
+                       }
+
+                        _end_task();
+                        break;
+
+                    }
+                }
+
+                /* only supported for new credentials */
+                if (method == K4_METHOD_AUTO ||
+                    method == K4_METHOD_PASSWORD) {
+                    
+                    khm_size n_prompts = 0;
+                    khm_size idx;
+                    khm_size cb;
+                    wchar_t wpwd[KHUI_MAXCCH_PROMPT_VALUE];
+                    char pwd[KHUI_MAXCCH_PROMPT_VALUE];
+                    wchar_t widname[KCDB_IDENT_MAXCCH_NAME];
+                    char idname[KCDB_IDENT_MAXCCH_NAME];
+
+                    char * aname = NULL;
+                    char * inst = NULL;
+                    char * realm = NULL;
+
+                    assert(nc->subtype == KMSG_CRED_NEW_CREDS);
+
+                    _report_mr0(KHERR_INFO, MSG_K4_TRY_PASSWORD);
+
+                    code = TRUE; /* just has to be non-zero */
+
+                    khui_cw_get_prompt_count(nc, &n_prompts);
+
+                    if (n_prompts == 0)
+                        goto _skip_pwd;
+
+                    for (idx = 0; idx < n_prompts; idx++) {
+                        khui_new_creds_prompt * p;
+
+                        if (KHM_FAILED(khui_cw_get_prompt(nc, idx, &p)))
+                            continue;
+
+                        if (p->type == KHUI_NCPROMPT_TYPE_PASSWORD)
+                            break;
+                    }
+
+                    if (idx >= n_prompts) {
+                        _reportf(L"Password prompt not found");
+                        goto _skip_pwd;
+                    }
+
+                    khui_cw_sync_prompt_values(nc);
+
+                    cb = sizeof(wpwd);
+                    if (KHM_FAILED(khui_cw_get_prompt_value(nc, idx,
+                                                            wpwd,
+                                                            &cb))) {
+                        _reportf(L"Failed to obtain password value");
+                        goto _skip_pwd;
+                    }
+
+                    UnicodeStrToAnsi(pwd, sizeof(pwd), wpwd);
+
+                    cb = sizeof(widname);
+                    kcdb_identity_get_name(ident,
+                                           widname,
+                                           &cb);
+
+                    UnicodeStrToAnsi(idname, sizeof(idname), widname);
+
+                    {
+                        char * atsign;
+
+                        atsign = strchr(idname, '@');
+                        if (atsign == NULL) {
+                            _reportf(L"Identity name does not contain an '@'");
+                            goto _skip_pwd;
+                        }
+
+                        *atsign++ = 0;
+
+                        realm = atsign;
+                    }
+
+                    {
+                        char * slash;
+
+                        slash = strchr(idname, '/');
+                        if (slash != NULL) {
+                            *slash++ = 0;
+                            inst = slash;
+                        } else {
+                            inst = "";
+                        }
+                    }
+
+                    aname = idname;
+
+                    code = khm_krb4_kinit(aname, inst, realm,
+                                          (long) d->lifetime, pwd);
+
+                    _reportf(L"khm_krb4_kinit returns code %d", code);
+
+                _skip_pwd:
+
+                    if (code) {
+                        khui_cw_set_response(nc, credtype_id_krb4,
+                                             KHUI_NC_RESPONSE_EXIT |
+                                             KHUI_NC_RESPONSE_FAILED);
+
+                    } else {
+                        khui_cw_set_response(nc, credtype_id_krb4,
+                                             KHUI_NC_RESPONSE_EXIT |
+                                             KHUI_NC_RESPONSE_SUCCESS);
+
+                        if (nc->subtype == KMSG_CRED_NEW_CREDS) {
+
+                            assert(d != NULL);
+                            k4_write_identity_data(d);
+
+                        }
+                    }
+                }
+
+                _end_task();
+            }
+        }
+        break;
+
+    case KMSG_CRED_END:
+        {
+            khui_new_creds * nc;
+            khui_new_creds_by_type * nct = NULL;
+
+            nc = (khui_new_creds *) vparam;
+            if (KHM_FAILED(khui_cw_find_type(nc, credtype_id_krb4, &nct)))
+                break;
+
+            khui_cw_del_type(nc, credtype_id_krb4);
+
+            if (nct->name)
+                PFREE(nct->name);
+
+            if (nct->credtext)
+                PFREE(nct->credtext);
+
+            PFREE(nct);
+        }
+        break;
+    }
+
+    return KHM_ERROR_SUCCESS;
+}
index b4edd4192fca567d6d07342d2d06d8dffd06a6e4..9a50249f7a7ef830fd6eae4d01a534235342e422 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<krbcred.h>\r
-#include<kherror.h>\r
-#include<khmsgtypes.h>\r
-#include<khuidefs.h>\r
-#include<utils.h>\r
-#include<commctrl.h>\r
-#include<strsafe.h>\r
-#include<krb5.h>\r
-\r
-khm_int32 credtype_id_krb4 = KCDB_CREDTYPE_INVALID;\r
-khm_int32 credtype_id_krb5 = KCDB_CREDTYPE_INVALID;\r
-\r
-khm_boolean krb4_initialized = FALSE;\r
-khm_handle krb4_credset = NULL;\r
-\r
-/* Kerberos IV stuff */\r
-khm_int32 KHMAPI \r
-krb4_msg_system(khm_int32 msg_type, khm_int32 msg_subtype, \r
-                khm_ui_4 uparam, void * vparam)\r
-{\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    switch(msg_subtype) {\r
-    case KMSG_SYSTEM_INIT:\r
-        {\r
-#ifdef _WIN64\r
-            return KHM_ERROR_NOT_IMPLEMENTED;\r
-#else\r
-            kcdb_credtype ct;\r
-            wchar_t buf[KCDB_MAXCCH_SHORT_DESC];\r
-            size_t cbsize;\r
-            khui_config_node_reg reg;\r
-            wchar_t wshort_desc[KHUI_MAXCCH_SHORT_DESC];\r
-            wchar_t wlong_desc[KHUI_MAXCCH_LONG_DESC];\r
-\r
-            /* perform critical registrations and initialization\r
-               stuff */\r
-            ZeroMemory(&ct, sizeof(ct));\r
-            ct.id = KCDB_CREDTYPE_AUTO;\r
-            ct.name = KRB4_CREDTYPE_NAME;\r
-\r
-            if(LoadString(hResModule, IDS_KRB4_SHORT_DESC, \r
-                          buf, ARRAYLENGTH(buf)))\r
-                {\r
-                    StringCbLength(buf, KCDB_MAXCB_SHORT_DESC, &cbsize);\r
-                    cbsize += sizeof(wchar_t);\r
-                    ct.short_desc = PMALLOC(cbsize);\r
-                    StringCbCopy(ct.short_desc, cbsize, buf);\r
-                }\r
-\r
-            /* even though ideally we should be setting limits\r
-               based KCDB_MAXCB_LONG_DESC, our long description\r
-               actually fits nicely in KCDB_MAXCB_SHORT_DESC */\r
-            if(LoadString(hResModule, IDS_KRB4_LONG_DESC, \r
-                          buf, ARRAYLENGTH(buf)))\r
-                {\r
-                    StringCbLength(buf, KCDB_MAXCB_SHORT_DESC, &cbsize);\r
-                    cbsize += sizeof(wchar_t);\r
-                    ct.long_desc = PMALLOC(cbsize);\r
-                    StringCbCopy(ct.long_desc, cbsize, buf);\r
-                }\r
-\r
-            ct.icon = NULL; /* TODO: set a proper icon */\r
-            kmq_create_subscription(krb4_cb, &ct.sub);\r
-\r
-            rv = kcdb_credtype_register(&ct, &credtype_id_krb4);\r
-\r
-            if(KHM_SUCCEEDED(rv))\r
-                rv = kcdb_credset_create(&krb4_credset);\r
-\r
-            if (KHM_SUCCEEDED(rv))\r
-                rv = kcdb_credtype_get_id(KRB5_CREDTYPE_NAME, \r
-                                          &credtype_id_krb5);\r
-\r
-            if(ct.short_desc)\r
-                PFREE(ct.short_desc);\r
-\r
-            if(ct.long_desc)\r
-                PFREE(ct.long_desc);\r
-\r
-            if (KHM_SUCCEEDED(rv)) {\r
-                khui_config_node idents;\r
-\r
-                ZeroMemory(&reg, sizeof(reg));\r
-\r
-                reg.name = KRB4_CONFIG_NODE_NAME;\r
-                reg.short_desc = wshort_desc;\r
-                reg.long_desc = wlong_desc;\r
-                reg.h_module = hResModule;\r
-                reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_KRB4);\r
-                reg.dlg_proc = krb4_confg_proc;\r
-                reg.flags = 0;\r
-\r
-                LoadString(hResModule, IDS_CFG_KRB4_LONG,\r
-                           wlong_desc, ARRAYLENGTH(wlong_desc));\r
-                LoadString(hResModule, IDS_CFG_KRB4_SHORT,\r
-                           wshort_desc, ARRAYLENGTH(wshort_desc));\r
-\r
-                khui_cfg_register(NULL, &reg);\r
-\r
-                khui_cfg_open(NULL, L"KhmIdentities", &idents);\r
-\r
-                ZeroMemory(&reg, sizeof(reg));\r
-\r
-                reg.name = KRB4_IDS_CONFIG_NODE_NAME;\r
-                reg.short_desc = wshort_desc;\r
-                reg.long_desc = wlong_desc;\r
-                reg.h_module = hResModule;\r
-                reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_IDS_KRB4);\r
-                reg.dlg_proc = krb4_ids_config_proc;\r
-                reg.flags = KHUI_CNFLAG_SUBPANEL;\r
-\r
-                LoadString(hResModule, IDS_CFG_KRB4_SHORT,\r
-                           wlong_desc, ARRAYLENGTH(wlong_desc));\r
-                LoadString(hResModule, IDS_CFG_KRB4_SHORT,\r
-                           wshort_desc, ARRAYLENGTH(wshort_desc));\r
-\r
-                khui_cfg_register(idents, &reg);\r
-\r
-                ZeroMemory(&reg, sizeof(reg));\r
-\r
-                reg.name = KRB4_ID_CONFIG_NODE_NAME;\r
-                reg.short_desc = wshort_desc;\r
-                reg.long_desc = wlong_desc;\r
-                reg.h_module = hResModule;\r
-                reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_ID_KRB4);\r
-                reg.dlg_proc = krb4_id_config_proc;\r
-                reg.flags = KHUI_CNFLAG_SUBPANEL | KHUI_CNFLAG_PLURAL;\r
-\r
-                LoadString(hResModule, IDS_CFG_KRB4_SHORT,\r
-                           wlong_desc, ARRAYLENGTH(wlong_desc));\r
-                LoadString(hResModule, IDS_CFG_KRB4_SHORT,\r
-                           wshort_desc, ARRAYLENGTH(wshort_desc));\r
-\r
-                khui_cfg_register(idents, &reg);\r
-\r
-                khui_cfg_release(idents);\r
-\r
-            }\r
-\r
-            /* Lookup common data types */\r
-            if(KHM_FAILED(kcdb_type_get_id(TYPENAME_ENCTYPE, \r
-                                           &type_id_enctype))) {\r
-                rv = KHM_ERROR_UNKNOWN;\r
-            }\r
-\r
-            if(KHM_FAILED(kcdb_type_get_id(TYPENAME_ADDR_LIST, \r
-                                           &type_id_addr_list))) {\r
-                rv = KHM_ERROR_UNKNOWN;\r
-            }\r
-\r
-            if(KHM_FAILED(kcdb_type_get_id(TYPENAME_KRB5_FLAGS, \r
-                                           &type_id_krb5_flags))) {\r
-                rv = KHM_ERROR_UNKNOWN;\r
-            }\r
-\r
-            /* Lookup common attributes */\r
-            if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_KEY_ENCTYPE, \r
-                                             &attr_id_key_enctype))) {\r
-                rv = KHM_ERROR_UNKNOWN;\r
-            }\r
-\r
-            if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_TKT_ENCTYPE, \r
-                                             &attr_id_tkt_enctype))) {\r
-                rv = KHM_ERROR_UNKNOWN;\r
-            }\r
-\r
-            if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_ADDR_LIST, \r
-                                             &attr_id_addr_list))) {\r
-                rv = KHM_ERROR_UNKNOWN;\r
-            }\r
-\r
-            if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_KRB5_FLAGS, \r
-                                             &attr_id_krb5_flags))) {\r
-                rv = KHM_ERROR_UNKNOWN;\r
-            }\r
-\r
-            krb4_initialized = TRUE;\r
-\r
-            khm_krb4_set_def_tkt_string();\r
-\r
-            khm_krb4_list_tickets();\r
-#endif\r
-        }\r
-        break;\r
-\r
-    case KMSG_SYSTEM_EXIT:\r
-#ifdef _WIN64\r
-        /* See above.  On 64-bit platforms, we don't support Krb4 at\r
-           all. */\r
-        return 0;\r
-#else\r
-        if(credtype_id_krb4 >= 0)\r
-            {\r
-                /* basically just unregister the credential type */\r
-                kcdb_credtype_unregister(credtype_id_krb4);\r
-\r
-                kcdb_credset_delete(krb4_credset);\r
-            }\r
-        break;\r
-#endif\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-khm_int32 KHMAPI \r
-krb4_msg_cred(khm_int32 msg_type, khm_int32 msg_subtype, \r
-              khm_ui_4 uparam, void * vparam)\r
-{\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    switch(msg_subtype) {\r
-    case KMSG_CRED_REFRESH:\r
-        {\r
-            khm_krb4_list_tickets();\r
-        }\r
-        break;\r
-\r
-    case KMSG_CRED_DESTROY_CREDS:\r
-        {\r
-            khui_action_context * ctx;\r
-            khm_handle credset;\r
-            khm_size nc_root = 0;\r
-            khm_size nc_sel = 0;\r
-\r
-            ctx = (khui_action_context *) vparam;\r
-\r
-            /* if all krb4 tickets are selected, then we destroy all\r
-               of them.  Otherwise, we do nothing. */\r
-\r
-            kcdb_credset_create(&credset);\r
-\r
-            kcdb_credset_extract(credset, ctx->credset,\r
-                                 NULL, credtype_id_krb4);\r
-            kcdb_credset_get_size(credset, &nc_sel);\r
-\r
-            kcdb_credset_flush(credset);\r
-\r
-            kcdb_credset_extract(credset, NULL,\r
-                                 NULL, credtype_id_krb4);\r
-            kcdb_credset_get_size(credset, &nc_root);\r
-\r
-            kcdb_credset_delete(credset);\r
-\r
-            if (nc_root == nc_sel) {\r
-                khm_krb4_kdestroy();\r
-            }\r
-        }\r
-        break;\r
-\r
-    default:\r
-        if (IS_CRED_ACQ_MSG(msg_subtype))\r
-            return krb4_msg_newcred(msg_type, msg_subtype, uparam, vparam);\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-khm_int32 KHMAPI \r
-krb4_cb(khm_int32 msg_type, khm_int32 msg_subtype, \r
-        khm_ui_4 uparam, void * vparam)\r
-{\r
-    switch(msg_type) {\r
-        case KMSG_SYSTEM:\r
-            return krb4_msg_system(msg_type, msg_subtype, uparam, vparam);\r
-        case KMSG_CRED:\r
-            return krb4_msg_cred(msg_type, msg_subtype, uparam, vparam);\r
-    }\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<utils.h>
+#include<commctrl.h>
+#include<strsafe.h>
+#include<krb5.h>
+
+khm_int32 credtype_id_krb4 = KCDB_CREDTYPE_INVALID;
+khm_int32 credtype_id_krb5 = 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:
+        {
+#ifdef _WIN64
+            return KHM_ERROR_NOT_IMPLEMENTED;
+#else
+            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 = PMALLOC(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 = PMALLOC(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 (KHM_SUCCEEDED(rv))
+                rv = kcdb_credtype_get_id(KRB5_CREDTYPE_NAME, 
+                                          &credtype_id_krb5);
+
+            if(ct.short_desc)
+                PFREE(ct.short_desc);
+
+            if(ct.long_desc)
+                PFREE(ct.long_desc);
+
+            if (KHM_SUCCEEDED(rv)) {
+                khui_config_node idents;
+
+                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);
+
+                khui_cfg_open(NULL, L"KhmIdentities", &idents);
+
+                ZeroMemory(&reg, sizeof(reg));
+
+                reg.name = KRB4_IDS_CONFIG_NODE_NAME;
+                reg.short_desc = wshort_desc;
+                reg.long_desc = wlong_desc;
+                reg.h_module = hResModule;
+                reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_IDS_KRB4);
+                reg.dlg_proc = krb4_ids_config_proc;
+                reg.flags = KHUI_CNFLAG_SUBPANEL;
+
+                LoadString(hResModule, IDS_CFG_KRB4_SHORT,
+                           wlong_desc, ARRAYLENGTH(wlong_desc));
+                LoadString(hResModule, IDS_CFG_KRB4_SHORT,
+                           wshort_desc, ARRAYLENGTH(wshort_desc));
+
+                khui_cfg_register(idents, &reg);
+
+                ZeroMemory(&reg, sizeof(reg));
+
+                reg.name = KRB4_ID_CONFIG_NODE_NAME;
+                reg.short_desc = wshort_desc;
+                reg.long_desc = wlong_desc;
+                reg.h_module = hResModule;
+                reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_ID_KRB4);
+                reg.dlg_proc = krb4_id_config_proc;
+                reg.flags = KHUI_CNFLAG_SUBPANEL | KHUI_CNFLAG_PLURAL;
+
+                LoadString(hResModule, IDS_CFG_KRB4_SHORT,
+                           wlong_desc, ARRAYLENGTH(wlong_desc));
+                LoadString(hResModule, IDS_CFG_KRB4_SHORT,
+                           wshort_desc, ARRAYLENGTH(wshort_desc));
+
+                khui_cfg_register(idents, &reg);
+
+                khui_cfg_release(idents);
+
+            }
+
+            /* Lookup common data types */
+            if(KHM_FAILED(kcdb_type_get_id(TYPENAME_ENCTYPE, 
+                                           &type_id_enctype))) {
+                rv = KHM_ERROR_UNKNOWN;
+            }
+
+            if(KHM_FAILED(kcdb_type_get_id(TYPENAME_ADDR_LIST, 
+                                           &type_id_addr_list))) {
+                rv = KHM_ERROR_UNKNOWN;
+            }
+
+            if(KHM_FAILED(kcdb_type_get_id(TYPENAME_KRB5_FLAGS, 
+                                           &type_id_krb5_flags))) {
+                rv = KHM_ERROR_UNKNOWN;
+            }
+
+            /* Lookup common attributes */
+            if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_KEY_ENCTYPE, 
+                                             &attr_id_key_enctype))) {
+                rv = KHM_ERROR_UNKNOWN;
+            }
+
+            if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_TKT_ENCTYPE, 
+                                             &attr_id_tkt_enctype))) {
+                rv = KHM_ERROR_UNKNOWN;
+            }
+
+            if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_ADDR_LIST, 
+                                             &attr_id_addr_list))) {
+                rv = KHM_ERROR_UNKNOWN;
+            }
+
+            if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_KRB5_FLAGS, 
+                                             &attr_id_krb5_flags))) {
+                rv = KHM_ERROR_UNKNOWN;
+            }
+
+            krb4_initialized = TRUE;
+
+            khm_krb4_set_def_tkt_string();
+
+            khm_krb4_list_tickets();
+#endif
+        }
+        break;
+
+    case KMSG_SYSTEM_EXIT:
+#ifdef _WIN64
+        /* See above.  On 64-bit platforms, we don't support Krb4 at
+           all. */
+        return 0;
+#else
+        if(credtype_id_krb4 >= 0)
+            {
+                /* basically just unregister the credential type */
+                kcdb_credtype_unregister(credtype_id_krb4);
+
+                kcdb_credset_delete(krb4_credset);
+            }
+        break;
+#endif
+    }
+
+    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;
+
+    case KMSG_CRED_DESTROY_CREDS:
+        {
+            khui_action_context * ctx;
+            khm_handle credset;
+            khm_size nc_root = 0;
+            khm_size nc_sel = 0;
+
+            ctx = (khui_action_context *) vparam;
+
+            /* if all krb4 tickets are selected, then we destroy all
+               of them.  Otherwise, we do nothing. */
+
+            kcdb_credset_create(&credset);
+
+            kcdb_credset_extract(credset, ctx->credset,
+                                 NULL, credtype_id_krb4);
+            kcdb_credset_get_size(credset, &nc_sel);
+
+            kcdb_credset_flush(credset);
+
+            kcdb_credset_extract(credset, NULL,
+                                 NULL, credtype_id_krb4);
+            kcdb_credset_get_size(credset, &nc_root);
+
+            kcdb_credset_delete(credset);
+
+            if (nc_root == nc_sel) {
+                khm_krb4_kdestroy();
+            }
+        }
+        break;
+
+    default:
+        if (IS_CRED_ACQ_MSG(msg_subtype))
+            return krb4_msg_newcred(msg_type, msg_subtype, uparam, vparam);
+    }
+
+    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;
+}
index 7c3b31a133d84618d443fd54df132ff7fc0c94a6..53e22c8929b6edbd3b4405570a76b882dbed23c4 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KRBAFSCRED_H\r
-#define __KHIMAIRA_KRBAFSCRED_H\r
-\r
-#include<windows.h>\r
-\r
-#define KHERR_FACILITY L"Krb4Cred"\r
-#define KHERR_FACILITY_ID 65\r
-#define KHERR_HMODULE hResModule\r
-\r
-#include<netidmgr.h>\r
-\r
-#include<krb4funcs.h>\r
-#include<krb5common.h>\r
-#include<errorfuncs.h>\r
-#include<dynimport.h>\r
-\r
-#include<langres.h>\r
-#include<krb4_msgs.h>\r
-\r
-#define TYPENAME_ENCTYPE        L"EncType"\r
-#define TYPENAME_ADDR_LIST      L"AddrList"\r
-#define TYPENAME_KRB5_FLAGS     L"Krb5Flags"\r
-\r
-#define ATTRNAME_KEY_ENCTYPE    L"KeyEncType"\r
-#define ATTRNAME_TKT_ENCTYPE    L"TktEncType"\r
-#define ATTRNAME_ADDR_LIST      L"AddrList"\r
-#define ATTRNAME_KRB5_FLAGS     L"Krb5Flags"\r
-#define ATTRNAME_RENEW_TILL     L"RenewTill"\r
-#define ATTRNAME_RENEW_FOR      L"RenewFor"\r
-\r
-void init_krb();\r
-void exit_krb();\r
-KHMEXP khm_int32 KHMAPI init_module(kmm_module h_module);\r
-KHMEXP khm_int32 KHMAPI exit_module(kmm_module h_module);\r
-\r
-/* globals */\r
-extern kmm_module h_khModule;\r
-extern HMODULE hResModule;\r
-extern HINSTANCE hInstance;\r
-\r
-extern khm_int32 type_id_enctype;\r
-extern khm_int32 type_id_addr_list;\r
-extern khm_int32 type_id_krb5_flags;\r
-\r
-extern khm_int32 attr_id_key_enctype;\r
-extern khm_int32 attr_id_tkt_enctype;\r
-extern khm_int32 attr_id_addr_list;\r
-extern khm_int32 attr_id_krb5_flags;\r
-extern khm_int32 attr_id_renew_till;\r
-extern khm_int32 attr_id_renew_for;\r
-\r
-/* Configuration spaces */\r
-#define CSNAME_KRB4CRED     L"Krb4Cred"\r
-#define CSNAME_PARAMS       L"Parameters"\r
-\r
-/* plugin constants */\r
-#define KRB4_PLUGIN_NAME    L"Krb4Cred"\r
-\r
-#define KRB4_PLUGIN_DEPS    L"Krb5Cred\0"\r
-\r
-#define KRB4_CREDTYPE_NAME  L"Krb4Cred"\r
-\r
-#define KRB5_CREDTYPE_NAME  L"Krb5Cred"\r
-\r
-#define KRB4_CONFIG_NODE_NAME L"Krb4Config"\r
-\r
-#define KRB4_ID_CONFIG_NODE_NAME L"Krb4IdentConfig"\r
-#define KRB4_IDS_CONFIG_NODE_NAME L"Krb4IdentsConfig"\r
-\r
-extern khm_handle csp_plugins;\r
-extern khm_handle csp_krbcred;\r
-extern khm_handle csp_params;\r
-\r
-extern kconf_schema schema_krbconfig[];\r
-\r
-/* other globals */\r
-extern khm_int32 credtype_id_krb4;\r
-extern khm_int32 credtype_id_krb5;\r
-\r
-extern khm_boolean krb4_initialized;\r
-\r
-extern khm_handle krb4_credset;\r
-\r
-/* plugin callbacks */\r
-khm_int32 KHMAPI \r
-krb4_cb(khm_int32 msg_type, khm_int32 msg_subtype, \r
-        khm_ui_4 uparam, void * vparam);\r
-\r
-INT_PTR CALLBACK\r
-krb4_confg_proc(HWND hwnd,\r
-                UINT uMsg,\r
-                WPARAM wParam,\r
-                LPARAM lParam);\r
-\r
-INT_PTR CALLBACK\r
-krb4_ids_config_proc(HWND hwnd,\r
-                     UINT uMsg,\r
-                     WPARAM wParam,\r
-                     LPARAM lParam);\r
-\r
-INT_PTR CALLBACK\r
-krb4_id_config_proc(HWND hwnd,\r
-                    UINT uMsg,\r
-                    WPARAM wParam,\r
-                    LPARAM lParam);\r
-\r
-khm_int32\r
-krb4_msg_newcred(khm_int32 msg_type, khm_int32 msg_subtype,\r
-                 khm_ui_4 uparam, void * vparam);\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+
+#define KHERR_FACILITY L"Krb4Cred"
+#define KHERR_FACILITY_ID 65
+#define KHERR_HMODULE hResModule
+
+#include<netidmgr.h>
+
+#include<krb4funcs.h>
+#include<krb5common.h>
+#include<errorfuncs.h>
+#include<dynimport.h>
+
+#include<langres.h>
+#include<krb4_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_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 KRB5_CREDTYPE_NAME  L"Krb5Cred"
+
+#define KRB4_CONFIG_NODE_NAME L"Krb4Config"
+
+#define KRB4_ID_CONFIG_NODE_NAME L"Krb4IdentConfig"
+#define KRB4_IDS_CONFIG_NODE_NAME L"Krb4IdentsConfig"
+
+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_int32 credtype_id_krb5;
+
+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);
+
+INT_PTR CALLBACK
+krb4_ids_config_proc(HWND hwnd,
+                     UINT uMsg,
+                     WPARAM wParam,
+                     LPARAM lParam);
+
+INT_PTR CALLBACK
+krb4_id_config_proc(HWND hwnd,
+                    UINT uMsg,
+                    WPARAM wParam,
+                    LPARAM lParam);
+
+khm_int32
+krb4_msg_newcred(khm_int32 msg_type, khm_int32 msg_subtype,
+                 khm_ui_4 uparam, void * vparam);
+#endif
index c78ae41674aeff87080015048b5954eb57c4eb78..5c0e46f9aa133c9c1726f9004213df400023c1bc 100644 (file)
@@ -1,49 +1,49 @@
-//{{NO_DEPENDENCIES}}\r
-// Microsoft Visual C++ generated include file.\r
-// Used by C:\work\pismere\athena\auth\krb5\src\windows\identity\plugins\krb4\lang\en_us\langres.rc\r
-//\r
-#define IDD_NC_KRB4                     103\r
-#define IDS_PLUGIN_DESC                 103\r
-#define IDD_CFG_KRB4                    104\r
-#define IDS_NC_K4_SHORT                 104\r
-#define IDS_ERR_REALM                   105\r
-#define IDD_CFG_IDS_KRB4                105\r
-#define IDS_ERR_PRINCIPAL               106\r
-#define IDD_CFG_ID_KRB4                 106\r
-#define IDS_ERR_INVINST                 107\r
-#define IDI_PLUGIN                      107\r
-#define IDS_ERR_PWINTKT                 108\r
-#define IDS_CT_DISABLED                 109\r
-#define IDS_CT_TGTFOR                   110\r
-#define IDS_METHOD_AUTO                 111\r
-#define IDS_METHOD_PWD                  112\r
-#define IDS_METHOD_K524                 113\r
-#define IDS_CFG_IDS_KRB4_SHORT          114\r
-#define IDS_KRB4_SHORT_DESC             128\r
-#define IDS_KRB4_LONG_DESC              129\r
-#define IDS_CFG_KRB4_LONG               135\r
-#define IDS_CFG_KRB4_SHORT              136\r
-#define IDC_CFG_LBL_CACHE               1025\r
-#define IDC_CFG_LBL_CFGFILE             1026\r
-#define IDC_CFG_LBL_RLMPATH             1027\r
-#define IDC_CFG_CACHE                   1028\r
-#define IDC_CFG_CFGPATH                 1029\r
-#define IDC_CFG_RLMPATH                 1030\r
-#define IDC_CFG_CFGBROW                 1031\r
-#define IDC_CFG_RLMBROW                 1032\r
-#define IDC_NCK4_OBTAIN                 1033\r
-#define IDC_NCK4_AUTO                   1034\r
-#define IDC_NCK4_K524                   1035\r
-#define IDC_NCK4_PWD                    1036\r
-#define IDC_CFG_GETTIX                  1037\r
-\r
-// Next default values for new objects\r
-// \r
-#ifdef APSTUDIO_INVOKED\r
-#ifndef APSTUDIO_READONLY_SYMBOLS\r
-#define _APS_NEXT_RESOURCE_VALUE        108\r
-#define _APS_NEXT_COMMAND_VALUE         40001\r
-#define _APS_NEXT_CONTROL_VALUE         1043\r
-#define _APS_NEXT_SYMED_VALUE           101\r
-#endif\r
-#endif\r
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by C:\work\pismere\athena\auth\krb5\src\windows\identity\plugins\krb4\lang\en_us\langres.rc
+//
+#define IDD_NC_KRB4                     103
+#define IDS_PLUGIN_DESC                 103
+#define IDD_CFG_KRB4                    104
+#define IDS_NC_K4_SHORT                 104
+#define IDS_ERR_REALM                   105
+#define IDD_CFG_IDS_KRB4                105
+#define IDS_ERR_PRINCIPAL               106
+#define IDD_CFG_ID_KRB4                 106
+#define IDS_ERR_INVINST                 107
+#define IDI_PLUGIN                      107
+#define IDS_ERR_PWINTKT                 108
+#define IDS_CT_DISABLED                 109
+#define IDS_CT_TGTFOR                   110
+#define IDS_METHOD_AUTO                 111
+#define IDS_METHOD_PWD                  112
+#define IDS_METHOD_K524                 113
+#define IDS_CFG_IDS_KRB4_SHORT          114
+#define IDS_KRB4_SHORT_DESC             128
+#define IDS_KRB4_LONG_DESC              129
+#define IDS_CFG_KRB4_LONG               135
+#define IDS_CFG_KRB4_SHORT              136
+#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
+#define IDC_NCK4_OBTAIN                 1033
+#define IDC_NCK4_AUTO                   1034
+#define IDC_NCK4_K524                   1035
+#define IDC_NCK4_PWD                    1036
+#define IDC_CFG_GETTIX                  1037
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        108
+#define _APS_NEXT_COMMAND_VALUE         40001
+#define _APS_NEXT_CONTROL_VALUE         1043
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
index 92eabf4daa287bd3718492ba871fefb8cc9bef38..5c292e4780ea4c3da4bdeccb1a0ff7323d112b00 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/* Data representation and related functions */\r
-\r
-#include<winsock2.h>\r
-#include<krbcred.h>\r
-#include<krb5.h>\r
-#include<kherror.h>\r
-#include<strsafe.h>\r
-#include<assert.h>\r
-\r
-khm_int32 KHMAPI \r
-enctype_toString(const void * data, khm_size cbdata,\r
-                wchar_t *destbuf, khm_size *pcbdestbuf,\r
-                khm_int32 flags)\r
-{\r
-    int resid = 0;\r
-    int etype;\r
-    wchar_t buf[256];\r
-    size_t cblength;\r
-\r
-    if(cbdata != sizeof(khm_int32))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    etype = *((khm_int32 *) data);\r
-\r
-    switch(etype) {\r
-    case ENCTYPE_NULL:\r
-        resid = IDS_ETYPE_NULL;\r
-        break;\r
-\r
-    case ENCTYPE_DES_CBC_CRC:\r
-        resid = IDS_ETYPE_DES_CBC_CRC;\r
-        break;\r
-\r
-    case ENCTYPE_DES_CBC_MD4:\r
-        resid = IDS_ETYPE_DES_CBC_MD4;\r
-        break;\r
-\r
-    case ENCTYPE_DES_CBC_MD5:\r
-        resid = IDS_ETYPE_DES_CBC_MD5;\r
-        break;\r
-\r
-    case ENCTYPE_DES_CBC_RAW:\r
-        resid = IDS_ETYPE_DES_CBC_RAW;\r
-        break;\r
-\r
-    case ENCTYPE_DES3_CBC_SHA:\r
-        resid = IDS_ETYPE_DES3_CBC_SHA;\r
-        break;\r
-\r
-    case ENCTYPE_DES3_CBC_RAW:\r
-        resid = IDS_ETYPE_DES3_CBC_RAW;\r
-        break;\r
-\r
-    case ENCTYPE_DES_HMAC_SHA1:\r
-        resid = IDS_ETYPE_DES_HMAC_SHA1;\r
-        break;\r
-\r
-    case ENCTYPE_DES3_CBC_SHA1:\r
-        resid = IDS_ETYPE_DES3_CBC_SHA1;\r
-        break;\r
-\r
-    case ENCTYPE_AES128_CTS_HMAC_SHA1_96:\r
-        resid = IDS_ETYPE_AES128_CTS_HMAC_SHA1_96;\r
-        break;\r
-\r
-    case ENCTYPE_AES256_CTS_HMAC_SHA1_96:\r
-        resid = IDS_ETYPE_AES256_CTS_HMAC_SHA1_96;\r
-        break;\r
-\r
-    case ENCTYPE_ARCFOUR_HMAC:\r
-        resid = IDS_ETYPE_ARCFOUR_HMAC;\r
-        break;\r
-\r
-    case ENCTYPE_ARCFOUR_HMAC_EXP:\r
-        resid = IDS_ETYPE_ARCFOUR_HMAC_EXP;\r
-        break;\r
-\r
-    case ENCTYPE_UNKNOWN:\r
-        resid = IDS_ETYPE_UNKNOWN;\r
-        break;\r
-\r
-#if 0\r
-    case ENCTYPE_LOCAL_DES3_HMAC_SHA1:\r
-        resid = IDS_ETYPE_LOCAL_DES3_HMAC_SHA1;\r
-        break;\r
-\r
-    case ENCTYPE_LOCAL_RC4_MD4:\r
-        resid = IDS_ETYPE_LOCAL_RC4_MD4;\r
-        break;\r
-#endif\r
-    }\r
-\r
-    if(resid != 0) {\r
-        LoadString(hResModule, (UINT) resid, buf, ARRAYLENGTH(buf));\r
-    } else {\r
-        StringCbPrintf(buf, sizeof(buf), L"#%d", etype);\r
-    }\r
-\r
-    StringCbLength(buf, ARRAYLENGTH(buf), &cblength);\r
-    cblength += sizeof(wchar_t);\r
-\r
-    if(!destbuf || *pcbdestbuf < cblength) {\r
-        *pcbdestbuf = cblength;\r
-        return KHM_ERROR_TOO_LONG;\r
-    } else {\r
-        StringCbCopy(destbuf, *pcbdestbuf, buf);\r
-        *pcbdestbuf = cblength;\r
-        return KHM_ERROR_SUCCESS;\r
-    }\r
-}\r
-\r
-khm_int32 KHMAPI\r
-addr_list_comp(const void *d1, khm_size cb_d1,\r
-              const void *d2, khm_size cb_d2)\r
-{\r
-    if (cb_d1 < cb_d2)\r
-       return -1;\r
-    if (cb_d1 > cb_d2)\r
-       return 1;\r
-    return memcmp(d1, d2, cb_d1);\r
-}\r
-\r
-khm_int32 KHMAPI\r
-addr_list_toString(const void *d, khm_size cb_d,\r
-                  wchar_t *buf, khm_size *pcb_buf,\r
-                  khm_int32 flags)\r
-{\r
-    wchar_t tbuf[2048];\r
-    wchar_t * strpos;\r
-    khm_size cbleft;\r
-    size_t t;\r
-    k5_serial_address * addrs;\r
-\r
-    if (cb_d == 0 || d == NULL) {\r
-        tbuf[0] = L'\0';\r
-    } else {\r
-        addrs = (k5_serial_address *) d;\r
-\r
-        strpos = tbuf;\r
-        cbleft = sizeof(tbuf);\r
-        tbuf[0] = L'\0';\r
-\r
-        while (TRUE) {\r
-            if (cb_d < sizeof(*addrs) ||\r
-                addrs->magic != K5_SERIAL_ADDRESS_MAGIC ||\r
-                cb_d < sizeof(*addrs) + addrs->length - sizeof(khm_int32))\r
-                break;\r
-\r
-            if (strpos != tbuf) {\r
-                if (FAILED(StringCbCatEx(strpos, cbleft, L"  ",\r
-                                         &strpos, &cbleft,\r
-                                         0)))\r
-                    break;\r
-            }\r
-\r
-#ifdef DEBUG\r
-            assert(*strpos == L'\0');\r
-#endif\r
-\r
-            one_addr(addrs, strpos, cbleft);\r
-\r
-           t = 0;\r
-           if (FAILED(StringCchLength(strpos,\r
-                                      cbleft / sizeof(wchar_t),\r
-                                      &t)))\r
-               break;\r
-\r
-           strpos += t;\r
-           cbleft -= t * sizeof(wchar_t);\r
-\r
-            t = sizeof(*addrs) + addrs->length - sizeof(khm_int32);\r
-            addrs = (k5_serial_address *) BYTEOFFSET(addrs, t);\r
-            cb_d -= t;\r
-        }\r
-    }\r
-\r
-    StringCbLength(tbuf, sizeof(tbuf), &t);\r
-\r
-    if (!buf || *pcb_buf < t) {\r
-        *pcb_buf = t;\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-\r
-    StringCbCopy(buf, *pcb_buf, tbuf);\r
-    *pcb_buf = t;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-khm_int32 KHMAPI\r
-krb5flags_toString(const void *d, \r
-                  khm_size cb_d, \r
-                  wchar_t *buf, \r
-                  khm_size *pcb_buf, \r
-                  khm_int32 f)\r
-{\r
-    wchar_t sbuf[32];\r
-    int i = 0;\r
-    khm_size cb;\r
-    khm_int32 flags;\r
-\r
-    flags = *((khm_int32 *) d);\r
-\r
-    if (flags & TKT_FLG_FORWARDABLE)\r
-        sbuf[i++] = L'F';\r
-\r
-    if (flags & TKT_FLG_FORWARDED)\r
-        sbuf[i++] = L'f';\r
-\r
-    if (flags & TKT_FLG_PROXIABLE)\r
-        sbuf[i++] = L'P';\r
-\r
-    if (flags & TKT_FLG_PROXY)\r
-        sbuf[i++] = L'p';\r
-\r
-    if (flags & TKT_FLG_MAY_POSTDATE)\r
-        sbuf[i++] = L'D';\r
-\r
-    if (flags & TKT_FLG_POSTDATED)\r
-        sbuf[i++] = L'd';\r
-\r
-    if (flags & TKT_FLG_INVALID)\r
-        sbuf[i++] = L'i';\r
-\r
-    if (flags & TKT_FLG_RENEWABLE)\r
-        sbuf[i++] = L'R';\r
-\r
-    if (flags & TKT_FLG_INITIAL)\r
-        sbuf[i++] = L'I';\r
-\r
-    if (flags & TKT_FLG_HW_AUTH)\r
-        sbuf[i++] = L'H';\r
-\r
-    if (flags & TKT_FLG_PRE_AUTH)\r
-        sbuf[i++] = L'A';\r
-\r
-    sbuf[i++] = L'\0';\r
-\r
-    cb = i * sizeof(wchar_t);\r
-\r
-    if (!buf || *pcb_buf < cb) {\r
-        *pcb_buf = cb;\r
-        return KHM_ERROR_TOO_LONG;\r
-    } else {\r
-        StringCbCopy(buf, *pcb_buf, sbuf);\r
-        *pcb_buf = cb;\r
-        return KHM_ERROR_SUCCESS;\r
-    }\r
-}\r
-\r
-khm_int32 KHMAPI\r
-kvno_toString(const void * data, khm_size cbdata,\r
-              wchar_t *destbuf, khm_size *pcbdestbuf,\r
-              khm_int32 flags)\r
-{\r
-    int resid = 0;\r
-    int kvno;\r
-    wchar_t buf[256];\r
-    size_t cblength;\r
-\r
-    if (cbdata != sizeof(khm_int32))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    kvno = *((khm_int32 *) data);\r
-\r
-    StringCbPrintf(buf, sizeof(buf), L"#%d", kvno);\r
-\r
-    StringCbLength(buf, ARRAYLENGTH(buf), &cblength);\r
-    cblength += sizeof(wchar_t);\r
-\r
-    if (!destbuf || *pcbdestbuf < cblength) {\r
-        *pcbdestbuf = cblength;\r
-        return KHM_ERROR_TOO_LONG;\r
-    } else {\r
-        StringCbCopy(destbuf, *pcbdestbuf, buf);\r
-        *pcbdestbuf = cblength;\r
-        return KHM_ERROR_SUCCESS;\r
-    }\r
-}\r
-\r
-khm_int32\r
-serialize_krb5_addresses(krb5_address ** a, void * buf, size_t * pcbbuf)\r
-{\r
-    k5_serial_address * addr;\r
-    khm_size cb_req;\r
-    khm_size t;\r
-    khm_boolean overflow = FALSE;\r
-\r
-    addr = (k5_serial_address *) buf;\r
-    cb_req = 0;\r
-\r
-    for(; *a; a++) {\r
-        t = sizeof(k5_serial_address) + (*a)->length - sizeof(khm_int32);\r
-        cb_req += t;\r
-        if (cb_req < *pcbbuf) {\r
-            addr->magic = K5_SERIAL_ADDRESS_MAGIC;\r
-            addr->addrtype = (*a)->addrtype;\r
-            addr->length = (*a)->length;\r
-            memcpy(&addr->data, (*a)->contents, (*a)->length);\r
-\r
-            addr = (k5_serial_address *) BYTEOFFSET(addr, t);\r
-        } else {\r
-            overflow = TRUE;\r
-        }\r
-    }\r
-\r
-    *pcbbuf = cb_req;\r
-\r
-    return (overflow)?KHM_ERROR_TOO_LONG: KHM_ERROR_SUCCESS;\r
-}\r
-\r
-void\r
-one_addr(k5_serial_address *a, wchar_t * buf, khm_size cbbuf)\r
-{\r
-    wchar_t retstr[256];\r
-    struct hostent *h = NULL;\r
-    int no_resolve = 1;\r
-\r
-    retstr[0] = L'\0';\r
-\r
-    if ((a->addrtype == ADDRTYPE_INET && a->length == 4)\r
-#ifdef AF_INET6\r
-        || (a->addrtype == ADDRTYPE_INET6 && a->length == 16)\r
-#endif\r
-        ) \r
-    {\r
-        int af = AF_INET;\r
-#ifdef AF_INET6\r
-        if (a->addrtype == ADDRTYPE_INET6)\r
-            af = AF_INET6;\r
-#endif\r
-        if (!no_resolve) {\r
-#ifdef HAVE_GETIPNODEBYADDR\r
-            int err;\r
-            h = getipnodebyaddr(&a->data, a->length, af, &err);\r
-            if (h) {\r
-                StringCbPrintf(retstr, sizeof(retstr), L"%S", h->h_name);\r
-                freehostent(h);\r
-            }\r
-            else\r
-                h = gethostbyaddr(&a->data, a->length, af);\r
-            if (h) {\r
-                StringCbPrintf(retstr, sizeof(retstr), L"%S", h->h_name);\r
-            }\r
-#endif\r
-            if (h)\r
-                goto _copy_string;\r
-        }\r
-        if (no_resolve || !h) {\r
-#ifdef HAVE_INET_NTOP\r
-            char buf[46];\r
-            const char *name = inet_ntop(a->addrtype, &a->data, buf, sizeof(buf));\r
-            if (name) {\r
-                StringCbPrintf(retstr, sizeof(retstr), L"%S", name);\r
-                goto _copy_string;\r
-            }\r
-#else\r
-            if (a->addrtype == ADDRTYPE_INET) {\r
-                khm_ui_4 addr = a->data;\r
-                StringCbPrintf(retstr, sizeof(retstr),\r
-                               L"%d.%d.%d.%d",\r
-                               (int) (addr & 0xff),\r
-                               (int) ((addr >> 8) & 0xff),\r
-                               (int) ((addr >> 16)& 0xff),\r
-                               (int) ((addr >> 24)& 0xff));\r
-                goto _copy_string;\r
-            }\r
-#endif\r
-        }\r
-    }\r
-\r
-    {\r
-        wchar_t tmpfmt[128];\r
-        LoadString(hResModule, IDS_UNK_ADDR_FMT, tmpfmt, sizeof(tmpfmt)/sizeof(wchar_t));\r
-        StringCbPrintf(retstr, sizeof(retstr), tmpfmt, a->addrtype);\r
-    }\r
-\r
- _copy_string:\r
-    StringCbCopy(buf, cbbuf, retstr);\r
-}\r
-\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<winsock2.h>
+#include<krbcred.h>
+#include<krb5.h>
+#include<kherror.h>
+#include<strsafe.h>
+#include<assert.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_PARAM;
+
+    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_comp(const void *d1, khm_size cb_d1,
+              const void *d2, khm_size cb_d2)
+{
+    if (cb_d1 < cb_d2)
+       return -1;
+    if (cb_d1 > cb_d2)
+       return 1;
+    return memcmp(d1, d2, cb_d1);
+}
+
+khm_int32 KHMAPI
+addr_list_toString(const void *d, khm_size cb_d,
+                  wchar_t *buf, khm_size *pcb_buf,
+                  khm_int32 flags)
+{
+    wchar_t tbuf[2048];
+    wchar_t * strpos;
+    khm_size cbleft;
+    size_t t;
+    k5_serial_address * addrs;
+
+    if (cb_d == 0 || d == NULL) {
+        tbuf[0] = L'\0';
+    } else {
+        addrs = (k5_serial_address *) d;
+
+        strpos = tbuf;
+        cbleft = sizeof(tbuf);
+        tbuf[0] = L'\0';
+
+        while (TRUE) {
+            if (cb_d < sizeof(*addrs) ||
+                addrs->magic != K5_SERIAL_ADDRESS_MAGIC ||
+                cb_d < sizeof(*addrs) + addrs->length - sizeof(khm_int32))
+                break;
+
+            if (strpos != tbuf) {
+                if (FAILED(StringCbCatEx(strpos, cbleft, L"  ",
+                                         &strpos, &cbleft,
+                                         0)))
+                    break;
+            }
+
+#ifdef DEBUG
+            assert(*strpos == L'\0');
+#endif
+
+            one_addr(addrs, strpos, cbleft);
+
+           t = 0;
+           if (FAILED(StringCchLength(strpos,
+                                      cbleft / sizeof(wchar_t),
+                                      &t)))
+               break;
+
+           strpos += t;
+           cbleft -= t * sizeof(wchar_t);
+
+            t = sizeof(*addrs) + addrs->length - sizeof(khm_int32);
+            addrs = (k5_serial_address *) BYTEOFFSET(addrs, t);
+            cb_d -= t;
+        }
+    }
+
+    StringCbLength(tbuf, sizeof(tbuf), &t);
+
+    if (!buf || *pcb_buf < t) {
+        *pcb_buf = t;
+        return KHM_ERROR_TOO_LONG;
+    }
+
+    StringCbCopy(buf, *pcb_buf, tbuf);
+    *pcb_buf = t;
+
+    return KHM_ERROR_SUCCESS;
+}
+
+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 KHMAPI
+kvno_toString(const void * data, khm_size cbdata,
+              wchar_t *destbuf, khm_size *pcbdestbuf,
+              khm_int32 flags)
+{
+    int resid = 0;
+    int kvno;
+    wchar_t buf[256];
+    size_t cblength;
+
+    if (cbdata != sizeof(khm_int32))
+        return KHM_ERROR_INVALID_PARAM;
+
+    kvno = *((khm_int32 *) data);
+
+    StringCbPrintf(buf, sizeof(buf), L"#%d", kvno);
+
+    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
+serialize_krb5_addresses(krb5_address ** a, void * buf, size_t * pcbbuf)
+{
+    k5_serial_address * addr;
+    khm_size cb_req;
+    khm_size t;
+    khm_boolean overflow = FALSE;
+
+    addr = (k5_serial_address *) buf;
+    cb_req = 0;
+
+    for(; *a; a++) {
+        t = sizeof(k5_serial_address) + (*a)->length - sizeof(khm_int32);
+        cb_req += t;
+        if (cb_req < *pcbbuf) {
+            addr->magic = K5_SERIAL_ADDRESS_MAGIC;
+            addr->addrtype = (*a)->addrtype;
+            addr->length = (*a)->length;
+            memcpy(&addr->data, (*a)->contents, (*a)->length);
+
+            addr = (k5_serial_address *) BYTEOFFSET(addr, t);
+        } else {
+            overflow = TRUE;
+        }
+    }
+
+    *pcbbuf = cb_req;
+
+    return (overflow)?KHM_ERROR_TOO_LONG: KHM_ERROR_SUCCESS;
+}
+
+void
+one_addr(k5_serial_address *a, wchar_t * buf, khm_size cbbuf)
+{
+    wchar_t retstr[256];
+    struct hostent *h = NULL;
+    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->data, a->length, af, &err);
+            if (h) {
+                StringCbPrintf(retstr, sizeof(retstr), L"%S", h->h_name);
+                freehostent(h);
+            }
+            else
+                h = gethostbyaddr(&a->data, a->length, af);
+            if (h) {
+                StringCbPrintf(retstr, sizeof(retstr), L"%S", h->h_name);
+            }
+#endif
+            if (h)
+                goto _copy_string;
+        }
+        if (no_resolve || !h) {
+#ifdef HAVE_INET_NTOP
+            char buf[46];
+            const char *name = inet_ntop(a->addrtype, &a->data, buf, sizeof(buf));
+            if (name) {
+                StringCbPrintf(retstr, sizeof(retstr), L"%S", name);
+                goto _copy_string;
+            }
+#else
+            if (a->addrtype == ADDRTYPE_INET) {
+                khm_ui_4 addr = a->data;
+                StringCbPrintf(retstr, sizeof(retstr),
+                               L"%d.%d.%d.%d",
+                               (int) (addr & 0xff),
+                               (int) ((addr >> 8) & 0xff),
+                               (int) ((addr >> 16)& 0xff),
+                               (int) ((addr >> 24)& 0xff));
+                goto _copy_string;
+            }
+#endif
+        }
+    }
+
+    {
+        wchar_t tmpfmt[128];
+        LoadString(hResModule, IDS_UNK_ADDR_FMT, tmpfmt, sizeof(tmpfmt)/sizeof(wchar_t));
+        StringCbPrintf(retstr, sizeof(retstr), tmpfmt, a->addrtype);
+    }
+
+ _copy_string:
+    StringCbCopy(buf, cbbuf, retstr);
+}
+
index 90f1923fa00c6fd649b02839ba29957a74a506aa..d81e7b91e96ad5c8dfb04b34c3de02b5ee0cde2a 100644 (file)
@@ -1,76 +1,76 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KRB_DATAREP_H\r
-#define __KHIMAIRA_KRB_DATAREP_H\r
-\r
-typedef struct tag_k5_serial_address {\r
-    khm_int32     magic;        /* should be K5_SERIAL_ADDRESS_MAGIC */\r
-    khm_int32     addrtype;     /* Address type. We only know what to\r
-                                   do with ADDRTYPE_INET and\r
-                                   ADDRTYPE_INET6 */\r
-    khm_size      length;       /* number of bytes of data in [data].\r
-                                   This should always be greater than\r
-                                   sizeof(khm_int32) */\r
-    khm_int32     data;         /* actually, &data is the beginning of\r
-                                   the data buffer that is [length]\r
-                                   bytes long. */\r
-} k5_serial_address;\r
-\r
-#define K5_SERIAL_ADDRESS_MAGIC 0x44ce832d\r
-\r
-khm_int32 KHMAPI\r
-enctype_toString(const void * data, khm_size cbdata,\r
-                 wchar_t *destbuf, khm_size *pcbdestbuf,\r
-                 khm_int32 flags);\r
-\r
-khm_int32 KHMAPI\r
-addr_list_comp(const void *d1, khm_size cb_d1,\r
-              const void *d2, khm_size cb_d2);\r
-\r
-khm_int32 KHMAPI\r
-addr_list_toString(const void *, khm_size, wchar_t *,\r
-                   khm_size *, khm_int32);\r
-\r
-khm_int32 KHMAPI\r
-krb5flags_toString(const void *, khm_size, wchar_t *,\r
-                   khm_size *, khm_int32);\r
-\r
-khm_int32 KHMAPI\r
-kvno_toString(const void * data, khm_size cbdata,\r
-              wchar_t *destbuf, khm_size *pcbdestbuf,\r
-              khm_int32 flags);\r
-\r
-khm_int32 KHMAPI\r
-renew_for_cb(khm_handle cred, khm_int32 id, void * buffer,\r
-             khm_size * pcbsize);\r
-\r
-khm_int32\r
-serialize_krb5_addresses(krb5_address ** a, void * buf, size_t * pcbbuf);\r
-\r
-void\r
-one_addr(k5_serial_address *a, wchar_t * buf, khm_size cbbuf);\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
+
+typedef struct tag_k5_serial_address {
+    khm_int32     magic;        /* should be K5_SERIAL_ADDRESS_MAGIC */
+    khm_int32     addrtype;     /* Address type. We only know what to
+                                   do with ADDRTYPE_INET and
+                                   ADDRTYPE_INET6 */
+    khm_size      length;       /* number of bytes of data in [data].
+                                   This should always be greater than
+                                   sizeof(khm_int32) */
+    khm_int32     data;         /* actually, &data is the beginning of
+                                   the data buffer that is [length]
+                                   bytes long. */
+} k5_serial_address;
+
+#define K5_SERIAL_ADDRESS_MAGIC 0x44ce832d
+
+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_comp(const void *d1, khm_size cb_d1,
+              const void *d2, khm_size cb_d2);
+
+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
+kvno_toString(const void * data, khm_size cbdata,
+              wchar_t *destbuf, khm_size *pcbdestbuf,
+              khm_int32 flags);
+
+khm_int32 KHMAPI
+renew_for_cb(khm_handle cred, khm_int32 id, void * buffer,
+             khm_size * pcbsize);
+
+khm_int32
+serialize_krb5_addresses(krb5_address ** a, void * buf, size_t * pcbbuf);
+
+void
+one_addr(k5_serial_address *a, wchar_t * buf, khm_size cbbuf);
+#endif
index f631b3c0ceee93e9cfe8271c3fee492d37ece276..4c2d78c630f89c8f2203cbc8761b950bdc057f17 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<krbcred.h>\r
-#include<kherror.h>\r
-\r
-extern void (__cdecl *pinitialize_krb_error_func)();\r
-extern void (__cdecl *pinitialize_kadm_error_table)();\r
-\r
-\r
-khm_int32 init_error_funcs()\r
-{\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-khm_int32 exit_error_funcs()\r
-{\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-#ifdef DEPRECATED_REMOVABLE\r
-HWND GetRootParent (HWND Child)\r
-{\r
-    HWND Last;\r
-    while (Child)\r
-    {\r
-        Last = Child;\r
-        Child = GetParent (Child);\r
-    }\r
-    return Last;\r
-}\r
-#endif\r
-\r
-void khm_err_describe(long code, wchar_t * buf, khm_size cbbuf, \r
-                      DWORD * suggestion,\r
-                      kherr_suggestion * suggest_code)\r
-{\r
-    const char * com_err_msg;\r
-    int offset;\r
-    long table_num;\r
-    DWORD msg_id = 0;\r
-    DWORD sugg_id = 0;\r
-    kherr_suggestion sugg_code = KHERR_SUGGEST_NONE;\r
-\r
-    if (suggestion == NULL || buf == NULL || cbbuf == 0 || suggest_code == 0)\r
-        return;\r
-\r
-    *buf = L'\0';\r
-\r
-    offset = (int) (code & 255);\r
-    table_num = code - offset;\r
-    com_err_msg = perror_message(code);\r
-\r
-    *suggestion = 0;\r
-    *suggest_code = KHERR_SUGGEST_NONE;\r
-\r
-    if (WSABASEERR <= code && code < (WSABASEERR + 1064)) {\r
-        /* winsock error */\r
-        table_num = WSABASEERR;\r
-        offset = code - WSABASEERR;\r
-    }\r
-\r
-    switch(table_num)\r
-    {\r
-    case krb_err_base:\r
-    case kadm_err_base:\r
-    case WSABASEERR:\r
-       break;\r
-    default:\r
-\r
-        if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) {\r
-            *suggestion = MSG_ERR_S_INTEGRITY;\r
-        }\r
-        *suggest_code = KHERR_SUGGEST_RETRY;\r
-        AnsiStrToUnicode(buf, cbbuf, com_err_msg);\r
-       return;\r
-    }\r
-\r
-    if (table_num == krb_err_base) {\r
-        switch(offset) {\r
-        case KDC_NAME_EXP:           /* 001 Principal expired */\r
-        case KDC_SERVICE_EXP:        /* 002 Service expired */\r
-        case KDC_AUTH_EXP:           /* 003 Auth expired */\r
-        case KDC_PKT_VER:            /* 004 Protocol version unknown */\r
-        case KDC_P_MKEY_VER:         /* 005 Wrong master key version */\r
-        case KDC_S_MKEY_VER:         /* 006 Wrong master key version */\r
-        case KDC_BYTE_ORDER:         /* 007 Byte order unknown */\r
-        case KDC_PR_N_UNIQUE:        /* 009 Principal not unique */\r
-        case KDC_NULL_KEY:           /* 010 Principal has null key */\r
-        case KDC_GEN_ERR:            /* 011 Generic error from KDC */\r
-        case INTK_W_NOTALL   :       /* 061 Not ALL tickets returned */\r
-        case INTK_PROT       :       /* 063 Protocol Error */\r
-        case INTK_ERR        :       /* 070 Other error */\r
-            msg_id = MSG_ERR_UNKNOWN;\r
-            sugg_code = KHERR_SUGGEST_RETRY;\r
-            break;\r
-\r
-        case KDC_PR_UNKNOWN:         /* 008 Principal unknown */\r
-            msg_id = MSG_ERR_PR_UNKNOWN;\r
-            sugg_code = KHERR_SUGGEST_RETRY;\r
-            break;\r
-        case GC_TKFIL                : /* 021 Can't read ticket file */\r
-        case GC_NOTKT                : /* 022 Can't find ticket or TGT */\r
-            msg_id = MSG_ERR_TKFIL;\r
-            sugg_id = MSG_ERR_S_TKFIL;\r
-            sugg_code = KHERR_SUGGEST_RETRY;\r
-            break;\r
-        case MK_AP_TGTEXP    :  /* 026 TGT Expired */\r
-            /* no extra error msg */\r
-            break;\r
-\r
-        case RD_AP_TIME              : /* 037 delta_t too big */\r
-            msg_id = MSG_ERR_CLOCKSKEW;\r
-            sugg_id = MSG_ERR_S_CLOCKSKEW;\r
-            sugg_code = KHERR_SUGGEST_RETRY;\r
-            break;\r
-\r
-        case RD_AP_UNDEC             : /* 031 Can't decode\r
-                                          authenticator */\r
-        case RD_AP_EXP               : /* 032 Ticket expired */\r
-        case RD_AP_NYV               : /* 033 Ticket not yet valid */\r
-        case RD_AP_REPEAT    :  /* 034 Repeated request */\r
-        case RD_AP_NOT_US    :  /* 035 The ticket isn't for us */\r
-        case RD_AP_INCON             : /* 036 Request is inconsistent */\r
-        case RD_AP_BADD              : /* 038 Incorrect net address */\r
-        case RD_AP_VERSION   :  /* 039 protocol version mismatch */\r
-        case RD_AP_MSG_TYPE  :  /* 040 invalid msg type */\r
-        case RD_AP_MODIFIED  :  /* 041 message stream modified */\r
-        case RD_AP_ORDER             : /* 042 message out of order */\r
-        case RD_AP_UNAUTHOR  :  /* 043 unauthorized request */\r
-            /* no extra error msg */\r
-            sugg_code = KHERR_SUGGEST_RETRY;\r
-            break;\r
-\r
-        case GT_PW_NULL:     /* 51    Current PW is null */\r
-        case GT_PW_BADPW:    /* 52    Incorrect current password */\r
-        case GT_PW_PROT:     /* 53    Protocol Error */\r
-        case GT_PW_KDCERR:   /* 54    Error returned by KDC */\r
-        case GT_PW_NULLTKT:  /* 55    Null tkt returned by KDC */\r
-            /* no error msg yet */\r
-            sugg_code = KHERR_SUGGEST_RETRY;\r
-            break;\r
-         \r
-            /* Values returned by send_to_kdc */\r
-        case SKDC_RETRY   :     /* 56    Retry count exceeded */\r
-        case SKDC_CANT    :     /* 57    Can't send request */\r
-            msg_id = MSG_ERR_KDC_CONTACT;\r
-            break;\r
-            /* no error message on purpose: */\r
-        case INTK_BADPW      :  /* 062 Incorrect password */\r
-            sugg_code = KHERR_SUGGEST_RETRY;\r
-            break;\r
-        default:\r
-            /* no extra error msg */\r
-            break;\r
-        }\r
-    } else if (table_num == kadm_err_base) {\r
-        switch(code) {\r
-        case KADM_INSECURE_PW:\r
-            /* if( kadm_info != NULL ){\r
-             * wsprintf(buf, "%s\n%s", com_err_msg, kadm_info);\r
-             * } else {\r
-             * wsprintf(buf, "%s\nPlease see the help file for information "\r
-             * "about secure passwords.", com_err_msg);\r
-             * }\r
-             * com_err_msg = buf;\r
-             */\r
-\r
-            /* The above code would be preferred since it allows site\r
-             * specific information to be delivered from the Kerberos\r
-             * server. However the message box is too small for VGA\r
-             * screens.  It does work well if we only have to support\r
-             * 1024x768\r
-             */\r
-\r
-            msg_id = MSG_ERR_INSECURE_PW;\r
-            sugg_code = KHERR_SUGGEST_RETRY;\r
-            break;\r
-       \r
-        default:\r
-            /* no extra error msg */\r
-            break;\r
-        }\r
-    } else if (table_num == WSABASEERR) {\r
-        switch(code) {\r
-        case WSAENETDOWN:\r
-            msg_id = MSG_ERR_NETDOWN;\r
-            sugg_id = MSG_ERR_S_NETRETRY;\r
-            sugg_code = KHERR_SUGGEST_RETRY;\r
-            break;\r
-\r
-        case WSATRY_AGAIN:\r
-            msg_id = MSG_ERR_TEMPDOWN;\r
-            sugg_id = MSG_ERR_S_TEMPDOWN;\r
-            sugg_code = KHERR_SUGGEST_RETRY;\r
-            break;\r
-\r
-        case WSAENETUNREACH:\r
-        case WSAENETRESET:\r
-        case WSAECONNABORTED:\r
-        case WSAECONNRESET:\r
-        case WSAETIMEDOUT:\r
-        case WSAECONNREFUSED:\r
-        case WSAEHOSTDOWN:\r
-        case WSAEHOSTUNREACH:\r
-            msg_id = MSG_ERR_NOHOST;\r
-            sugg_id = MSG_ERR_S_NOHOST;\r
-            sugg_code = KHERR_SUGGEST_RETRY;\r
-            break;\r
-        }\r
-    }\r
-\r
-    if (msg_id != 0) {\r
-        FormatMessage(FORMAT_MESSAGE_FROM_HMODULE |\r
-                      FORMAT_MESSAGE_IGNORE_INSERTS,\r
-                      KHERR_HMODULE,\r
-                      msg_id,\r
-                      0,\r
-                      buf,\r
-                      (int) (cbbuf / sizeof(buf[0])),\r
-                      NULL);\r
-    }\r
-\r
-    if (sugg_id != 0) {\r
-        *suggestion = sugg_id;\r
-    }\r
-\r
-    if (sugg_code != KHERR_SUGGEST_NONE)\r
-        *suggest_code = sugg_code;\r
-}\r
-\r
-#ifdef DEPRECATED_REMOVABLE\r
-int lsh_com_err_proc (LPSTR whoami, long code,\r
-                              LPSTR fmt, va_list args)\r
-{\r
-    int retval;\r
-    HWND hOldFocus;\r
-    char buf[1024], *cp;\r
-    WORD mbformat = MB_OK | MB_ICONEXCLAMATION;\r
-  \r
-    cp = buf;\r
-    memset(buf, '\0', sizeof(buf));\r
-    cp[0] = '\0';\r
-  \r
-    if (code)\r
-    {\r
-        err_describe(buf, code);\r
-        while (*cp)\r
-            cp++;\r
-    }\r
-  \r
-    if (fmt)\r
-    {\r
-        if (fmt[0] == '%' && fmt[1] == 'b')\r
-       {\r
-            fmt += 2;\r
-            mbformat = va_arg(args, WORD);\r
-            /* if the first arg is a %b, we use it for the message\r
-               box MB_??? flags. */\r
-       }\r
-        if (code)\r
-       {\r
-            *cp++ = '\n';\r
-            *cp++ = '\n';\r
-       }\r
-        wvsprintfA((LPSTR)cp, fmt, args);\r
-    }\r
-    hOldFocus = GetFocus();\r
-    retval = MessageBoxA(/*GetRootParent(hOldFocus)*/NULL, buf, whoami, \r
-                        mbformat | MB_ICONHAND | MB_TASKMODAL);\r
-    SetFocus(hOldFocus);\r
-    return retval;\r
-}\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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;
+
+    if (WSABASEERR <= code && code < (WSABASEERR + 1064)) {
+        /* winsock error */
+        table_num = WSABASEERR;
+        offset = code - WSABASEERR;
+    }
+
+    switch(table_num)
+    {
+    case krb_err_base:
+    case kadm_err_base:
+    case WSABASEERR:
+       break;
+    default:
+
+        if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
+            *suggestion = MSG_ERR_S_INTEGRITY;
+        }
+        *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 if (table_num == kadm_err_base) {
+        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;
+        }
+    } else if (table_num == WSABASEERR) {
+        switch(code) {
+        case WSAENETDOWN:
+            msg_id = MSG_ERR_NETDOWN;
+            sugg_id = MSG_ERR_S_NETRETRY;
+            sugg_code = KHERR_SUGGEST_RETRY;
+            break;
+
+        case WSATRY_AGAIN:
+            msg_id = MSG_ERR_TEMPDOWN;
+            sugg_id = MSG_ERR_S_TEMPDOWN;
+            sugg_code = KHERR_SUGGEST_RETRY;
+            break;
+
+        case WSAENETUNREACH:
+        case WSAENETRESET:
+        case WSAECONNABORTED:
+        case WSAECONNRESET:
+        case WSAETIMEDOUT:
+        case WSAECONNREFUSED:
+        case WSAEHOSTDOWN:
+        case WSAEHOSTUNREACH:
+            msg_id = MSG_ERR_NOHOST;
+            sugg_id = MSG_ERR_S_NOHOST;
+            sugg_code = KHERR_SUGGEST_RETRY;
+            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
index 86fc5b44046a2ab3e5ea47c9bde7244efc1b8571..4b1d2e2b52b9c40db9d6659f2047eb82a3112d7a 100644 (file)
@@ -1,75 +1,75 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_ERR_H\r
-#define __KHIMAIRA_ERR_H\r
-\r
-/* All error handling and reporting related functions for the krb4/5\r
-   and AFS plugins */\r
-\r
-#include <errno.h>\r
-#include <com_err.h>\r
-/*\r
- * This is a hack needed because the real com_err.h does\r
- * not define err_func.  We need it in the case where\r
- * we pull in the real com_err instead of the krb4 \r
- * impostor.\r
- */\r
-#ifndef _DCNS_MIT_COM_ERR_H\r
-typedef LPSTR (*err_func)(int, long);\r
-#endif\r
-\r
-#include <krberr.h>\r
-#include <kadm_err.h>\r
-\r
-#define kadm_err_base ERROR_TABLE_BASE_kadm\r
-\r
-#include <stdarg.h>\r
-\r
-#ifndef KRBERR\r
-#define KRBERR(code) (code + krb_err_base)\r
-#endif\r
-\r
-/*! \internal\r
-    \brief Describe an error \r
-\r
-    \param[in] code Error code returned by Kerberos\r
-    \param[out] buf Receives the error string\r
-    \param[in] cbbuf Size of buffer pointed to by \a buf\r
-    \param[out] suggestion Message ID of suggestion\r
-    \param[out] suggest_code Suggestion ID\r
-*/\r
-void khm_err_describe(long code, wchar_t * buf, khm_size cbbuf, \r
-                      DWORD * suggestion, \r
-                      kherr_suggestion * suggest_code);\r
-\r
-/* */\r
-khm_int32 init_error_funcs();\r
-\r
-khm_int32 exit_error_funcs();\r
-\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
index 5edc02b1fe8ef306773e2fe5f3f65a919d86f9f4..b2b498e685d50779b1e6824e071dfcf68a6abc26 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#if _WIN32_WINNT < 0x501\r
-#undef _WIN32_WINNT\r
-#define _WIN32_WINNT 0x501\r
-#endif\r
-\r
-#include<krbcred.h>\r
-#include<krb5.h>\r
-#include<assert.h>\r
-#include<lm.h>\r
-#include<commctrl.h>\r
-#include<shlwapi.h>\r
-\r
-#include<strsafe.h>\r
-\r
-typedef struct tag_k5_file_cc {\r
-    wchar_t path[MAX_PATH];\r
-    khm_int32 flags;\r
-} k5_file_cc;\r
-\r
-#define K5_FCC_ALLOC_INCR  8\r
-\r
-#define K5_FCC_FLAG_EXISTS 1\r
-\r
-typedef struct tag_k5_ccc_data {\r
-    khm_boolean   inc_api;\r
-    khm_boolean   inc_mslsa;\r
-    k5_file_cc *  file_ccs;\r
-    khm_size      n_file_ccs;\r
-    khm_size      nc_file_ccs;\r
-} k5_ccc_data;\r
-\r
-typedef struct tag_k5_ccc_dlg_data {\r
-    khui_config_node node;\r
-    k5_ccc_data save;\r
-    k5_ccc_data work;\r
-} k5_ccc_dlg_data;\r
-\r
-void k5_free_file_ccs(k5_ccc_data * d) {\r
-    if (d->file_ccs)\r
-        PFREE(d->file_ccs);\r
-    d->n_file_ccs = 0;\r
-    d->nc_file_ccs = 0;\r
-}\r
-\r
-void k5_flush_file_ccs(k5_ccc_data * d) {\r
-    d->n_file_ccs = 0;\r
-}\r
-\r
-void k5_del_file_cc(k5_ccc_data * d, khm_size idx) {\r
-    if (idx > d->n_file_ccs)\r
-        return;\r
-\r
-    if (idx < d->n_file_ccs - 1) {\r
-        MoveMemory(&d->file_ccs[idx],\r
-                   &d->file_ccs[idx + 1],\r
-                   sizeof(d->file_ccs[0]) * (d->n_file_ccs - (idx + 1)));\r
-    }\r
-\r
-    d->n_file_ccs--;\r
-}\r
-\r
-void k5_add_file_cc(k5_ccc_data * d, wchar_t * path) {\r
-    khm_size i;\r
-    khm_size cch;\r
-\r
-    if (FAILED(StringCchLength(path, MAX_PATH, &cch)) ||\r
-        cch == 0)\r
-        return;\r
-\r
-    /* see if it's there first */\r
-    for (i=0; i < d->n_file_ccs; i++) {\r
-        if(!_wcsicmp(d->file_ccs[i].path, path))\r
-            return;\r
-    }\r
-\r
-    if (d->n_file_ccs == d->nc_file_ccs) {\r
-        k5_file_cc * f;\r
-\r
-        d->nc_file_ccs = UBOUNDSS(d->n_file_ccs + 1,\r
-                                  K5_FCC_ALLOC_INCR,\r
-                                  K5_FCC_ALLOC_INCR);\r
-#ifdef DEBUG\r
-        assert(d->nc_file_ccs > d->n_file_ccs);\r
-#endif\r
-        f = PMALLOC(sizeof(*f) * d->nc_file_ccs);\r
-        ZeroMemory(f, sizeof(*f) * d->nc_file_ccs);\r
-\r
-        if (d->n_file_ccs > 0) {\r
-#ifdef DEBUG\r
-            assert(d->file_ccs != NULL);\r
-#endif\r
-            memcpy(f, d->file_ccs, sizeof(*f) * d->n_file_ccs);\r
-        }\r
-        if (d->file_ccs)\r
-            PFREE(d->file_ccs);\r
-        d->file_ccs = f;\r
-    }\r
-\r
-    StringCbCopy(d->file_ccs[d->n_file_ccs].path,\r
-                 sizeof(d->file_ccs[0].path),\r
-                 path);\r
-    if(PathFileExists(path))\r
-        d->file_ccs[d->n_file_ccs].flags = K5_FCC_FLAG_EXISTS;\r
-    else\r
-        d->file_ccs[d->n_file_ccs].flags = 0;\r
-\r
-    d->n_file_ccs++;\r
-}\r
-\r
-void k5_read_file_cc_data(k5_ccc_data * d) {\r
-    khm_int32 t;\r
-    wchar_t * fclist = NULL;\r
-    wchar_t * fc;\r
-    khm_size cb;\r
-\r
-#ifdef DEBUG\r
-    assert(csp_params);\r
-#endif\r
-\r
-    d->inc_api = TRUE;\r
-    t = TRUE;\r
-    khc_read_int32(csp_params, L"MsLsaList", &t);\r
-    d->inc_mslsa = t;\r
-\r
-    if (khc_read_multi_string(csp_params, L"FileCCList", NULL, &cb)\r
-        != KHM_ERROR_TOO_LONG ||\r
-        cb <= sizeof(wchar_t) * 2) {\r
-\r
-        k5_flush_file_ccs(d);\r
-    } else {\r
-        fclist = PMALLOC(cb);\r
-#ifdef DEBUG\r
-        assert(fclist);\r
-#endif\r
-        khc_read_multi_string(csp_params, L"FileCCList", fclist, &cb);\r
-\r
-        for(fc = fclist; fc && *fc; fc = multi_string_next(fc)) {\r
-            k5_add_file_cc(d, fc);\r
-        }\r
-\r
-        PFREE(fclist);\r
-    }\r
-}\r
-\r
-void k5_write_file_cc_data(k5_ccc_data * d) {\r
-    wchar_t * ms;\r
-    khm_size cb;\r
-    khm_size cbt;\r
-    khm_int32 t;\r
-    khm_size i;\r
-\r
-#ifdef DEBUG\r
-    assert(csp_params);\r
-#endif\r
-    if (KHM_FAILED(khc_read_int32(csp_params, L"MsLsaList", &t)) ||\r
-        !!t != !!d->inc_mslsa) {\r
-        khc_write_int32(csp_params, L"MsLsaList", !!d->inc_mslsa);\r
-    }\r
-\r
-    if (d->n_file_ccs > 0) {\r
-        cb = d->n_file_ccs * MAX_PATH * sizeof(wchar_t);\r
-        ms = PMALLOC(cb);\r
-#ifdef DEBUG\r
-        assert(ms);\r
-#endif\r
-        multi_string_init(ms, cb);\r
-\r
-        for(i=0; i<d->n_file_ccs; i++) {\r
-            cbt = cb;\r
-            multi_string_append(ms, &cbt, d->file_ccs[i].path);\r
-        }\r
-\r
-        khc_write_multi_string(csp_params, L"FileCCList", ms);\r
-\r
-        PFREE(ms);\r
-    } else {\r
-        if (khc_read_multi_string(csp_params, L"FileCCList", NULL, &cb)\r
-            != KHM_ERROR_TOO_LONG ||\r
-            cb != sizeof(wchar_t) * 2)\r
-\r
-            khc_write_multi_string(csp_params, L"FileCCList", L"\0\0");\r
-    }\r
-}\r
-\r
-void k5_copy_file_cc_data(k5_ccc_data * dest, const k5_ccc_data * src) {\r
-    khm_size i;\r
-\r
-    k5_flush_file_ccs(dest);\r
-    dest->inc_mslsa = src->inc_mslsa;\r
-    dest->inc_api = src->inc_api;\r
-\r
-    for (i=0; i < src->n_file_ccs; i++) {\r
-        k5_add_file_cc(dest, src->file_ccs[i].path);\r
-    }\r
-}\r
-\r
-BOOL k5_ccc_get_mod(k5_ccc_dlg_data * d) {\r
-    khm_size i, j;\r
-\r
-    if (!!d->work.inc_mslsa != !!d->save.inc_mslsa ||\r
-        !!d->work.inc_api != !!d->save.inc_api ||\r
-        d->work.n_file_ccs != d->save.n_file_ccs)\r
-        return TRUE;\r
-\r
-    for (i=0; i < d->work.n_file_ccs; i++) {\r
-        for (j=0; j < d->save.n_file_ccs; j++) {\r
-            if (!_wcsicmp(d->work.file_ccs[i].path,\r
-                         d->save.file_ccs[j].path))\r
-                break;\r
-        }\r
-        if (j >= d->save.n_file_ccs)\r
-            return TRUE;\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-void k5_ccc_update_ui(HWND hwnd, k5_ccc_dlg_data * d) {\r
-    khm_size i;\r
-    HWND lv;\r
-\r
-    if (d->work.inc_api)\r
-        CheckDlgButton(hwnd, IDC_CFG_INCAPI, BST_CHECKED);\r
-    else\r
-        CheckDlgButton(hwnd, IDC_CFG_INCAPI, BST_UNCHECKED);\r
-    if (d->work.inc_mslsa)\r
-        CheckDlgButton(hwnd, IDC_CFG_INCMSLSA, BST_CHECKED);\r
-    else\r
-        CheckDlgButton(hwnd, IDC_CFG_INCMSLSA, BST_UNCHECKED);\r
-\r
-    lv = GetDlgItem(hwnd, IDC_CFG_FCLIST);\r
-#ifdef DEBUG\r
-    assert(lv);\r
-#endif\r
-    ListView_DeleteAllItems(lv);\r
-\r
-    for (i=0; i<d->work.n_file_ccs; i++) {\r
-        LVITEM lvi;\r
-\r
-        ZeroMemory(&lvi, sizeof(lvi));\r
-\r
-        lvi.mask = LVIF_PARAM | LVIF_TEXT;\r
-        lvi.lParam = (LPARAM) i;\r
-        lvi.pszText = d->work.file_ccs[i].path;\r
-\r
-        ListView_InsertItem(lv, &lvi);\r
-    }\r
-\r
-    if (k5_ccc_get_mod(d)) {\r
-        khui_cfg_set_flags(d->node,\r
-                           KHUI_CNFLAG_MODIFIED,\r
-                           KHUI_CNFLAG_MODIFIED);\r
-    } else {\r
-        khui_cfg_set_flags(d->node,\r
-                           0,\r
-                           KHUI_CNFLAG_MODIFIED);\r
-    }\r
-}\r
-\r
-void k5_ccc_update_data(HWND hwnd, k5_ccc_data * d) {\r
-    if (IsDlgButtonChecked(hwnd, IDC_CFG_INCAPI) == BST_CHECKED)\r
-        d->inc_api = TRUE;\r
-    else\r
-        d->inc_api = FALSE;\r
-\r
-    if (IsDlgButtonChecked(hwnd, IDC_CFG_INCMSLSA) == BST_CHECKED)\r
-        d->inc_mslsa = TRUE;\r
-    else\r
-        d->inc_mslsa = FALSE;\r
-    /* everything else is controlled by buttons */\r
-}\r
-\r
-INT_PTR CALLBACK \r
-k5_ccconfig_dlgproc(HWND hwnd,\r
-                    UINT uMsg,\r
-                    WPARAM wParam,\r
-                    LPARAM lParam) {\r
-\r
-    k5_ccc_dlg_data * d;\r
-\r
-    switch(uMsg) {\r
-    case WM_INITDIALOG:\r
-        d = PMALLOC(sizeof(*d));\r
-#ifdef DEBUG\r
-        assert(d);\r
-#endif\r
-        ZeroMemory(d, sizeof(*d));\r
-        k5_read_file_cc_data(&d->save);\r
-        k5_copy_file_cc_data(&d->work, &d->save);\r
-\r
-        d->node = (khui_config_node) lParam;\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-        SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);\r
-#pragma warning(pop)\r
-\r
-        {\r
-            LVCOLUMN lvc;\r
-            HWND lv;\r
-            wchar_t buf[256];\r
-            RECT r;\r
-\r
-            lv = GetDlgItem(hwnd, IDC_CFG_FCLIST);\r
-#ifdef DEBUG\r
-            assert(lv);\r
-#endif\r
-            ZeroMemory(&lvc, sizeof(lvc));\r
-            lvc.mask = LVCF_TEXT | LVCF_WIDTH;\r
-\r
-            LoadString(hResModule, IDS_CFG_FCTITLE,\r
-                       buf, ARRAYLENGTH(buf));\r
-\r
-            GetWindowRect(lv, &r);\r
-\r
-            lvc.pszText = buf;\r
-            lvc.cx = (r.right - r.left) * 9 / 10;\r
-\r
-            ListView_InsertColumn(lv, 0, &lvc);\r
-        }\r
-\r
-        SendDlgItemMessage(hwnd, IDC_CFG_FCNAME, EM_SETLIMITTEXT, \r
-                           MAX_PATH - 1, 0);\r
-\r
-        k5_ccc_update_ui(hwnd, d);\r
-        break;\r
-\r
-    case WM_COMMAND:\r
-        d = (k5_ccc_dlg_data *) (DWORD_PTR)\r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-        switch(wParam) {\r
-        case MAKEWPARAM(IDC_CFG_ADD, BN_CLICKED):\r
-            {\r
-                wchar_t path[MAX_PATH];\r
-                wchar_t cpath[MAX_PATH];\r
-                khm_size i;\r
-\r
-                GetDlgItemText(hwnd, IDC_CFG_FCNAME, \r
-                               cpath, ARRAYLENGTH(cpath));\r
-\r
-                PathCanonicalize(path, cpath);\r
-\r
-                if (!*path)\r
-                    return TRUE; /* nothing to add */\r
-\r
-                for (i=0; i < d->work.n_file_ccs; i++) {\r
-                    if (!_wcsicmp(path, d->work.file_ccs[i].path)) {\r
-\r
-                        /* allow the user to correct case, as appropriate */\r
-                        StringCbCopy(d->work.file_ccs[i].path,\r
-                                     sizeof(d->work.file_ccs[i].path),\r
-                                     path);\r
-                        k5_ccc_update_ui(hwnd, d);\r
-                        return TRUE;\r
-                    }\r
-                }\r
-\r
-                /* not there.  we need to add.  but check a few things\r
-                   first */\r
-                if (!PathFileExists(path)) {\r
-                    wchar_t title[64];\r
-                    wchar_t text[128];\r
-\r
-                    LoadString(hResModule, IDS_CFG_FCN_WARNING,\r
-                               title, ARRAYLENGTH(title));\r
-\r
-                    LoadString(hResModule, IDS_CFG_FCN_W_NOTFOUND,\r
-                               text, ARRAYLENGTH(text));\r
-#if _WIN32_WINNT >= 0x501\r
-                    if (IS_COMMCTL6())\r
-                    {\r
-                        EDITBALLOONTIP bt;\r
-\r
-                        bt.cbStruct = sizeof(bt);\r
-                        bt.pszTitle = title;\r
-                        bt.pszText = text;\r
-                        bt.ttiIcon = TTI_WARNING;\r
-\r
-                        SendDlgItemMessage(hwnd, IDC_CFG_FCNAME,\r
-                                           EM_SHOWBALLOONTIP,\r
-                                           0,\r
-                                           (LPARAM) &bt);\r
-                    } else {\r
-#endif\r
-                        MessageBox(hwnd, text, title, MB_OK | MB_ICONWARNING);\r
-#if _WIN32_WINNT >= 0x501\r
-                    }\r
-#endif\r
-                } else if (PathIsRelative(path)) {\r
-                    wchar_t title[64];\r
-                    wchar_t text[128];\r
-\r
-                    LoadString(hResModule, IDS_CFG_FCN_WARNING,\r
-                               title, ARRAYLENGTH(title));\r
-                    LoadString(hResModule, IDS_CFG_FCN_W_RELATIVE,\r
-                               text, ARRAYLENGTH(text));\r
-\r
-#if _WIN32_WINNT >= 0x501\r
-                    if (IS_COMMCTL6())\r
-                    {\r
-                        EDITBALLOONTIP bt;\r
-\r
-                        bt.cbStruct = sizeof(bt);\r
-                        bt.pszTitle = title;\r
-                        bt.pszText = text;\r
-                        bt.ttiIcon = TTI_WARNING;\r
-\r
-                        SendDlgItemMessage(hwnd, IDC_CFG_FCNAME,\r
-                                           EM_SHOWBALLOONTIP,\r
-                                           0,\r
-                                           (LPARAM) &bt);\r
-                    } else {\r
-#endif\r
-                        MessageBox(hwnd, text, title, MB_OK | MB_ICONWARNING);\r
-#if _WIN32_WINNT >= 0x501\r
-                    }\r
-#endif\r
-                }\r
-\r
-                k5_add_file_cc(&d->work, path);\r
-\r
-                k5_ccc_update_ui(hwnd, d);\r
-            }\r
-            return TRUE;\r
-\r
-        case MAKEWPARAM(IDC_CFG_BROWSE, BN_CLICKED):\r
-            {\r
-                OPENFILENAME ofn;\r
-                wchar_t path[MAX_PATH * 8];\r
-                wchar_t title[128];\r
-\r
-                ZeroMemory(&ofn, sizeof(ofn));\r
-                ZeroMemory(path, sizeof(path));\r
-\r
-                GetDlgItemText(hwnd, IDC_CFG_FCNAME,\r
-                               path, ARRAYLENGTH(path));\r
-\r
-                /* don't pass in invalid paths */\r
-                if (!PathFileExists(path))\r
-                    *path = 0;\r
-\r
-                ofn.lStructSize = sizeof(ofn);\r
-                ofn.hwndOwner = hwnd;\r
-                ofn.lpstrFilter = L"All files\0*.*\0\0";\r
-                ofn.nFilterIndex = 1;\r
-                ofn.lpstrFile = path;\r
-                ofn.nMaxFile = ARRAYLENGTH(path);\r
-                ofn.lpstrTitle = title;\r
-\r
-                LoadString(hResModule, IDS_CFG_FCOPENTITLE,\r
-                           title, ARRAYLENGTH(title));\r
-\r
-                ofn.Flags = OFN_ALLOWMULTISELECT |\r
-                    OFN_DONTADDTORECENT |\r
-                    OFN_FORCESHOWHIDDEN |\r
-                    OFN_EXPLORER;\r
-\r
-                if (GetOpenFileName(&ofn)) {\r
-                    wchar_t * p;\r
-                    wchar_t spath[MAX_PATH];\r
-\r
-                    p = multi_string_next(path);\r
-                    if (p) {\r
-                        /* multi select */\r
-                        for(;p && *p; p = multi_string_next(p)) {\r
-                            StringCbCopy(spath, sizeof(spath), path);\r
-                            PathAppend(spath, p);\r
-\r
-                            k5_add_file_cc(&d->work, spath);\r
-                        }\r
-                    } else {\r
-                        /* single select */\r
-                        k5_add_file_cc(&d->work, path);\r
-                    }\r
-                    k5_ccc_update_ui(hwnd, d);\r
-                }\r
-            }\r
-            return TRUE;\r
-\r
-        case MAKEWPARAM(IDC_CFG_REMOVE, BN_CLICKED):\r
-            {\r
-                khm_size i;\r
-                int lv_idx;\r
-                HWND lv;\r
-                wchar_t buf[MAX_PATH];\r
-\r
-                lv = GetDlgItem(hwnd, IDC_CFG_FCLIST);\r
-#ifdef DEBUG\r
-                assert(lv);\r
-#endif\r
-\r
-                lv_idx = -1;\r
-                while((lv_idx = ListView_GetNextItem(lv, lv_idx, \r
-                                                     LVNI_SELECTED)) != -1) {\r
-                    ListView_GetItemText(lv, lv_idx, 0, buf, ARRAYLENGTH(buf));\r
-                    for (i=0; i < d->work.n_file_ccs; i++) {\r
-                        if (!_wcsicmp(buf, d->work.file_ccs[i].path)) {\r
-                            k5_del_file_cc(&d->work, i);\r
-                            break;\r
-                        }\r
-                    }\r
-                }\r
-\r
-                k5_ccc_update_ui(hwnd, d);\r
-            }\r
-            return TRUE;\r
-\r
-        case MAKEWPARAM(IDC_CFG_INCAPI, BN_CLICKED):\r
-        case MAKEWPARAM(IDC_CFG_INCMSLSA, BN_CLICKED):\r
-            k5_ccc_update_data(hwnd, &d->work);\r
-            k5_ccc_update_ui(hwnd, d);\r
-            return TRUE;\r
-        }\r
-        break;\r
-\r
-    case WM_DESTROY:\r
-        d = (k5_ccc_dlg_data *) (DWORD_PTR)\r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        k5_free_file_ccs(&d->work);\r
-        k5_free_file_ccs(&d->save);\r
-        PFREE(d);\r
-        return TRUE;\r
-\r
-    case KHUI_WM_CFG_NOTIFY:\r
-        d = (k5_ccc_dlg_data *) (DWORD_PTR)\r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        switch(HIWORD(wParam)) {\r
-        case WMCFG_APPLY:\r
-            if (k5_ccc_get_mod(d)) {\r
-                k5_write_file_cc_data(&d->work);\r
-                k5_copy_file_cc_data(&d->save, &d->work);\r
-                khui_cfg_set_flags(d->node,\r
-                                   KHUI_CNFLAG_APPLIED,\r
-                                   KHUI_CNFLAG_APPLIED);\r
-                k5_ccc_update_ui(hwnd, d);\r
-\r
-                kmq_post_sub_msg(k5_sub, KMSG_CRED, KMSG_CRED_REFRESH, 0, 0);\r
-            }\r
-            break;\r
-        }\r
-    }\r
-    return FALSE;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/* $Id$ */
+
+#if _WIN32_WINNT < 0x501
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x501
+#endif
+
+#include<krbcred.h>
+#include<krb5.h>
+#include<assert.h>
+#include<lm.h>
+#include<commctrl.h>
+#include<shlwapi.h>
+
+#include<strsafe.h>
+
+typedef struct tag_k5_file_cc {
+    wchar_t path[MAX_PATH];
+    khm_int32 flags;
+} k5_file_cc;
+
+#define K5_FCC_ALLOC_INCR  8
+
+#define K5_FCC_FLAG_EXISTS 1
+
+typedef struct tag_k5_ccc_data {
+    khm_boolean   inc_api;
+    khm_boolean   inc_mslsa;
+    k5_file_cc *  file_ccs;
+    khm_size      n_file_ccs;
+    khm_size      nc_file_ccs;
+} k5_ccc_data;
+
+typedef struct tag_k5_ccc_dlg_data {
+    khui_config_node node;
+    k5_ccc_data save;
+    k5_ccc_data work;
+} k5_ccc_dlg_data;
+
+void k5_free_file_ccs(k5_ccc_data * d) {
+    if (d->file_ccs)
+        PFREE(d->file_ccs);
+    d->n_file_ccs = 0;
+    d->nc_file_ccs = 0;
+}
+
+void k5_flush_file_ccs(k5_ccc_data * d) {
+    d->n_file_ccs = 0;
+}
+
+void k5_del_file_cc(k5_ccc_data * d, khm_size idx) {
+    if (idx > d->n_file_ccs)
+        return;
+
+    if (idx < d->n_file_ccs - 1) {
+        MoveMemory(&d->file_ccs[idx],
+                   &d->file_ccs[idx + 1],
+                   sizeof(d->file_ccs[0]) * (d->n_file_ccs - (idx + 1)));
+    }
+
+    d->n_file_ccs--;
+}
+
+void k5_add_file_cc(k5_ccc_data * d, wchar_t * path) {
+    khm_size i;
+    khm_size cch;
+
+    if (FAILED(StringCchLength(path, MAX_PATH, &cch)) ||
+        cch == 0)
+        return;
+
+    /* see if it's there first */
+    for (i=0; i < d->n_file_ccs; i++) {
+        if(!_wcsicmp(d->file_ccs[i].path, path))
+            return;
+    }
+
+    if (d->n_file_ccs == d->nc_file_ccs) {
+        k5_file_cc * f;
+
+        d->nc_file_ccs = UBOUNDSS(d->n_file_ccs + 1,
+                                  K5_FCC_ALLOC_INCR,
+                                  K5_FCC_ALLOC_INCR);
+#ifdef DEBUG
+        assert(d->nc_file_ccs > d->n_file_ccs);
+#endif
+        f = PMALLOC(sizeof(*f) * d->nc_file_ccs);
+        ZeroMemory(f, sizeof(*f) * d->nc_file_ccs);
+
+        if (d->n_file_ccs > 0) {
+#ifdef DEBUG
+            assert(d->file_ccs != NULL);
+#endif
+            memcpy(f, d->file_ccs, sizeof(*f) * d->n_file_ccs);
+        }
+        if (d->file_ccs)
+            PFREE(d->file_ccs);
+        d->file_ccs = f;
+    }
+
+    StringCbCopy(d->file_ccs[d->n_file_ccs].path,
+                 sizeof(d->file_ccs[0].path),
+                 path);
+    if(PathFileExists(path))
+        d->file_ccs[d->n_file_ccs].flags = K5_FCC_FLAG_EXISTS;
+    else
+        d->file_ccs[d->n_file_ccs].flags = 0;
+
+    d->n_file_ccs++;
+}
+
+void k5_read_file_cc_data(k5_ccc_data * d) {
+    khm_int32 t;
+    wchar_t * fclist = NULL;
+    wchar_t * fc;
+    khm_size cb;
+
+#ifdef DEBUG
+    assert(csp_params);
+#endif
+
+    d->inc_api = TRUE;
+    t = TRUE;
+    khc_read_int32(csp_params, L"MsLsaList", &t);
+    d->inc_mslsa = t;
+
+    if (khc_read_multi_string(csp_params, L"FileCCList", NULL, &cb)
+        != KHM_ERROR_TOO_LONG ||
+        cb <= sizeof(wchar_t) * 2) {
+
+        k5_flush_file_ccs(d);
+    } else {
+        fclist = PMALLOC(cb);
+#ifdef DEBUG
+        assert(fclist);
+#endif
+        khc_read_multi_string(csp_params, L"FileCCList", fclist, &cb);
+
+        for(fc = fclist; fc && *fc; fc = multi_string_next(fc)) {
+            k5_add_file_cc(d, fc);
+        }
+
+        PFREE(fclist);
+    }
+}
+
+void k5_write_file_cc_data(k5_ccc_data * d) {
+    wchar_t * ms;
+    khm_size cb;
+    khm_size cbt;
+    khm_int32 t;
+    khm_size i;
+
+#ifdef DEBUG
+    assert(csp_params);
+#endif
+    if (KHM_FAILED(khc_read_int32(csp_params, L"MsLsaList", &t)) ||
+        !!t != !!d->inc_mslsa) {
+        khc_write_int32(csp_params, L"MsLsaList", !!d->inc_mslsa);
+    }
+
+    if (d->n_file_ccs > 0) {
+        cb = d->n_file_ccs * MAX_PATH * sizeof(wchar_t);
+        ms = PMALLOC(cb);
+#ifdef DEBUG
+        assert(ms);
+#endif
+        multi_string_init(ms, cb);
+
+        for(i=0; i<d->n_file_ccs; i++) {
+            cbt = cb;
+            multi_string_append(ms, &cbt, d->file_ccs[i].path);
+        }
+
+        khc_write_multi_string(csp_params, L"FileCCList", ms);
+
+        PFREE(ms);
+    } else {
+        if (khc_read_multi_string(csp_params, L"FileCCList", NULL, &cb)
+            != KHM_ERROR_TOO_LONG ||
+            cb != sizeof(wchar_t) * 2)
+
+            khc_write_multi_string(csp_params, L"FileCCList", L"\0\0");
+    }
+}
+
+void k5_copy_file_cc_data(k5_ccc_data * dest, const k5_ccc_data * src) {
+    khm_size i;
+
+    k5_flush_file_ccs(dest);
+    dest->inc_mslsa = src->inc_mslsa;
+    dest->inc_api = src->inc_api;
+
+    for (i=0; i < src->n_file_ccs; i++) {
+        k5_add_file_cc(dest, src->file_ccs[i].path);
+    }
+}
+
+BOOL k5_ccc_get_mod(k5_ccc_dlg_data * d) {
+    khm_size i, j;
+
+    if (!!d->work.inc_mslsa != !!d->save.inc_mslsa ||
+        !!d->work.inc_api != !!d->save.inc_api ||
+        d->work.n_file_ccs != d->save.n_file_ccs)
+        return TRUE;
+
+    for (i=0; i < d->work.n_file_ccs; i++) {
+        for (j=0; j < d->save.n_file_ccs; j++) {
+            if (!_wcsicmp(d->work.file_ccs[i].path,
+                         d->save.file_ccs[j].path))
+                break;
+        }
+        if (j >= d->save.n_file_ccs)
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+void k5_ccc_update_ui(HWND hwnd, k5_ccc_dlg_data * d) {
+    khm_size i;
+    HWND lv;
+
+    if (d->work.inc_api)
+        CheckDlgButton(hwnd, IDC_CFG_INCAPI, BST_CHECKED);
+    else
+        CheckDlgButton(hwnd, IDC_CFG_INCAPI, BST_UNCHECKED);
+    if (d->work.inc_mslsa)
+        CheckDlgButton(hwnd, IDC_CFG_INCMSLSA, BST_CHECKED);
+    else
+        CheckDlgButton(hwnd, IDC_CFG_INCMSLSA, BST_UNCHECKED);
+
+    lv = GetDlgItem(hwnd, IDC_CFG_FCLIST);
+#ifdef DEBUG
+    assert(lv);
+#endif
+    ListView_DeleteAllItems(lv);
+
+    for (i=0; i<d->work.n_file_ccs; i++) {
+        LVITEM lvi;
+
+        ZeroMemory(&lvi, sizeof(lvi));
+
+        lvi.mask = LVIF_PARAM | LVIF_TEXT;
+        lvi.lParam = (LPARAM) i;
+        lvi.pszText = d->work.file_ccs[i].path;
+
+        ListView_InsertItem(lv, &lvi);
+    }
+
+    if (k5_ccc_get_mod(d)) {
+        khui_cfg_set_flags(d->node,
+                           KHUI_CNFLAG_MODIFIED,
+                           KHUI_CNFLAG_MODIFIED);
+    } else {
+        khui_cfg_set_flags(d->node,
+                           0,
+                           KHUI_CNFLAG_MODIFIED);
+    }
+}
+
+void k5_ccc_update_data(HWND hwnd, k5_ccc_data * d) {
+    if (IsDlgButtonChecked(hwnd, IDC_CFG_INCAPI) == BST_CHECKED)
+        d->inc_api = TRUE;
+    else
+        d->inc_api = FALSE;
+
+    if (IsDlgButtonChecked(hwnd, IDC_CFG_INCMSLSA) == BST_CHECKED)
+        d->inc_mslsa = TRUE;
+    else
+        d->inc_mslsa = FALSE;
+    /* everything else is controlled by buttons */
+}
+
+INT_PTR CALLBACK 
+k5_ccconfig_dlgproc(HWND hwnd,
+                    UINT uMsg,
+                    WPARAM wParam,
+                    LPARAM lParam) {
+
+    k5_ccc_dlg_data * d;
+
+    switch(uMsg) {
+    case WM_INITDIALOG:
+        d = PMALLOC(sizeof(*d));
+#ifdef DEBUG
+        assert(d);
+#endif
+        ZeroMemory(d, sizeof(*d));
+        k5_read_file_cc_data(&d->save);
+        k5_copy_file_cc_data(&d->work, &d->save);
+
+        d->node = (khui_config_node) lParam;
+
+#pragma warning(push)
+#pragma warning(disable: 4244)
+        SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);
+#pragma warning(pop)
+
+        {
+            LVCOLUMN lvc;
+            HWND lv;
+            wchar_t buf[256];
+            RECT r;
+
+            lv = GetDlgItem(hwnd, IDC_CFG_FCLIST);
+#ifdef DEBUG
+            assert(lv);
+#endif
+            ZeroMemory(&lvc, sizeof(lvc));
+            lvc.mask = LVCF_TEXT | LVCF_WIDTH;
+
+            LoadString(hResModule, IDS_CFG_FCTITLE,
+                       buf, ARRAYLENGTH(buf));
+
+            GetWindowRect(lv, &r);
+
+            lvc.pszText = buf;
+            lvc.cx = (r.right - r.left) * 9 / 10;
+
+            ListView_InsertColumn(lv, 0, &lvc);
+        }
+
+        SendDlgItemMessage(hwnd, IDC_CFG_FCNAME, EM_SETLIMITTEXT, 
+                           MAX_PATH - 1, 0);
+
+        k5_ccc_update_ui(hwnd, d);
+        break;
+
+    case WM_COMMAND:
+        d = (k5_ccc_dlg_data *) (DWORD_PTR)
+            GetWindowLongPtr(hwnd, DWLP_USER);
+        switch(wParam) {
+        case MAKEWPARAM(IDC_CFG_ADD, BN_CLICKED):
+            {
+                wchar_t path[MAX_PATH];
+                wchar_t cpath[MAX_PATH];
+                khm_size i;
+
+                GetDlgItemText(hwnd, IDC_CFG_FCNAME, 
+                               cpath, ARRAYLENGTH(cpath));
+
+                PathCanonicalize(path, cpath);
+
+                if (!*path)
+                    return TRUE; /* nothing to add */
+
+                for (i=0; i < d->work.n_file_ccs; i++) {
+                    if (!_wcsicmp(path, d->work.file_ccs[i].path)) {
+
+                        /* allow the user to correct case, as appropriate */
+                        StringCbCopy(d->work.file_ccs[i].path,
+                                     sizeof(d->work.file_ccs[i].path),
+                                     path);
+                        k5_ccc_update_ui(hwnd, d);
+                        return TRUE;
+                    }
+                }
+
+                /* not there.  we need to add.  but check a few things
+                   first */
+                if (!PathFileExists(path)) {
+                    wchar_t title[64];
+                    wchar_t text[128];
+
+                    LoadString(hResModule, IDS_CFG_FCN_WARNING,
+                               title, ARRAYLENGTH(title));
+
+                    LoadString(hResModule, IDS_CFG_FCN_W_NOTFOUND,
+                               text, ARRAYLENGTH(text));
+#if _WIN32_WINNT >= 0x501
+                    if (IS_COMMCTL6())
+                    {
+                        EDITBALLOONTIP bt;
+
+                        bt.cbStruct = sizeof(bt);
+                        bt.pszTitle = title;
+                        bt.pszText = text;
+                        bt.ttiIcon = TTI_WARNING;
+
+                        SendDlgItemMessage(hwnd, IDC_CFG_FCNAME,
+                                           EM_SHOWBALLOONTIP,
+                                           0,
+                                           (LPARAM) &bt);
+                    } else {
+#endif
+                        MessageBox(hwnd, text, title, MB_OK | MB_ICONWARNING);
+#if _WIN32_WINNT >= 0x501
+                    }
+#endif
+                } else if (PathIsRelative(path)) {
+                    wchar_t title[64];
+                    wchar_t text[128];
+
+                    LoadString(hResModule, IDS_CFG_FCN_WARNING,
+                               title, ARRAYLENGTH(title));
+                    LoadString(hResModule, IDS_CFG_FCN_W_RELATIVE,
+                               text, ARRAYLENGTH(text));
+
+#if _WIN32_WINNT >= 0x501
+                    if (IS_COMMCTL6())
+                    {
+                        EDITBALLOONTIP bt;
+
+                        bt.cbStruct = sizeof(bt);
+                        bt.pszTitle = title;
+                        bt.pszText = text;
+                        bt.ttiIcon = TTI_WARNING;
+
+                        SendDlgItemMessage(hwnd, IDC_CFG_FCNAME,
+                                           EM_SHOWBALLOONTIP,
+                                           0,
+                                           (LPARAM) &bt);
+                    } else {
+#endif
+                        MessageBox(hwnd, text, title, MB_OK | MB_ICONWARNING);
+#if _WIN32_WINNT >= 0x501
+                    }
+#endif
+                }
+
+                k5_add_file_cc(&d->work, path);
+
+                k5_ccc_update_ui(hwnd, d);
+            }
+            return TRUE;
+
+        case MAKEWPARAM(IDC_CFG_BROWSE, BN_CLICKED):
+            {
+                OPENFILENAME ofn;
+                wchar_t path[MAX_PATH * 8];
+                wchar_t title[128];
+
+                ZeroMemory(&ofn, sizeof(ofn));
+                ZeroMemory(path, sizeof(path));
+
+                GetDlgItemText(hwnd, IDC_CFG_FCNAME,
+                               path, ARRAYLENGTH(path));
+
+                /* don't pass in invalid paths */
+                if (!PathFileExists(path))
+                    *path = 0;
+
+                ofn.lStructSize = sizeof(ofn);
+                ofn.hwndOwner = hwnd;
+                ofn.lpstrFilter = L"All files\0*.*\0\0";
+                ofn.nFilterIndex = 1;
+                ofn.lpstrFile = path;
+                ofn.nMaxFile = ARRAYLENGTH(path);
+                ofn.lpstrTitle = title;
+
+                LoadString(hResModule, IDS_CFG_FCOPENTITLE,
+                           title, ARRAYLENGTH(title));
+
+                ofn.Flags = OFN_ALLOWMULTISELECT |
+                    OFN_DONTADDTORECENT |
+                    OFN_FORCESHOWHIDDEN |
+                    OFN_EXPLORER;
+
+                if (GetOpenFileName(&ofn)) {
+                    wchar_t * p;
+                    wchar_t spath[MAX_PATH];
+
+                    p = multi_string_next(path);
+                    if (p) {
+                        /* multi select */
+                        for(;p && *p; p = multi_string_next(p)) {
+                            StringCbCopy(spath, sizeof(spath), path);
+                            PathAppend(spath, p);
+
+                            k5_add_file_cc(&d->work, spath);
+                        }
+                    } else {
+                        /* single select */
+                        k5_add_file_cc(&d->work, path);
+                    }
+                    k5_ccc_update_ui(hwnd, d);
+                }
+            }
+            return TRUE;
+
+        case MAKEWPARAM(IDC_CFG_REMOVE, BN_CLICKED):
+            {
+                khm_size i;
+                int lv_idx;
+                HWND lv;
+                wchar_t buf[MAX_PATH];
+
+                lv = GetDlgItem(hwnd, IDC_CFG_FCLIST);
+#ifdef DEBUG
+                assert(lv);
+#endif
+
+                lv_idx = -1;
+                while((lv_idx = ListView_GetNextItem(lv, lv_idx, 
+                                                     LVNI_SELECTED)) != -1) {
+                    ListView_GetItemText(lv, lv_idx, 0, buf, ARRAYLENGTH(buf));
+                    for (i=0; i < d->work.n_file_ccs; i++) {
+                        if (!_wcsicmp(buf, d->work.file_ccs[i].path)) {
+                            k5_del_file_cc(&d->work, i);
+                            break;
+                        }
+                    }
+                }
+
+                k5_ccc_update_ui(hwnd, d);
+            }
+            return TRUE;
+
+        case MAKEWPARAM(IDC_CFG_INCAPI, BN_CLICKED):
+        case MAKEWPARAM(IDC_CFG_INCMSLSA, BN_CLICKED):
+            k5_ccc_update_data(hwnd, &d->work);
+            k5_ccc_update_ui(hwnd, d);
+            return TRUE;
+        }
+        break;
+
+    case WM_DESTROY:
+        d = (k5_ccc_dlg_data *) (DWORD_PTR)
+            GetWindowLongPtr(hwnd, DWLP_USER);
+
+        k5_free_file_ccs(&d->work);
+        k5_free_file_ccs(&d->save);
+        PFREE(d);
+        return TRUE;
+
+    case KHUI_WM_CFG_NOTIFY:
+        d = (k5_ccc_dlg_data *) (DWORD_PTR)
+            GetWindowLongPtr(hwnd, DWLP_USER);
+
+        switch(HIWORD(wParam)) {
+        case WMCFG_APPLY:
+            if (k5_ccc_get_mod(d)) {
+                k5_write_file_cc_data(&d->work);
+                k5_copy_file_cc_data(&d->save, &d->work);
+                khui_cfg_set_flags(d->node,
+                                   KHUI_CNFLAG_APPLIED,
+                                   KHUI_CNFLAG_APPLIED);
+                k5_ccc_update_ui(hwnd, d);
+
+                kmq_post_sub_msg(k5_sub, KMSG_CRED, KMSG_CRED_REFRESH, 0, 0);
+            }
+            break;
+        }
+    }
+    return FALSE;
+}
index e09281906e59178b514203c1d57fef77ec5d9db5..b5af1c2b2eb88ba0d1895bc1e66c37babafc1e88 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<krbcred.h>\r
-#include<krb5.h>\r
-#include<assert.h>\r
-#include<lm.h>\r
-#include<commctrl.h>\r
-#include<shlwapi.h>\r
-\r
-#include<strsafe.h>\r
-\r
-typedef struct tag_k5_id_dlg_data {\r
-    khui_config_init_data cfg;\r
-\r
-    khm_handle   ident;\r
-\r
-    khui_tracker tc_life;\r
-    khui_tracker tc_renew;\r
-\r
-    wchar_t ccache[KRB5_MAXCCH_CCNAME];\r
-\r
-    khm_boolean renewable;\r
-    khm_boolean forwardable;\r
-    khm_boolean proxiable;\r
-    khm_boolean addressless;\r
-\r
-    DWORD public_ip;\r
-\r
-    time_t life;\r
-    time_t renew_life;\r
-} k5_id_dlg_data;\r
-\r
-static void\r
-k5_id_read_params(k5_id_dlg_data * d) {\r
-\r
-    wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-    khm_size cb;\r
-    khm_int32 rv;\r
-    khm_int32 t;\r
-    khm_handle csp_ident;\r
-    khm_handle csp_idroot = NULL;\r
-\r
-    cb = sizeof(idname);\r
-    rv = khui_cfg_get_name(d->cfg.ctx_node, idname, &cb);\r
-#ifdef DEBUG\r
-    assert(KHM_SUCCEEDED(rv));\r
-#endif\r
-\r
-    rv = kcdb_identity_create(idname, 0, &d->ident);\r
-#ifdef DEBUG\r
-    assert(KHM_SUCCEEDED(rv));\r
-#endif\r
-\r
-    rv = kcdb_identity_get_config(d->ident, 0, &csp_idroot);\r
-    if (KHM_SUCCEEDED(rv) &&\r
-        KHM_SUCCEEDED(khc_open_space(csp_idroot, CSNAME_KRB5CRED, 0,\r
-                                     &csp_ident))) {\r
-        khc_shadow_space(csp_ident, csp_params);\r
-    } else {\r
-        csp_ident = csp_params;\r
-    }\r
-\r
-    if (csp_idroot)\r
-        khc_close_space(csp_idroot);\r
-\r
-    rv = khc_read_int32(csp_ident, L"DefaultLifetime",  &t);\r
-    if (KHM_SUCCEEDED(rv))\r
-        d->life = t;\r
-    else\r
-        d->life = 36000;\r
-\r
-    rv = khc_read_int32(csp_ident, L"DefaultRenewLifetime", &t);\r
-    if (KHM_SUCCEEDED(rv))\r
-        d->renew_life = t;\r
-    else\r
-        d->renew_life = 604800;\r
-\r
-    rv = khc_read_int32(csp_ident, L"Renewable", &t);\r
-    if (KHM_SUCCEEDED(rv))\r
-        d->renewable = !!t;\r
-    else\r
-        d->renewable = TRUE;\r
-\r
-    rv = khc_read_int32(csp_ident, L"Forwardable", &t);\r
-    if (KHM_SUCCEEDED(rv))\r
-        d->forwardable = !!t;\r
-    else\r
-        d->forwardable = FALSE;\r
-\r
-    rv = khc_read_int32(csp_ident, L"Proxiable", &t);\r
-    if (KHM_SUCCEEDED(rv))\r
-        d->proxiable = !!t;\r
-    else\r
-        d->proxiable = FALSE;\r
-\r
-    rv = khc_read_int32(csp_ident, L"Addressless", &t);\r
-    if (KHM_SUCCEEDED(rv))\r
-        d->addressless = !!t;\r
-    else\r
-        d->addressless = TRUE;\r
-\r
-    rv = khc_read_int32(csp_ident, L"PublicIP", &t);\r
-    if (KHM_SUCCEEDED(rv))\r
-        d->public_ip = (khm_ui_4) t;\r
-    else\r
-        d->public_ip = 0;\r
-\r
-    cb = sizeof(d->ccache);\r
-    rv = khc_read_string(csp_ident, L"DefaultCCName", d->ccache, &cb);\r
-    if (KHM_FAILED(rv) || cb <= sizeof(wchar_t)) {\r
-        cb = sizeof(d->ccache);\r
-        if (KHM_FAILED(kcdb_identity_get_attr(d->ident, attr_id_krb5_ccname,\r
-                                              NULL, d->ccache, &cb)))\r
-            ZeroMemory(d->ccache, sizeof(d->ccache));\r
-    }\r
-\r
-    khui_tracker_initialize(&d->tc_life);\r
-    d->tc_life.current = d->life;\r
-    d->tc_life.min = 0;\r
-    d->tc_life.max = 3600 * 24 * 7;\r
-\r
-    khui_tracker_initialize(&d->tc_renew);\r
-    d->tc_renew.current = d->renew_life;\r
-    d->tc_renew.min = 0;\r
-    d->tc_renew.max = 3600 * 24 * 30;\r
-\r
-    if (csp_ident != csp_params)\r
-        khc_close_space(csp_ident);\r
-}\r
-\r
-static khm_boolean\r
-k5_id_is_mod(HWND hw, k5_id_dlg_data * d) {\r
-    wchar_t ccache[KRB5_MAXCCH_CCNAME];\r
-    DWORD dwaddress = 0;\r
-\r
-    GetDlgItemText(hw, IDC_CFG_CCACHE, ccache, ARRAYLENGTH(ccache));\r
-\r
-    SendDlgItemMessage(hw, IDC_CFG_PUBLICIP, IPM_GETADDRESS,\r
-                       0, (LPARAM) &dwaddress);\r
-\r
-    if (_wcsicmp(ccache, d->ccache) ||\r
-\r
-        d->tc_renew.current != d->renew_life ||\r
-\r
-        d->tc_life.current != d->life ||\r
-\r
-        (IsDlgButtonChecked(hw, IDC_CFG_RENEW) == BST_CHECKED) != d->renewable ||\r
-\r
-        (IsDlgButtonChecked(hw, IDC_CFG_FORWARD) == BST_CHECKED) != d->forwardable ||\r
-\r
-        (IsDlgButtonChecked(hw, IDC_CFG_ADDRESSLESS) == BST_CHECKED)\r
-        != d->addressless ||\r
-\r
-        dwaddress != d->public_ip)\r
-\r
-        return TRUE;\r
-\r
-    return FALSE;\r
-}\r
-\r
-static void\r
-k5_id_check_mod(HWND hw, k5_id_dlg_data * d) {\r
-    BOOL modified = k5_id_is_mod(hw, d);\r
-\r
-    khui_cfg_set_flags_inst(&d->cfg,\r
-                            (modified)?KHUI_CNFLAG_MODIFIED:0,\r
-                            KHUI_CNFLAG_MODIFIED);\r
-}\r
-\r
-static void\r
-k5_id_write_params(HWND hw, k5_id_dlg_data * d) {\r
-\r
-    khm_handle csp_idroot = NULL;\r
-    khm_handle csp_ident = NULL;\r
-    wchar_t ccache[KRB5_MAXCCH_CCNAME];\r
-    khm_size cb;\r
-    khm_int32 rv;\r
-    khm_boolean b;\r
-    DWORD dwaddress = 0;\r
-\r
-    if (!k5_id_is_mod(hw, d))\r
-        return;\r
-\r
-    rv = kcdb_identity_get_config(d->ident, KHM_FLAG_CREATE, &csp_idroot);\r
-    if (KHM_SUCCEEDED(rv)) {\r
-        khc_open_space(csp_idroot, CSNAME_KRB5CRED,\r
-                       KHM_FLAG_CREATE,\r
-                       &csp_ident);\r
-    }\r
-\r
-    if (csp_idroot)\r
-        khc_close_space(csp_idroot);\r
-\r
-    if (!csp_ident)\r
-        return;\r
-\r
-    if (d->life != d->tc_life.current) {\r
-        d->life = d->tc_life.current;\r
-        khc_write_int32(csp_ident, L"DefaultLifetime", (khm_int32) d->life);\r
-    }\r
-\r
-    if (d->renew_life != d->tc_renew.current) {\r
-        d->renew_life = d->tc_renew.current;\r
-        khc_write_int32(csp_ident, L"DefaultRenewLifetime", (khm_int32) d->renew_life);\r
-    }\r
-\r
-    b = (IsDlgButtonChecked(hw, IDC_CFG_RENEW) == BST_CHECKED);\r
-    if (b != d->renewable) {\r
-        d->renewable = b;\r
-        khc_write_int32(csp_ident, L"Renewable", (khm_int32) b);\r
-    }\r
-\r
-    b = (IsDlgButtonChecked(hw, IDC_CFG_FORWARD) == BST_CHECKED);\r
-    if (b != d->forwardable) {\r
-        d->forwardable = b;\r
-        khc_write_int32(csp_ident, L"Forwardable", (khm_int32) b);\r
-    }\r
-\r
-    b = (IsDlgButtonChecked(hw, IDC_CFG_ADDRESSLESS) == BST_CHECKED);\r
-    if (b != d->addressless) {\r
-        d->addressless = b;\r
-        khc_write_int32(csp_ident, L"Addressless", (khm_int32) b);\r
-    }\r
-\r
-    SendDlgItemMessage(hw, IDC_CFG_PUBLICIP, IPM_GETADDRESS,\r
-                       0, (LPARAM) &dwaddress);\r
-\r
-    if (dwaddress != d->public_ip) {\r
-        d->public_ip = dwaddress;\r
-        khc_write_int32(csp_ident, L"PublicIP", (khm_int32) dwaddress);\r
-    }\r
-\r
-    GetDlgItemText(hw, IDC_CFG_CCACHE, ccache, ARRAYLENGTH(ccache));\r
-\r
-    if (SUCCEEDED(StringCbLength(ccache, sizeof(ccache), &cb)) &&\r
-        _wcsicmp(ccache, d->ccache)) {\r
-        khc_write_string(csp_ident, L"DefaultCCName", ccache);\r
-        StringCbCopy(d->ccache, sizeof(d->ccache), ccache);\r
-    } else {\r
-        khc_remove_value(csp_ident, L"DefaultCCName", KCONF_FLAG_USER);\r
-    }\r
-\r
-    if (csp_ident)\r
-        khc_close_space(csp_ident);\r
-\r
-    khui_cfg_set_flags_inst(&d->cfg,\r
-                            KHUI_CNFLAG_APPLIED,\r
-                            KHUI_CNFLAG_APPLIED | KHUI_CNFLAG_MODIFIED);\r
-}\r
-\r
-INT_PTR CALLBACK \r
-k5_id_tab_dlgproc(HWND hwnd,\r
-                  UINT uMsg,\r
-                  WPARAM wParam,\r
-                  LPARAM lParam) {\r
-\r
-    k5_id_dlg_data * d;\r
-\r
-    switch(uMsg) {\r
-    case WM_INITDIALOG:\r
-        d = PMALLOC(sizeof(*d));\r
-#ifdef DEBUG\r
-        assert(d);\r
-#endif\r
-        ZeroMemory(d, sizeof(*d));\r
-\r
-        d->cfg = *((khui_config_init_data *) lParam);\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-        SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);\r
-#pragma warning(pop)\r
-\r
-        k5_id_read_params(d);\r
-\r
-        khui_tracker_install(GetDlgItem(hwnd, IDC_CFG_DEFLIFE),\r
-                             &d->tc_life);\r
-        khui_tracker_install(GetDlgItem(hwnd, IDC_CFG_DEFRLIFE),\r
-                             &d->tc_renew);\r
-        khui_tracker_refresh(&d->tc_life);\r
-        khui_tracker_refresh(&d->tc_renew);\r
-\r
-        SetDlgItemText(hwnd, IDC_CFG_CCACHE, d->ccache);\r
-\r
-        CheckDlgButton(hwnd, IDC_CFG_RENEW,\r
-                       (d->renewable? BST_CHECKED: BST_UNCHECKED));\r
-\r
-        CheckDlgButton(hwnd, IDC_CFG_FORWARD,\r
-                       (d->forwardable? BST_CHECKED: BST_UNCHECKED));\r
-\r
-        CheckDlgButton(hwnd, IDC_CFG_ADDRESSLESS,\r
-                       (d->addressless? BST_CHECKED: BST_UNCHECKED));\r
-\r
-        SendDlgItemMessage(hwnd, IDC_CFG_PUBLICIP,\r
-                           IPM_SETADDRESS,\r
-                           0, (LPARAM) d->public_ip);\r
-        break;\r
-\r
-    case WM_COMMAND:\r
-        d = (k5_id_dlg_data *) (LONG_PTR)\r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        if (HIWORD(wParam) == EN_CHANGE ||\r
-            HIWORD(wParam) == BN_CLICKED)\r
-            k5_id_check_mod(hwnd, d);\r
-        break;\r
-\r
-    case KHUI_WM_CFG_NOTIFY:\r
-        d = (k5_id_dlg_data *) (LONG_PTR)\r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        if (HIWORD(wParam) == WMCFG_APPLY) {\r
-            k5_id_write_params(hwnd, d);\r
-        }\r
-        break;\r
-\r
-    case WM_DESTROY:\r
-        d = (k5_id_dlg_data *) (LONG_PTR)\r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        khui_tracker_kill_controls(&d->tc_life);\r
-        khui_tracker_kill_controls(&d->tc_renew);\r
-\r
-        if (d->ident)\r
-            kcdb_identity_release(d->ident);\r
-\r
-        PFREE(d);\r
-        break;\r
-    }\r
-    return FALSE;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+#include<commctrl.h>
+#include<shlwapi.h>
+
+#include<strsafe.h>
+
+typedef struct tag_k5_id_dlg_data {
+    khui_config_init_data cfg;
+
+    khm_handle   ident;
+
+    khui_tracker tc_life;
+    khui_tracker tc_renew;
+
+    wchar_t ccache[KRB5_MAXCCH_CCNAME];
+
+    khm_boolean renewable;
+    khm_boolean forwardable;
+    khm_boolean proxiable;
+    khm_boolean addressless;
+
+    DWORD public_ip;
+
+    time_t life;
+    time_t renew_life;
+} k5_id_dlg_data;
+
+static void
+k5_id_read_params(k5_id_dlg_data * d) {
+
+    wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
+    khm_size cb;
+    khm_int32 rv;
+    khm_int32 t;
+    khm_handle csp_ident;
+    khm_handle csp_idroot = NULL;
+
+    cb = sizeof(idname);
+    rv = khui_cfg_get_name(d->cfg.ctx_node, idname, &cb);
+#ifdef DEBUG
+    assert(KHM_SUCCEEDED(rv));
+#endif
+
+    rv = kcdb_identity_create(idname, 0, &d->ident);
+#ifdef DEBUG
+    assert(KHM_SUCCEEDED(rv));
+#endif
+
+    rv = kcdb_identity_get_config(d->ident, 0, &csp_idroot);
+    if (KHM_SUCCEEDED(rv) &&
+        KHM_SUCCEEDED(khc_open_space(csp_idroot, CSNAME_KRB5CRED, 0,
+                                     &csp_ident))) {
+        khc_shadow_space(csp_ident, csp_params);
+    } else {
+        csp_ident = csp_params;
+    }
+
+    if (csp_idroot)
+        khc_close_space(csp_idroot);
+
+    rv = khc_read_int32(csp_ident, L"DefaultLifetime",  &t);
+    if (KHM_SUCCEEDED(rv))
+        d->life = t;
+    else
+        d->life = 36000;
+
+    rv = khc_read_int32(csp_ident, L"DefaultRenewLifetime", &t);
+    if (KHM_SUCCEEDED(rv))
+        d->renew_life = t;
+    else
+        d->renew_life = 604800;
+
+    rv = khc_read_int32(csp_ident, L"Renewable", &t);
+    if (KHM_SUCCEEDED(rv))
+        d->renewable = !!t;
+    else
+        d->renewable = TRUE;
+
+    rv = khc_read_int32(csp_ident, L"Forwardable", &t);
+    if (KHM_SUCCEEDED(rv))
+        d->forwardable = !!t;
+    else
+        d->forwardable = FALSE;
+
+    rv = khc_read_int32(csp_ident, L"Proxiable", &t);
+    if (KHM_SUCCEEDED(rv))
+        d->proxiable = !!t;
+    else
+        d->proxiable = FALSE;
+
+    rv = khc_read_int32(csp_ident, L"Addressless", &t);
+    if (KHM_SUCCEEDED(rv))
+        d->addressless = !!t;
+    else
+        d->addressless = TRUE;
+
+    rv = khc_read_int32(csp_ident, L"PublicIP", &t);
+    if (KHM_SUCCEEDED(rv))
+        d->public_ip = (khm_ui_4) t;
+    else
+        d->public_ip = 0;
+
+    cb = sizeof(d->ccache);
+    rv = khc_read_string(csp_ident, L"DefaultCCName", d->ccache, &cb);
+    if (KHM_FAILED(rv) || cb <= sizeof(wchar_t)) {
+        cb = sizeof(d->ccache);
+        if (KHM_FAILED(kcdb_identity_get_attr(d->ident, attr_id_krb5_ccname,
+                                              NULL, d->ccache, &cb)))
+            ZeroMemory(d->ccache, sizeof(d->ccache));
+    }
+
+    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;
+
+    if (csp_ident != csp_params)
+        khc_close_space(csp_ident);
+}
+
+static khm_boolean
+k5_id_is_mod(HWND hw, k5_id_dlg_data * d) {
+    wchar_t ccache[KRB5_MAXCCH_CCNAME];
+    DWORD dwaddress = 0;
+
+    GetDlgItemText(hw, IDC_CFG_CCACHE, ccache, ARRAYLENGTH(ccache));
+
+    SendDlgItemMessage(hw, IDC_CFG_PUBLICIP, IPM_GETADDRESS,
+                       0, (LPARAM) &dwaddress);
+
+    if (_wcsicmp(ccache, d->ccache) ||
+
+        d->tc_renew.current != d->renew_life ||
+
+        d->tc_life.current != d->life ||
+
+        (IsDlgButtonChecked(hw, IDC_CFG_RENEW) == BST_CHECKED) != d->renewable ||
+
+        (IsDlgButtonChecked(hw, IDC_CFG_FORWARD) == BST_CHECKED) != d->forwardable ||
+
+        (IsDlgButtonChecked(hw, IDC_CFG_ADDRESSLESS) == BST_CHECKED)
+        != d->addressless ||
+
+        dwaddress != d->public_ip)
+
+        return TRUE;
+
+    return FALSE;
+}
+
+static void
+k5_id_check_mod(HWND hw, k5_id_dlg_data * d) {
+    BOOL modified = k5_id_is_mod(hw, d);
+
+    khui_cfg_set_flags_inst(&d->cfg,
+                            (modified)?KHUI_CNFLAG_MODIFIED:0,
+                            KHUI_CNFLAG_MODIFIED);
+}
+
+static void
+k5_id_write_params(HWND hw, k5_id_dlg_data * d) {
+
+    khm_handle csp_idroot = NULL;
+    khm_handle csp_ident = NULL;
+    wchar_t ccache[KRB5_MAXCCH_CCNAME];
+    khm_size cb;
+    khm_int32 rv;
+    khm_boolean b;
+    DWORD dwaddress = 0;
+
+    if (!k5_id_is_mod(hw, d))
+        return;
+
+    rv = kcdb_identity_get_config(d->ident, KHM_FLAG_CREATE, &csp_idroot);
+    if (KHM_SUCCEEDED(rv)) {
+        khc_open_space(csp_idroot, CSNAME_KRB5CRED,
+                       KHM_FLAG_CREATE,
+                       &csp_ident);
+    }
+
+    if (csp_idroot)
+        khc_close_space(csp_idroot);
+
+    if (!csp_ident)
+        return;
+
+    if (d->life != d->tc_life.current) {
+        d->life = d->tc_life.current;
+        khc_write_int32(csp_ident, L"DefaultLifetime", (khm_int32) d->life);
+    }
+
+    if (d->renew_life != d->tc_renew.current) {
+        d->renew_life = d->tc_renew.current;
+        khc_write_int32(csp_ident, L"DefaultRenewLifetime", (khm_int32) d->renew_life);
+    }
+
+    b = (IsDlgButtonChecked(hw, IDC_CFG_RENEW) == BST_CHECKED);
+    if (b != d->renewable) {
+        d->renewable = b;
+        khc_write_int32(csp_ident, L"Renewable", (khm_int32) b);
+    }
+
+    b = (IsDlgButtonChecked(hw, IDC_CFG_FORWARD) == BST_CHECKED);
+    if (b != d->forwardable) {
+        d->forwardable = b;
+        khc_write_int32(csp_ident, L"Forwardable", (khm_int32) b);
+    }
+
+    b = (IsDlgButtonChecked(hw, IDC_CFG_ADDRESSLESS) == BST_CHECKED);
+    if (b != d->addressless) {
+        d->addressless = b;
+        khc_write_int32(csp_ident, L"Addressless", (khm_int32) b);
+    }
+
+    SendDlgItemMessage(hw, IDC_CFG_PUBLICIP, IPM_GETADDRESS,
+                       0, (LPARAM) &dwaddress);
+
+    if (dwaddress != d->public_ip) {
+        d->public_ip = dwaddress;
+        khc_write_int32(csp_ident, L"PublicIP", (khm_int32) dwaddress);
+    }
+
+    GetDlgItemText(hw, IDC_CFG_CCACHE, ccache, ARRAYLENGTH(ccache));
+
+    if (SUCCEEDED(StringCbLength(ccache, sizeof(ccache), &cb)) &&
+        _wcsicmp(ccache, d->ccache)) {
+        khc_write_string(csp_ident, L"DefaultCCName", ccache);
+        StringCbCopy(d->ccache, sizeof(d->ccache), ccache);
+    } else {
+        khc_remove_value(csp_ident, L"DefaultCCName", KCONF_FLAG_USER);
+    }
+
+    if (csp_ident)
+        khc_close_space(csp_ident);
+
+    khui_cfg_set_flags_inst(&d->cfg,
+                            KHUI_CNFLAG_APPLIED,
+                            KHUI_CNFLAG_APPLIED | KHUI_CNFLAG_MODIFIED);
+}
+
+INT_PTR CALLBACK 
+k5_id_tab_dlgproc(HWND hwnd,
+                  UINT uMsg,
+                  WPARAM wParam,
+                  LPARAM lParam) {
+
+    k5_id_dlg_data * d;
+
+    switch(uMsg) {
+    case WM_INITDIALOG:
+        d = PMALLOC(sizeof(*d));
+#ifdef DEBUG
+        assert(d);
+#endif
+        ZeroMemory(d, sizeof(*d));
+
+        d->cfg = *((khui_config_init_data *) lParam);
+
+#pragma warning(push)
+#pragma warning(disable: 4244)
+        SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);
+#pragma warning(pop)
+
+        k5_id_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_refresh(&d->tc_life);
+        khui_tracker_refresh(&d->tc_renew);
+
+        SetDlgItemText(hwnd, IDC_CFG_CCACHE, d->ccache);
+
+        CheckDlgButton(hwnd, IDC_CFG_RENEW,
+                       (d->renewable? BST_CHECKED: BST_UNCHECKED));
+
+        CheckDlgButton(hwnd, IDC_CFG_FORWARD,
+                       (d->forwardable? BST_CHECKED: BST_UNCHECKED));
+
+        CheckDlgButton(hwnd, IDC_CFG_ADDRESSLESS,
+                       (d->addressless? BST_CHECKED: BST_UNCHECKED));
+
+        SendDlgItemMessage(hwnd, IDC_CFG_PUBLICIP,
+                           IPM_SETADDRESS,
+                           0, (LPARAM) d->public_ip);
+        break;
+
+    case WM_COMMAND:
+        d = (k5_id_dlg_data *) (LONG_PTR)
+            GetWindowLongPtr(hwnd, DWLP_USER);
+
+        if (HIWORD(wParam) == EN_CHANGE ||
+            HIWORD(wParam) == BN_CLICKED)
+            k5_id_check_mod(hwnd, d);
+        break;
+
+    case KHUI_WM_CFG_NOTIFY:
+        d = (k5_id_dlg_data *) (LONG_PTR)
+            GetWindowLongPtr(hwnd, DWLP_USER);
+
+        if (HIWORD(wParam) == WMCFG_APPLY) {
+            k5_id_write_params(hwnd, d);
+        }
+        break;
+
+    case WM_DESTROY:
+        d = (k5_id_dlg_data *) (LONG_PTR)
+            GetWindowLongPtr(hwnd, DWLP_USER);
+
+        khui_tracker_kill_controls(&d->tc_life);
+        khui_tracker_kill_controls(&d->tc_renew);
+
+        if (d->ident)
+            kcdb_identity_release(d->ident);
+
+        PFREE(d);
+        break;
+    }
+    return FALSE;
+}
index 5f4729253e975fdf68d93ba808f9d952e21c353a..8d6afd4731f1945b60a6f17b1de254ffa1e95e26 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<krbcred.h>\r
-#include<krb5.h>\r
-#include<assert.h>\r
-#include<lm.h>\r
-#include<commctrl.h>\r
-#include<shlwapi.h>\r
-\r
-typedef struct tag_k5_ids_opts {\r
-    khm_int32 renewable;\r
-    khm_int32 forwardable;\r
-    khm_int32 addressless;\r
-} k5_ids_opts;\r
-\r
-typedef struct tag_k5_ids_dlg_data {\r
-    khui_config_init_data cfg;\r
-\r
-    khui_tracker tc_life;\r
-    khui_tracker tc_renew;\r
-    khui_tracker tc_life_min;\r
-    khui_tracker tc_life_max;\r
-    khui_tracker tc_renew_min;\r
-    khui_tracker tc_renew_max;\r
-\r
-    time_t life;\r
-    time_t renew_life;\r
-    time_t life_min;\r
-    time_t life_max;\r
-    time_t renew_min;\r
-    time_t renew_max;\r
-\r
-    k5_ids_opts opt;\r
-    k5_ids_opts opt_saved;\r
-\r
-} k5_ids_dlg_data;\r
-\r
-static khm_boolean\r
-k5_ids_is_mod(k5_ids_dlg_data * d) {\r
-    if (d->life != d->tc_life.current ||\r
-        d->renew_life != d->tc_renew.current ||\r
-        d->life_max != d->tc_life_max.current ||\r
-        d->life_min != d->tc_life_min.current ||\r
-        d->renew_max != d->tc_renew_max.current ||\r
-        d->renew_min != d->tc_renew_min.current ||\r
-        !!d->opt.renewable != !!d->opt_saved.renewable ||\r
-        !!d->opt.forwardable != !!d->opt_saved.forwardable ||\r
-        !!d->opt.addressless != !!d->opt_saved.addressless)\r
-        return TRUE;\r
-    return FALSE;\r
-}\r
-\r
-static void\r
-k5_ids_check_mod(k5_ids_dlg_data * d) {\r
-    BOOL modified = k5_ids_is_mod(d);\r
-\r
-    khui_cfg_set_flags_inst(&d->cfg,\r
-                            (modified)?KHUI_CNFLAG_MODIFIED:0,\r
-                            KHUI_CNFLAG_MODIFIED);\r
-}\r
-\r
-static void\r
-k5_ids_write_params(k5_ids_dlg_data * d) {\r
-\r
-    khm_int32 rv;\r
-\r
-#ifdef DEBUG\r
-    assert(csp_params);\r
-#endif\r
-\r
-    if (!k5_ids_is_mod(d))\r
-        return;\r
-\r
-#define WRITEPARAM(po,pn,vn) \\r
-  if (po != pn) {            \\r
-   po = pn;                  \\r
-   rv = khc_write_int32(csp_params, vn, (khm_int32) po); \\r
-   assert(KHM_SUCCEEDED(rv));       \\r
-  }\r
-    \r
-    WRITEPARAM(d->life,d->tc_life.current, L"DefaultLifetime");\r
-    WRITEPARAM(d->renew_life,d->tc_renew.current, L"DefaultRenewLifetime");\r
-    WRITEPARAM(d->life_max,d->tc_life_max.current, L"MaxLifetime");\r
-    WRITEPARAM(d->life_min,d->tc_life_min.current, L"MinLifetime");\r
-    WRITEPARAM(d->renew_max,d->tc_renew_max.current, L"MaxRenewLifetime");\r
-    WRITEPARAM(d->renew_min,d->tc_renew_min.current, L"MinRenewLifetime");\r
-    WRITEPARAM(d->opt_saved.renewable, d->opt.renewable, L"Renewable");\r
-    WRITEPARAM(d->opt_saved.forwardable, d->opt.forwardable, L"Forwardable");\r
-    WRITEPARAM(d->opt_saved.addressless, d->opt.addressless, L"Addressless");\r
-\r
-#undef WRITEPARAM\r
-\r
-    khui_cfg_set_flags_inst(&d->cfg,\r
-                            KHUI_CNFLAG_APPLIED,\r
-                            KHUI_CNFLAG_APPLIED | KHUI_CNFLAG_MODIFIED);\r
-}\r
-\r
-static void\r
-k5_ids_read_params(k5_ids_dlg_data * d) {\r
-    k5_params p;\r
-\r
-    khm_krb5_get_identity_params(NULL, &p);\r
-\r
-    d->life = p.lifetime;\r
-    d->life_max = p.lifetime_max;\r
-    d->life_min = p.lifetime_min;\r
-\r
-    d->renew_life = p.renew_life;\r
-    d->renew_max = p.renew_life_max;\r
-    d->renew_min = p.renew_life_min;\r
-\r
-    d->opt_saved.forwardable = p.forwardable;\r
-    d->opt_saved.renewable = p.renewable;\r
-    d->opt_saved.addressless = p.addressless;\r
-\r
-    d->opt = d->opt_saved;\r
-\r
-    khui_tracker_initialize(&d->tc_life);\r
-    d->tc_life.current = d->life;\r
-    d->tc_life.min = 0;\r
-    d->tc_life.max = 3600 * 24 * 7;\r
-\r
-    khui_tracker_initialize(&d->tc_renew);\r
-    d->tc_renew.current = d->renew_life;\r
-    d->tc_renew.min = 0;\r
-    d->tc_renew.max = 3600 * 24 * 30;\r
-\r
-    khui_tracker_initialize(&d->tc_life_min);\r
-    d->tc_life_min.current = d->life_min;\r
-    d->tc_life_min.min = d->tc_life.min;\r
-    d->tc_life_min.max = d->tc_life.max;\r
-\r
-    khui_tracker_initialize(&d->tc_life_max);\r
-    d->tc_life_max.current = d->life_max;\r
-    d->tc_life_max.min = d->tc_life.min;\r
-    d->tc_life_max.max = d->tc_life.max;\r
-\r
-    khui_tracker_initialize(&d->tc_renew_min);\r
-    d->tc_renew_min.current = d->renew_min;\r
-    d->tc_renew_min.min = d->tc_renew.min;\r
-    d->tc_renew_min.max = d->tc_renew.max;\r
-\r
-    khui_tracker_initialize(&d->tc_renew_max);\r
-    d->tc_renew_max.current = d->renew_max;\r
-    d->tc_renew_max.min = d->tc_renew.min;\r
-    d->tc_renew_max.max = d->tc_renew.max;\r
-}\r
-\r
-INT_PTR CALLBACK \r
-k5_ids_tab_dlgproc(HWND hwnd,\r
-                  UINT uMsg,\r
-                  WPARAM wParam,\r
-                  LPARAM lParam) {\r
-    k5_ids_dlg_data * d;\r
-\r
-    switch(uMsg) {\r
-    case WM_INITDIALOG:\r
-        d = PMALLOC(sizeof(*d));\r
-#ifdef DEBUG\r
-        assert(d);\r
-#endif\r
-        ZeroMemory(d, sizeof(*d));\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-        SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);\r
-#pragma warning(pop)\r
-\r
-        d->cfg = *((khui_config_init_data *) lParam);\r
-\r
-        k5_ids_read_params(d);\r
-\r
-        khui_tracker_install(GetDlgItem(hwnd, IDC_CFG_DEFLIFE),\r
-                             &d->tc_life);\r
-        khui_tracker_install(GetDlgItem(hwnd, IDC_CFG_DEFRLIFE),\r
-                             &d->tc_renew);\r
-        khui_tracker_install(GetDlgItem(hwnd, IDC_CFG_LRNG_MIN),\r
-                             &d->tc_life_min);\r
-        khui_tracker_install(GetDlgItem(hwnd, IDC_CFG_LRNG_MAX),\r
-                             &d->tc_life_max);\r
-        khui_tracker_install(GetDlgItem(hwnd, IDC_CFG_RLRNG_MIN),\r
-                             &d->tc_renew_min);\r
-        khui_tracker_install(GetDlgItem(hwnd, IDC_CFG_RLRNG_MAX),\r
-                             &d->tc_renew_max);\r
-        khui_tracker_refresh(&d->tc_life);\r
-        khui_tracker_refresh(&d->tc_life_min);\r
-        khui_tracker_refresh(&d->tc_life_max);\r
-        khui_tracker_refresh(&d->tc_renew);\r
-        khui_tracker_refresh(&d->tc_renew_min);\r
-        khui_tracker_refresh(&d->tc_renew_max);\r
-\r
-        CheckDlgButton(hwnd, IDC_CFG_RENEW, (d->opt.renewable ? BST_CHECKED: BST_UNCHECKED));\r
-        CheckDlgButton(hwnd, IDC_CFG_FORWARD, (d->opt.forwardable ? BST_CHECKED: BST_UNCHECKED));\r
-        CheckDlgButton(hwnd, IDC_CFG_ADDRESSLESS, (d->opt.addressless ? BST_CHECKED: BST_UNCHECKED));\r
-        break;\r
-\r
-    case WM_COMMAND:\r
-        d = (k5_ids_dlg_data *) (LONG_PTR)\r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        if (HIWORD(wParam) == EN_CHANGE) {\r
-            k5_ids_check_mod(d);\r
-        } else if (HIWORD(wParam) == BN_CLICKED) {\r
-            switch (LOWORD(wParam)) {\r
-            case IDC_CFG_RENEW:\r
-                d->opt.renewable = (IsDlgButtonChecked(hwnd, IDC_CFG_RENEW) == BST_CHECKED);\r
-                break;\r
-\r
-            case IDC_CFG_FORWARD:\r
-                d->opt.forwardable = (IsDlgButtonChecked(hwnd, IDC_CFG_FORWARD) == BST_CHECKED);\r
-                break;\r
-\r
-            case IDC_CFG_ADDRESSLESS:\r
-                d->opt.addressless = (IsDlgButtonChecked(hwnd, IDC_CFG_ADDRESSLESS) == BST_CHECKED);\r
-                break;\r
-            }\r
-\r
-            k5_ids_check_mod(d);\r
-        }\r
-        break;\r
-\r
-    case KHUI_WM_CFG_NOTIFY:\r
-        d = (k5_ids_dlg_data *) (LONG_PTR)\r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-        if (HIWORD(wParam) == WMCFG_APPLY) {\r
-            k5_ids_write_params(d);\r
-        }\r
-        break;\r
-\r
-    case WM_DESTROY:\r
-        d = (k5_ids_dlg_data *) (LONG_PTR)\r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        khui_tracker_kill_controls(&d->tc_life);\r
-        khui_tracker_kill_controls(&d->tc_renew);\r
-        khui_tracker_kill_controls(&d->tc_life_min);\r
-        khui_tracker_kill_controls(&d->tc_life_max);\r
-        khui_tracker_kill_controls(&d->tc_renew_min);\r
-        khui_tracker_kill_controls(&d->tc_renew_max);\r
-\r
-        PFREE(d);\r
-        break;\r
-    }\r
-    return FALSE;\r
-}\r
-\r
-\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+#include<commctrl.h>
+#include<shlwapi.h>
+
+typedef struct tag_k5_ids_opts {
+    khm_int32 renewable;
+    khm_int32 forwardable;
+    khm_int32 addressless;
+} k5_ids_opts;
+
+typedef struct tag_k5_ids_dlg_data {
+    khui_config_init_data cfg;
+
+    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_opts opt;
+    k5_ids_opts opt_saved;
+
+} k5_ids_dlg_data;
+
+static khm_boolean
+k5_ids_is_mod(k5_ids_dlg_data * d) {
+    if (d->life != d->tc_life.current ||
+        d->renew_life != d->tc_renew.current ||
+        d->life_max != d->tc_life_max.current ||
+        d->life_min != d->tc_life_min.current ||
+        d->renew_max != d->tc_renew_max.current ||
+        d->renew_min != d->tc_renew_min.current ||
+        !!d->opt.renewable != !!d->opt_saved.renewable ||
+        !!d->opt.forwardable != !!d->opt_saved.forwardable ||
+        !!d->opt.addressless != !!d->opt_saved.addressless)
+        return TRUE;
+    return FALSE;
+}
+
+static void
+k5_ids_check_mod(k5_ids_dlg_data * d) {
+    BOOL modified = k5_ids_is_mod(d);
+
+    khui_cfg_set_flags_inst(&d->cfg,
+                            (modified)?KHUI_CNFLAG_MODIFIED:0,
+                            KHUI_CNFLAG_MODIFIED);
+}
+
+static void
+k5_ids_write_params(k5_ids_dlg_data * d) {
+
+    khm_int32 rv;
+
+#ifdef DEBUG
+    assert(csp_params);
+#endif
+
+    if (!k5_ids_is_mod(d))
+        return;
+
+#define WRITEPARAM(po,pn,vn) \
+  if (po != pn) {            \
+   po = pn;                  \
+   rv = khc_write_int32(csp_params, vn, (khm_int32) po); \
+   assert(KHM_SUCCEEDED(rv));       \
+  }
+    
+    WRITEPARAM(d->life,d->tc_life.current, L"DefaultLifetime");
+    WRITEPARAM(d->renew_life,d->tc_renew.current, L"DefaultRenewLifetime");
+    WRITEPARAM(d->life_max,d->tc_life_max.current, L"MaxLifetime");
+    WRITEPARAM(d->life_min,d->tc_life_min.current, L"MinLifetime");
+    WRITEPARAM(d->renew_max,d->tc_renew_max.current, L"MaxRenewLifetime");
+    WRITEPARAM(d->renew_min,d->tc_renew_min.current, L"MinRenewLifetime");
+    WRITEPARAM(d->opt_saved.renewable, d->opt.renewable, L"Renewable");
+    WRITEPARAM(d->opt_saved.forwardable, d->opt.forwardable, L"Forwardable");
+    WRITEPARAM(d->opt_saved.addressless, d->opt.addressless, L"Addressless");
+
+#undef WRITEPARAM
+
+    khui_cfg_set_flags_inst(&d->cfg,
+                            KHUI_CNFLAG_APPLIED,
+                            KHUI_CNFLAG_APPLIED | KHUI_CNFLAG_MODIFIED);
+}
+
+static void
+k5_ids_read_params(k5_ids_dlg_data * d) {
+    k5_params p;
+
+    khm_krb5_get_identity_params(NULL, &p);
+
+    d->life = p.lifetime;
+    d->life_max = p.lifetime_max;
+    d->life_min = p.lifetime_min;
+
+    d->renew_life = p.renew_life;
+    d->renew_max = p.renew_life_max;
+    d->renew_min = p.renew_life_min;
+
+    d->opt_saved.forwardable = p.forwardable;
+    d->opt_saved.renewable = p.renewable;
+    d->opt_saved.addressless = p.addressless;
+
+    d->opt = d->opt_saved;
+
+    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 = PMALLOC(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)
+
+        d->cfg = *((khui_config_init_data *) lParam);
+
+        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);
+
+        CheckDlgButton(hwnd, IDC_CFG_RENEW, (d->opt.renewable ? BST_CHECKED: BST_UNCHECKED));
+        CheckDlgButton(hwnd, IDC_CFG_FORWARD, (d->opt.forwardable ? BST_CHECKED: BST_UNCHECKED));
+        CheckDlgButton(hwnd, IDC_CFG_ADDRESSLESS, (d->opt.addressless ? BST_CHECKED: BST_UNCHECKED));
+        break;
+
+    case WM_COMMAND:
+        d = (k5_ids_dlg_data *) (LONG_PTR)
+            GetWindowLongPtr(hwnd, DWLP_USER);
+
+        if (HIWORD(wParam) == EN_CHANGE) {
+            k5_ids_check_mod(d);
+        } else if (HIWORD(wParam) == BN_CLICKED) {
+            switch (LOWORD(wParam)) {
+            case IDC_CFG_RENEW:
+                d->opt.renewable = (IsDlgButtonChecked(hwnd, IDC_CFG_RENEW) == BST_CHECKED);
+                break;
+
+            case IDC_CFG_FORWARD:
+                d->opt.forwardable = (IsDlgButtonChecked(hwnd, IDC_CFG_FORWARD) == BST_CHECKED);
+                break;
+
+            case IDC_CFG_ADDRESSLESS:
+                d->opt.addressless = (IsDlgButtonChecked(hwnd, IDC_CFG_ADDRESSLESS) == BST_CHECKED);
+                break;
+            }
+
+            k5_ids_check_mod(d);
+        }
+        break;
+
+    case KHUI_WM_CFG_NOTIFY:
+        d = (k5_ids_dlg_data *) (LONG_PTR)
+            GetWindowLongPtr(hwnd, DWLP_USER);
+        if (HIWORD(wParam) == WMCFG_APPLY) {
+            k5_ids_write_params(d);
+        }
+        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);
+
+        PFREE(d);
+        break;
+    }
+    return FALSE;
+}
+
+
index e66e755a74b0bbabf6e30458c568868d8da407b2..6f657e85199537406844c62906e9ff4449a86ce2 100644 (file)
-/*\r
-* Copyright (c) 2005 Massachusetts Institute of Technology\r
-* Copyright (c) 2006,2007 Secure Endpoints Inc.\r
-*\r
-* Permission is hereby granted, free of charge, to any person\r
-* obtaining a copy of this software and associated documentation\r
-* files (the "Software"), to deal in the Software without\r
-* restriction, including without limitation the rights to use, copy,\r
-* modify, merge, publish, distribute, sublicense, and/or sell copies\r
-* of the Software, and to permit persons to whom the Software is\r
-* furnished to do so, subject to the following conditions:\r
-*\r
-* The above copyright notice and this permission notice shall be\r
-* included in all copies or substantial portions of the Software.\r
-*\r
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
-* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
-* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
-* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
-* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
-* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
-* SOFTWARE.\r
-*/\r
-\r
-/* $Id$ */\r
-\r
-/* Originally this was krb5routines.c in Leash sources.  Subsequently\r
- * modified and adapted for NetIDMgr */\r
-\r
-#include<krbcred.h>\r
-#include<kherror.h>\r
-\r
-#define SECURITY_WIN32\r
-#include <security.h>\r
-\r
-#include <string.h>\r
-#include <time.h>\r
-#include <stdlib.h>\r
-#include <assert.h>\r
-#include <strsafe.h>\r
-\r
-long\r
-khm_convert524(krb5_context alt_ctx)\r
-{\r
-    krb5_context ctx = 0;\r
-    krb5_error_code code = 0;\r
-    int icode = 0;\r
-    krb5_principal me = 0;\r
-    krb5_principal server = 0;\r
-    krb5_creds *v5creds = 0;\r
-    krb5_creds increds;\r
-    krb5_ccache cc = 0;\r
-    CREDENTIALS * v4creds = NULL;\r
-    static int init_ets = 1;\r
-\r
-    if (!pkrb5_init_context ||\r
-        !pkrb_in_tkt ||\r
-        !pkrb524_init_ets ||\r
-        !pkrb524_convert_creds_kdc)\r
-        return 0;\r
-\r
-    v4creds = (CREDENTIALS *) PMALLOC(sizeof(CREDENTIALS));\r
-    memset((char *) v4creds, 0, sizeof(CREDENTIALS));\r
-\r
-    memset((char *) &increds, 0, sizeof(increds));\r
-    /*\r
-    From this point on, we can goto cleanup because increds is\r
-    initialized.\r
-    */\r
-\r
-    if (alt_ctx)\r
-    {\r
-        ctx = alt_ctx;\r
-    }\r
-    else\r
-    {\r
-        code = pkrb5_init_context(&ctx);\r
-        if (code) goto cleanup;\r
-    }\r
-\r
-    code = pkrb5_cc_default(ctx, &cc);\r
-    if (code) goto cleanup;\r
-\r
-    if ( init_ets ) {\r
-        pkrb524_init_ets(ctx);\r
-        init_ets = 0;\r
-    }\r
-\r
-    if (code = pkrb5_cc_get_principal(ctx, cc, &me))\r
-        goto cleanup;\r
-\r
-    if ((code = pkrb5_build_principal(ctx,\r
-        &server,\r
-        krb5_princ_realm(ctx, me)->length,\r
-        krb5_princ_realm(ctx, me)->data,\r
-        "krbtgt",\r
-        krb5_princ_realm(ctx, me)->data,\r
-        NULL))) \r
-    {\r
-        goto cleanup;\r
-    }\r
-\r
-    increds.client = me;\r
-    increds.server = server;\r
-    increds.times.endtime = 0;\r
-    increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC;\r
-    if ((code = pkrb5_get_credentials(ctx, 0,\r
-                                      cc,\r
-                                      &increds,\r
-                                      &v5creds))) \r
-    {\r
-        goto cleanup;\r
-    }\r
-\r
-    if ((icode = pkrb524_convert_creds_kdc(ctx,\r
-        v5creds,\r
-        v4creds))) \r
-    {\r
-        goto cleanup;\r
-    }\r
-\r
-    /* initialize ticket cache */\r
-    if ((icode = pkrb_in_tkt(v4creds->pname, v4creds->pinst, v4creds->realm)\r
-        != KSUCCESS)) \r
-    {\r
-        goto cleanup;\r
-    }\r
-    /* stash ticket, session key, etc. for future use */\r
-    if ((icode = pkrb_save_credentials(v4creds->service,\r
-        v4creds->instance,\r
-        v4creds->realm,\r
-        v4creds->session,\r
-        v4creds->lifetime,\r
-        v4creds->kvno,\r
-        &(v4creds->ticket_st),\r
-        v4creds->issue_date))) \r
-    {\r
-        goto cleanup;\r
-    }\r
-\r
-cleanup:\r
-    memset(v4creds, 0, sizeof(v4creds));\r
-    PFREE(v4creds);\r
-\r
-    if (v5creds) {\r
-        pkrb5_free_creds(ctx, v5creds);\r
-    }\r
-    if (increds.client == me)\r
-        me = 0;\r
-    if (increds.server == server)\r
-        server = 0;\r
-    pkrb5_free_cred_contents(ctx, &increds);\r
-    if (server) {\r
-        pkrb5_free_principal(ctx, server);\r
-    }\r
-    if (me) {\r
-        pkrb5_free_principal(ctx, me);\r
-    }\r
-    pkrb5_cc_close(ctx, cc);\r
-\r
-    if (ctx && (ctx != alt_ctx)) {\r
-        pkrb5_free_context(ctx);\r
-    }\r
-    return !(code || icode);\r
-}\r
-\r
-#ifdef DEPRECATED_REMOVABLE\r
-int com_addr(void)\r
-{\r
-    long ipAddr;\r
-    char loc_addr[ADDR_SZ];\r
-    CREDENTIALS cred;    \r
-    char service[40];\r
-    char instance[40];\r
-    //    char addr[40];\r
-    char realm[40];\r
-    struct in_addr LocAddr;\r
-    int k_errno;\r
-\r
-    if (pkrb_get_cred == NULL)\r
-        return(KSUCCESS);\r
-\r
-    k_errno = (*pkrb_get_cred)(service,instance,realm,&cred);\r
-    if (k_errno)\r
-        return KRBERR(k_errno);\r
-\r
-    while(1) {\r
-        ipAddr = (*pLocalHostAddr)();\r
-        LocAddr.s_addr = ipAddr;\r
-        StringCbCopyA(loc_addr, sizeof(loc_addr), inet_ntoa(LocAddr));\r
-        if ( strcmp(cred.address, loc_addr) != 0) {\r
-            /* TODO: do something about this */\r
-            //Leash_kdestroy ();\r
-            break;\r
-        }\r
-        break;\r
-    } // while()\r
-    return 0;\r
-} \r
-#endif\r
-\r
-/* we use these structures to keep track of identities that we find\r
-   while going through the API, FILE and MSLSA caches and enumerating\r
-   credentials.  The only identities we want to keep track of are the\r
-   ones that have an initial ticket.  We collect information for each\r
-   of the identities we find that we have initial tickets for and\r
-   then set the properties for the identities at once. */\r
-\r
-typedef struct tag_ident_data {\r
-    khm_handle  ident;          /* handle to the identity */\r
-    khm_int32 count;            /* number of initial tickets we have\r
-                                   found for this identity. */\r
-    wchar_t   ccname[MAX_PATH];\r
-    FILETIME  ft_issue;\r
-    FILETIME  ft_expire;\r
-    FILETIME  ft_renewexpire;\r
-    khm_int32 krb5_flags;\r
-} ident_data;\r
-\r
-typedef struct tag_identlist {\r
-    ident_data * list;\r
-    khm_size     n_list;\r
-    khm_size     nc_list;\r
-} identlist;\r
-\r
-#define IDLIST_ALLOC_INCR 8\r
-\r
-static void\r
-tc_prep_idlist(identlist * idlist) {\r
-    khm_int32 rv;\r
-    khm_size cb_ids = 0;\r
-    khm_size n_ids = 0;\r
-    khm_size i;\r
-    wchar_t * ids = NULL;\r
-    wchar_t *thisid;\r
-\r
-    idlist->list = NULL;\r
-    idlist->n_list = 0;\r
-    idlist->nc_list = 0;\r
-\r
-    do {\r
-\r
-        if (ids) {\r
-            PFREE(ids);\r
-            ids = NULL;\r
-        }\r
-\r
-        rv = kcdb_identity_enum(KCDB_IDENT_FLAG_ACTIVE,\r
-                                KCDB_IDENT_FLAG_ACTIVE,\r
-                                NULL,\r
-                                &cb_ids,\r
-                                &n_ids);\r
-\r
-        if (rv != KHM_ERROR_TOO_LONG)\r
-            break;              /* something else is wrong */\r
-\r
-        if (n_ids == 0 || cb_ids == 0)\r
-            break;              /* no identities to process */\r
-\r
-#ifdef DEBUG\r
-        assert(cb_ids > 0);\r
-#endif\r
-\r
-        ids = PMALLOC(cb_ids);\r
-#ifdef DEBUG\r
-        assert(ids != NULL);\r
-#endif\r
-        if (ids == NULL)\r
-            break;\r
-\r
-        rv = kcdb_identity_enum(KCDB_IDENT_FLAG_ACTIVE,\r
-                                KCDB_IDENT_FLAG_ACTIVE,\r
-                                ids,\r
-                                &cb_ids,\r
-                                &n_ids);\r
-\r
-        if (KHM_SUCCEEDED(rv))\r
-            break;\r
-\r
-    } while (TRUE);\r
-\r
-    if (ids == NULL)\r
-        return;\r
-\r
-    if (KHM_FAILED(rv) || n_ids == 0) {\r
-        if (ids)\r
-            PFREE(ids);\r
-        return;\r
-    }\r
-\r
-    idlist->nc_list = UBOUNDSS(n_ids, IDLIST_ALLOC_INCR, IDLIST_ALLOC_INCR);\r
-\r
-    idlist->list = PCALLOC(idlist->nc_list, sizeof(idlist->list[0]));\r
-\r
-    for (i = 0, thisid = ids;\r
-         thisid && thisid[0];\r
-         thisid = multi_string_next(thisid)) {\r
-\r
-        khm_handle ident;\r
-\r
-        rv = kcdb_identity_create(thisid, 0, &ident);\r
-\r
-        if (KHM_FAILED(rv))\r
-            continue;\r
-\r
-        idlist->list[i].ident = ident;\r
-        idlist->list[i].count = 0;\r
-\r
-        i++;\r
-    }\r
-\r
-    idlist->n_list = i;\r
-\r
-    PFREE(ids);\r
-}\r
-\r
-static ident_data *\r
-tc_add_ident_to_list(identlist * idlist, khm_handle ident) {\r
-    khm_size i;\r
-    ident_data * d;\r
-\r
-    for (i=0; i < idlist->n_list; i++) {\r
-        if (kcdb_identity_is_equal(ident, idlist->list[i].ident))\r
-            break;\r
-    }\r
-\r
-    if (i < idlist->n_list) {\r
-        /* we already have this identity on our list.  Increment the\r
-           count */\r
-        idlist->list[i].count++;\r
-        return &idlist->list[i];\r
-    }\r
-\r
-    /* it wasn't in our list.  Add it */\r
-\r
-    if (idlist->n_list + 1 > idlist->nc_list) {\r
-        idlist->nc_list = UBOUNDSS(idlist->n_list + 1,\r
-                                   IDLIST_ALLOC_INCR,\r
-                                   IDLIST_ALLOC_INCR);\r
-#ifdef DEBUG\r
-        assert(idlist->n_list + 1 <= idlist->nc_list);\r
-#endif\r
-        idlist->list = PREALLOC(idlist->list,\r
-                                sizeof(idlist->list[0]) * idlist->nc_list);\r
-#ifdef DEBUG\r
-        assert(idlist->list);\r
-#endif\r
-        ZeroMemory(&idlist->list[idlist->n_list],\r
-                   sizeof(idlist->list[0]) *\r
-                   (idlist->nc_list - idlist->n_list));\r
-    }\r
-\r
-    d = &idlist->list[idlist->n_list];\r
-\r
-    ZeroMemory(d, sizeof(*d));\r
-\r
-    d->ident = ident;\r
-    d->count = 1;\r
-\r
-    idlist->n_list++;\r
-\r
-    kcdb_identity_hold(ident);\r
-\r
-    return d;\r
-}\r
-\r
-static void\r
-tc_set_ident_data(identlist * idlist) {\r
-    khm_size i;\r
-    wchar_t k5idtype[KCDB_MAXCCH_NAME];\r
-\r
-    k5idtype[0] = L'\0';\r
-    LoadString(hResModule, IDS_KRB5_NC_NAME,\r
-               k5idtype, ARRAYLENGTH(k5idtype));\r
-\r
-    for (i=0; i < idlist->n_list; i++) {\r
-#ifdef DEBUG\r
-        assert(idlist->list[i].ident);\r
-#endif\r
-\r
-        if (idlist->list[i].count > 0) {\r
-            khm_int32 t;\r
-\r
-            t = credtype_id_krb5;\r
-            kcdb_identity_set_attr(idlist->list[i].ident,\r
-                                   KCDB_ATTR_TYPE,\r
-                                   &t,\r
-                                   sizeof(t));\r
-\r
-            /* We need to manually add the type name if we want the\r
-               name to show up in the property list for the identity.\r
-               The type name is only automatically calculated for\r
-               credentials. */\r
-            kcdb_identity_set_attr(idlist->list[i].ident,\r
-                                   KCDB_ATTR_TYPE_NAME,\r
-                                   k5idtype,\r
-                                   KCDB_CBSIZE_AUTO);\r
-\r
-            kcdb_identity_set_attr(idlist->list[i].ident,\r
-                                   attr_id_krb5_ccname,\r
-                                   idlist->list[i].ccname,\r
-                                   KCDB_CBSIZE_AUTO);\r
-\r
-            kcdb_identity_set_attr(idlist->list[i].ident,\r
-                                   KCDB_ATTR_EXPIRE,\r
-                                   &idlist->list[i].ft_expire,\r
-                                   sizeof(idlist->list[i].ft_expire));\r
-\r
-            kcdb_identity_set_attr(idlist->list[i].ident,\r
-                                   KCDB_ATTR_ISSUE,\r
-                                   &idlist->list[i].ft_issue,\r
-                                   sizeof(idlist->list[i].ft_issue));\r
-\r
-            kcdb_identity_set_attr(idlist->list[i].ident,\r
-                                   attr_id_krb5_flags,\r
-                                   &idlist->list[i].krb5_flags,\r
-                                   sizeof(idlist->list[i].krb5_flags));\r
-\r
-            if (idlist->list[i].ft_renewexpire.dwLowDateTime == 0 &&\r
-                idlist->list[i].ft_renewexpire.dwHighDateTime == 0) {\r
-                kcdb_identity_set_attr(idlist->list[i].ident,\r
-                                       KCDB_ATTR_RENEW_EXPIRE,\r
-                                       NULL, 0);\r
-            } else {\r
-                kcdb_identity_set_attr(idlist->list[i].ident,\r
-                                       KCDB_ATTR_RENEW_EXPIRE,\r
-                                       &idlist->list[i].ft_renewexpire,\r
-                                       sizeof(idlist->list[i].ft_renewexpire));\r
-            }\r
-\r
-        } else {\r
-            /* We didn't see any TGTs for this identity.  We have to\r
-               remove all the Krb5 supplied properties. */\r
-\r
-            khm_int32 t;\r
-            khm_size cb;\r
-\r
-            cb = sizeof(t);\r
-            if (KHM_SUCCEEDED(kcdb_identity_get_attr(idlist->list[i].ident,\r
-                                                     KCDB_ATTR_TYPE, NULL,\r
-                                                     &t,\r
-                                                     &cb)) &&\r
-                t == credtype_id_krb5) {\r
-\r
-                /* disown this and remove all our properties. the\r
-                   system will GC this identity if nobody claims it.*/\r
-\r
-                kcdb_identity_set_attr(idlist->list[i].ident,\r
-                                       KCDB_ATTR_TYPE, NULL, 0);\r
-                kcdb_identity_set_attr(idlist->list[i].ident,\r
-                                       KCDB_ATTR_TYPE_NAME, NULL, 0);\r
-                kcdb_identity_set_attr(idlist->list[i].ident,\r
-                                       attr_id_krb5_ccname, NULL, 0);\r
-                kcdb_identity_set_attr(idlist->list[i].ident,\r
-                                       KCDB_ATTR_EXPIRE, NULL, 0);\r
-                kcdb_identity_set_attr(idlist->list[i].ident,\r
-                                       KCDB_ATTR_ISSUE, NULL, 0);\r
-                kcdb_identity_set_attr(idlist->list[i].ident,\r
-                                       attr_id_krb5_flags, NULL, 0);\r
-                kcdb_identity_set_attr(idlist->list[i].ident,\r
-                                       KCDB_ATTR_RENEW_EXPIRE, NULL, 0);\r
-            } else {\r
-                /* otherwise, this identity doesn't belong to us.  We\r
-                   should leave it as is. */\r
-            }\r
-        }\r
-    }\r
-}\r
-\r
-static void\r
-tc_free_idlist(identlist * idlist) {\r
-    khm_size i;\r
-\r
-    for (i=0; i < idlist->n_list; i++) {\r
-        if (idlist->list[i].ident != NULL) {\r
-            kcdb_identity_release(idlist->list[i].ident);\r
-            idlist->list[i].ident = NULL;\r
-        }\r
-    }\r
-\r
-    if (idlist->list)\r
-        PFREE(idlist->list);\r
-    idlist->list = NULL;\r
-    idlist->n_list = 0;\r
-    idlist->nc_list = 0;\r
-}\r
-\r
-#ifndef ENCTYPE_LOCAL_RC4_MD4\r
-#define ENCTYPE_LOCAL_RC4_MD4    0xFFFFFF80\r
-#endif\r
-\r
-#define MAX_ADDRS 256\r
-\r
-static long get_tickets_from_cache(krb5_context ctx, \r
-                                   krb5_ccache cache,\r
-                                   identlist * idlist)\r
-{\r
-    krb5_error_code code;\r
-    krb5_principal  KRBv5Principal;\r
-    krb5_flags     flags = 0;\r
-    krb5_cc_cursor  KRBv5Cursor;\r
-    krb5_creds     KRBv5Credentials;\r
-    krb5_ticket    *tkt=NULL;\r
-    char          *ClientName = NULL;\r
-    char          *PrincipalName = NULL;\r
-    wchar_t         wbuf[256];  /* temporary conversion buffer */\r
-    wchar_t         wcc_name[KRB5_MAXCCH_CCNAME]; /* credential cache name */\r
-    char          *sServerName = NULL;\r
-    khm_handle      ident = NULL;\r
-    khm_handle      cred = NULL;\r
-    time_t          tt;\r
-    FILETIME        ft, eft;\r
-    khm_int32       ti;\r
-\r
-#ifdef KRB5_TC_NOTICKET\r
-    flags = KRB5_TC_NOTICKET;\r
-#else\r
-    flags = 0;\r
-#endif\r
-\r
-    {\r
-        const char * cc_name;\r
-        const char * cc_type;\r
-\r
-        cc_name = (*pkrb5_cc_get_name)(ctx, cache);\r
-        if(cc_name) {\r
-            cc_type = (*pkrb5_cc_get_type)(ctx, cache);\r
-            if (cc_type) {\r
-                StringCbPrintf(wcc_name, sizeof(wcc_name), L"%S:%S", cc_type, cc_name);\r
-            } else {\r
-                AnsiStrToUnicode(wcc_name, sizeof(wcc_name), cc_name);\r
-                khm_krb5_canon_cc_name(wcc_name, sizeof(wcc_name));\r
-            }\r
-        } else {\r
-            cc_type = (*pkrb5_cc_get_type)(ctx, cache);\r
-            if (cc_type) {\r
-                StringCbPrintf(wcc_name, sizeof(wcc_name), L"%S:", cc_type);\r
-            } else {\r
-#ifdef DEBUG\r
-                assert(FALSE);\r
-#endif\r
-                StringCbCopy(wcc_name, sizeof(wcc_name), L"");\r
-            }\r
-        }\r
-    }\r
-\r
-    _reportf(L"Getting tickets from cache [%s]", wcc_name);\r
-\r
-    if ((code = (*pkrb5_cc_set_flags)(ctx, cache, flags)))\r
-    {\r
-        if (code != KRB5_FCC_NOFILE && code != KRB5_CC_NOTFOUND)\r
-            khm_krb5_error(code, "krb5_cc_set_flags()", 0, &ctx, &cache);\r
-\r
-        goto _exit;\r
-    }\r
-\r
-    if ((code = (*pkrb5_cc_get_principal)(ctx, cache, &KRBv5Principal)))\r
-    {\r
-        if (code != KRB5_FCC_NOFILE && code != KRB5_CC_NOTFOUND)\r
-            khm_krb5_error(code, "krb5_cc_get_principal()", 0, &ctx, &cache);\r
-\r
-        goto _exit;\r
-    }\r
-\r
-    PrincipalName = NULL;\r
-    ClientName = NULL;\r
-    sServerName = NULL;\r
-    if ((code = (*pkrb5_unparse_name)(ctx, KRBv5Principal, \r
-        (char **)&PrincipalName))) \r
-    {\r
-        if (PrincipalName != NULL)\r
-            (*pkrb5_free_unparsed_name)(ctx, PrincipalName);\r
-\r
-        (*pkrb5_free_principal)(ctx, KRBv5Principal);\r
-\r
-        goto _exit;\r
-    }\r
-\r
-    if (!strcspn(PrincipalName, "@" ))\r
-    {\r
-        if (PrincipalName != NULL)\r
-            (*pkrb5_free_unparsed_name)(ctx, PrincipalName);\r
-\r
-        (*pkrb5_free_principal)(ctx, KRBv5Principal);\r
-\r
-        goto _exit;\r
-    }\r
-\r
-    AnsiStrToUnicode(wbuf, sizeof(wbuf), PrincipalName);\r
-    if(KHM_FAILED(kcdb_identity_create(wbuf, KCDB_IDENT_FLAG_CREATE, \r
-                                       &ident))) {\r
-        /* something bad happened */\r
-        code = 1;\r
-        goto _exit;\r
-    }\r
-\r
-    _reportf(L"Found principal [%s]", wbuf);\r
-\r
-    (*pkrb5_free_principal)(ctx, KRBv5Principal);\r
-\r
-    if ((code = (*pkrb5_cc_start_seq_get)(ctx, cache, &KRBv5Cursor))) \r
-    {\r
-        goto _exit; \r
-    }\r
-\r
-    memset(&KRBv5Credentials, '\0', sizeof(KRBv5Credentials));\r
-\r
-    ClientName = NULL;\r
-    sServerName = NULL;\r
-    cred = NULL;\r
-\r
-    while (!(code = pkrb5_cc_next_cred(ctx, cache, &KRBv5Cursor, \r
-                                       &KRBv5Credentials))) \r
-    {\r
-        khm_handle tident = NULL;\r
-        khm_int32 cred_flags = 0;\r
-\r
-        if(ClientName != NULL)\r
-            (*pkrb5_free_unparsed_name)(ctx, ClientName);\r
-        if(sServerName != NULL)\r
-            (*pkrb5_free_unparsed_name)(ctx, sServerName);\r
-        if(cred)\r
-            kcdb_cred_release(cred);\r
-\r
-        ClientName = NULL;\r
-        sServerName = NULL;\r
-        cred = NULL;\r
-\r
-        if ((*pkrb5_unparse_name)(ctx, KRBv5Credentials.client, &ClientName))\r
-        {\r
-            (*pkrb5_free_cred_contents)(ctx, &KRBv5Credentials);\r
-            khm_krb5_error(code, "krb5_free_cred_contents()", 0, &ctx, &cache);\r
-            continue;\r
-        }\r
-\r
-        if ((*pkrb5_unparse_name)(ctx, KRBv5Credentials.server, &sServerName))\r
-        {\r
-            (*pkrb5_free_cred_contents)(ctx, &KRBv5Credentials);\r
-            khm_krb5_error(code, "krb5_free_cred_contents()", 0, &ctx, &cache);\r
-            continue;\r
-        }\r
-\r
-        /* if the ClientName differs from PrincipalName for some\r
-           reason, we need to create a new identity */\r
-        if(strcmp(ClientName, PrincipalName)) {\r
-            AnsiStrToUnicode(wbuf, sizeof(wbuf), ClientName);\r
-            if(KHM_FAILED(kcdb_identity_create(wbuf, KCDB_IDENT_FLAG_CREATE, \r
-                                               &tident))) {\r
-                (*pkrb5_free_cred_contents)(ctx, &KRBv5Credentials);\r
-                continue;\r
-            }\r
-        } else {\r
-            tident = ident;\r
-        }\r
-\r
-        AnsiStrToUnicode(wbuf, sizeof(wbuf), sServerName);\r
-        if(KHM_FAILED(kcdb_cred_create(wbuf, tident, credtype_id_krb5, \r
-                                       &cred))) {\r
-            (*pkrb5_free_cred_contents)(ctx, &KRBv5Credentials);\r
-            continue;\r
-        }\r
-\r
-        if (!KRBv5Credentials.times.starttime)\r
-            KRBv5Credentials.times.starttime = KRBv5Credentials.times.authtime;\r
-\r
-        tt = KRBv5Credentials.times.starttime;\r
-        TimetToFileTime(tt, &ft);\r
-        kcdb_cred_set_attr(cred, KCDB_ATTR_ISSUE, &ft, sizeof(ft));\r
-\r
-        tt = KRBv5Credentials.times.endtime;\r
-        TimetToFileTime(tt, &eft);\r
-        kcdb_cred_set_attr(cred, KCDB_ATTR_EXPIRE, &eft, sizeof(eft));\r
-\r
-        {\r
-            FILETIME ftl;\r
-\r
-            ftl = FtSub(&eft, &ft);\r
-            kcdb_cred_set_attr(cred, KCDB_ATTR_LIFETIME, &ftl, sizeof(ftl));\r
-        }\r
-\r
-        if (KRBv5Credentials.times.renew_till > 0) {\r
-            FILETIME ftl;\r
-\r
-            tt = KRBv5Credentials.times.renew_till;\r
-            TimetToFileTime(tt, &eft);\r
-            kcdb_cred_set_attr(cred, KCDB_ATTR_RENEW_EXPIRE, &eft, \r
-                               sizeof(eft));\r
-\r
-\r
-            ftl = FtSub(&eft, &ft);\r
-            kcdb_cred_set_attr(cred, KCDB_ATTR_RENEW_LIFETIME, &ftl, \r
-                               sizeof(ftl));\r
-        }\r
-\r
-        ti = KRBv5Credentials.ticket_flags;\r
-        kcdb_cred_set_attr(cred, attr_id_krb5_flags, &ti, sizeof(ti));\r
-\r
-        /* special flags understood by NetIDMgr */\r
-        {\r
-            khm_int32 nflags = 0;\r
-\r
-            if (ti & TKT_FLG_RENEWABLE)\r
-                nflags |= KCDB_CRED_FLAG_RENEWABLE;\r
-            if (ti & TKT_FLG_INITIAL)\r
-                nflags |= KCDB_CRED_FLAG_INITIAL;\r
-           else {\r
-               krb5_data * c0, *c1, *r;\r
-\r
-               /* these are macros that do not allocate any memory */\r
-               c0 = krb5_princ_component(ctx,KRBv5Credentials.server,0);\r
-               c1 = krb5_princ_component(ctx,KRBv5Credentials.server,1);\r
-               r  = krb5_princ_realm(ctx,KRBv5Credentials.server);\r
-\r
-               if ( c0 && c1 && r && c1->length == r->length && \r
-                    !strncmp(c1->data,r->data,r->length) &&\r
-                    !strncmp("krbtgt",c0->data,c0->length) )\r
-                   nflags |= KCDB_CRED_FLAG_INITIAL;\r
-           }\r
-\r
-            kcdb_cred_set_flags(cred, nflags, KCDB_CRED_FLAGMASK_EXT);\r
-\r
-            cred_flags = nflags;\r
-        }\r
-\r
-        if ( !pkrb5_decode_ticket(&KRBv5Credentials.ticket, &tkt)) {\r
-            ti = tkt->enc_part.enctype;\r
-            kcdb_cred_set_attr(cred, attr_id_tkt_enctype, &ti, sizeof(ti));\r
-            ti = tkt->enc_part.kvno;\r
-            kcdb_cred_set_attr(cred, attr_id_kvno, &ti, sizeof(ti));\r
-            pkrb5_free_ticket(ctx, tkt);\r
-            tkt = NULL;\r
-        }\r
-\r
-        ti = KRBv5Credentials.keyblock.enctype;\r
-        kcdb_cred_set_attr(cred, attr_id_key_enctype, &ti, sizeof(ti));\r
-\r
-        kcdb_cred_set_attr(cred, KCDB_ATTR_LOCATION, wcc_name, \r
-                           KCDB_CBSIZE_AUTO);\r
-\r
-        if ( KRBv5Credentials.addresses && KRBv5Credentials.addresses[0] ) {\r
-           khm_int32 buffer[1024];\r
-           void * bufp;\r
-           khm_size cb;\r
-           khm_int32 rv;\r
-\r
-           bufp = (void *) buffer;\r
-           cb = sizeof(buffer);\r
-\r
-           rv = serialize_krb5_addresses(KRBv5Credentials.addresses,\r
-                                         bufp,\r
-                                         &cb);\r
-           if (rv == KHM_ERROR_TOO_LONG) {\r
-               bufp = PMALLOC(cb);\r
-               rv = serialize_krb5_addresses(KRBv5Credentials.addresses,\r
-                                             bufp,\r
-                                             &cb);\r
-           }\r
-\r
-           if (KHM_SUCCEEDED(rv)) {\r
-               kcdb_cred_set_attr(cred, attr_id_addr_list,\r
-                                  bufp, cb);\r
-           }\r
-\r
-           if (bufp != (void *) buffer)\r
-               PFREE(bufp);\r
-        }\r
-\r
-        if(cred_flags & KCDB_CRED_FLAG_INITIAL) {\r
-            FILETIME ft_issue_new;\r
-            FILETIME ft_expire_old;\r
-            FILETIME ft_expire_new;\r
-            ident_data * d;\r
-\r
-            /* an initial ticket!  Add it to the list of identities we\r
-               have seen so far with initial tickets. */\r
-            d = tc_add_ident_to_list(idlist, ident);\r
-#ifdef DEBUG\r
-            assert(d);\r
-            assert(d->count > 0);\r
-#endif\r
-\r
-            tt = KRBv5Credentials.times.endtime;\r
-            TimetToFileTime(tt, &ft_expire_new);\r
-\r
-            tt = KRBv5Credentials.times.starttime;\r
-            TimetToFileTime(tt, &ft_issue_new);\r
-\r
-            /* so now, we have to set the properties of the identity\r
-               based on the properties of this credential under the\r
-               following circumstances:\r
-\r
-               - If this is the first time we are hitting this\r
-                 identity.\r
-\r
-               - If this is not the MSLSA: cache and the expiry time\r
-                 for this credential is longer than the time already\r
-                 found for this identity.\r
-            */\r
-\r
-            ft_expire_old = d->ft_expire;\r
-\r
-            if(d->count == 1\r
-               || (CompareFileTime(&ft_expire_new, &ft_expire_old) > 0 &&\r
-                   wcscmp(wcc_name, L"MSLSA:") != 0)) {\r
-\r
-                _reportf(L"Setting properties for identity (count=%d)", d->count);\r
-\r
-                StringCbCopy(d->ccname, sizeof(d->ccname),\r
-                             wcc_name);\r
-                d->ft_expire = ft_expire_new;\r
-                d->ft_issue = ft_issue_new;\r
-\r
-                if (KRBv5Credentials.times.renew_till > 0) {\r
-                    tt = KRBv5Credentials.times.renew_till;\r
-                    TimetToFileTime(tt, &ft);\r
-                    d->ft_renewexpire = ft;\r
-                } else {\r
-                    ZeroMemory(&d->ft_renewexpire, sizeof(d->ft_renewexpire));\r
-                }\r
-\r
-                d->krb5_flags = KRBv5Credentials.ticket_flags;\r
-            }\r
-        }\r
-\r
-        kcdb_credset_add_cred(krb5_credset, cred, -1);\r
-\r
-        (*pkrb5_free_cred_contents)(ctx, &KRBv5Credentials);\r
-\r
-        if(tident != ident)\r
-            kcdb_identity_release(tident);\r
-    }\r
-\r
-    if (PrincipalName != NULL)\r
-        (*pkrb5_free_unparsed_name)(ctx, PrincipalName);\r
-\r
-    if (ClientName != NULL)\r
-        (*pkrb5_free_unparsed_name)(ctx, ClientName);\r
-\r
-    if (sServerName != NULL)\r
-        (*pkrb5_free_unparsed_name)(ctx, sServerName);\r
-\r
-    if (cred)\r
-        kcdb_cred_release(cred);\r
-\r
-    if ((code == KRB5_CC_END) || (code == KRB5_CC_NOTFOUND))\r
-    {\r
-        if ((code = pkrb5_cc_end_seq_get(ctx, cache, &KRBv5Cursor))) \r
-        {\r
-            goto _exit;\r
-        }\r
-\r
-        flags = KRB5_TC_OPENCLOSE;\r
-#ifdef KRB5_TC_NOTICKET\r
-        flags |= KRB5_TC_NOTICKET;\r
-#endif\r
-        if ((code = pkrb5_cc_set_flags(ctx, cache, flags))) \r
-        {\r
-            goto _exit;\r
-        }\r
-    }\r
-    else \r
-    {\r
-        goto _exit;\r
-    }\r
-\r
-_exit:\r
-\r
-    return code;\r
-}\r
-\r
-long\r
-khm_krb5_list_tickets(krb5_context *krbv5Context)\r
-{\r
-    krb5_context       ctx = NULL;\r
-    krb5_ccache                cache = NULL;\r
-    krb5_error_code    code = 0;\r
-    apiCB *             cc_ctx = NULL;\r
-    struct _infoNC **   pNCi = NULL;\r
-    int                 i;\r
-    khm_int32           t;\r
-    wchar_t *           ms = NULL;\r
-    khm_size            cb;\r
-    identlist           idl;\r
-\r
-    kcdb_credset_flush(krb5_credset);\r
-    tc_prep_idlist(&idl);\r
-\r
-    if((*krbv5Context == 0) && (code = (*pkrb5_init_context)(krbv5Context))) {\r
-        goto _exit;\r
-    }\r
-\r
-    ctx = (*krbv5Context);\r
-\r
-    if (!pcc_initialize ||\r
-        !pcc_get_NC_info ||\r
-        !pcc_free_NC_info ||\r
-        !pcc_shutdown)\r
-        goto _skip_cc_iter;\r
-\r
-    code = pcc_initialize(&cc_ctx, CC_API_VER_2, NULL, NULL);\r
-    if (code)\r
-        goto _exit;\r
-\r
-    code = pcc_get_NC_info(cc_ctx, &pNCi);\r
-    if (code) \r
-        goto _exit;\r
-\r
-    for(i=0; pNCi[i]; i++) {\r
-        char ccname[KRB5_MAXCCH_CCNAME];\r
-\r
-        if (pNCi[i]->vers != CC_CRED_V5)\r
-            continue;\r
-\r
-        if (FAILED(StringCchPrintfA(ccname, sizeof(ccname), "API:%s",\r
-                                    pNCi[i]->name)))\r
-            continue;\r
-\r
-        code = (*pkrb5_cc_resolve)(ctx, ccname, &cache);\r
-\r
-        if (code)\r
-            continue;\r
-\r
-        code = get_tickets_from_cache(ctx, cache, &idl);\r
-\r
-        if(ctx != NULL && cache != NULL)\r
-            (*pkrb5_cc_close)(ctx, cache);\r
-\r
-        cache = 0;\r
-    }\r
-\r
- _skip_cc_iter:\r
-\r
-    if (khc_read_multi_string(csp_params, L"FileCCList", NULL, &cb)\r
-        == KHM_ERROR_TOO_LONG &&\r
-        cb > sizeof(wchar_t) * 2) {\r
-        wchar_t * t;\r
-        char ccname[MAX_PATH + 6];\r
-\r
-        ms = PMALLOC(cb);\r
-#ifdef DEBUG\r
-        assert(ms);\r
-#endif\r
-        khc_read_multi_string(csp_params, L"FileCCList", ms, &cb);\r
-\r
-        for(t = ms; t && *t; t = multi_string_next(t)) {\r
-            StringCchPrintfA(ccname, ARRAYLENGTH(ccname),\r
-                             "FILE:%S", t);\r
-\r
-            code = (*pkrb5_cc_resolve)(ctx, ccname, &cache);\r
-\r
-            if (code)\r
-                continue;\r
-\r
-            code = get_tickets_from_cache(ctx, cache, &idl);\r
-\r
-            if (ctx != NULL && cache != NULL)\r
-                (*pkrb5_cc_close)(ctx, cache);\r
-            cache = 0;\r
-        }\r
-\r
-        PFREE(ms);\r
-    }\r
-\r
-    if (KHM_SUCCEEDED(khc_read_int32(csp_params, L"MsLsaList", &t)) && t) {\r
-        code = (*pkrb5_cc_resolve)(ctx, "MSLSA:", &cache);\r
-\r
-        if (code == 0 && cache) {\r
-            code = get_tickets_from_cache(ctx, cache, &idl);\r
-        }\r
-\r
-        if (ctx != NULL && cache != NULL)\r
-            (*pkrb5_cc_close)(ctx, cache);\r
-        cache = 0;\r
-    }\r
-\r
-_exit:\r
-    if (pNCi)\r
-        (*pcc_free_NC_info)(cc_ctx, &pNCi);\r
-    if (cc_ctx)\r
-        (*pcc_shutdown)(&cc_ctx);\r
-\r
-    kcdb_credset_collect(NULL, krb5_credset, NULL, credtype_id_krb5, NULL);\r
-    tc_set_ident_data(&idl);\r
-    tc_free_idlist(&idl);\r
-\r
-    return(code);\r
-}\r
-\r
-int\r
-khm_krb5_renew_cred(khm_handle cred)\r
-{\r
-    khm_handle          identity = NULL;\r
-    krb5_error_code     code = 0;\r
-    krb5_context        ctx = NULL;\r
-    krb5_ccache         cc = NULL;\r
-    krb5_principal     me = NULL, server = NULL;\r
-    krb5_creds          in_creds, cc_creds;\r
-    krb5_creds         * out_creds = NULL;\r
-\r
-    wchar_t            wname[512];\r
-    khm_size           cbname;\r
-    char                name[512];\r
-    khm_boolean                brenewIdentity = FALSE;\r
-    khm_boolean                istgt = FALSE;\r
-\r
-    khm_int32           flags;\r
-\r
-    cbname = sizeof(wname);\r
-    kcdb_cred_get_name(cred, wname, &cbname);\r
-    _reportf(L"Krb5 renew cred for %s", wname);\r
-\r
-    kcdb_cred_get_flags(cred, &flags);\r
-\r
-    if (!(flags & KCDB_CRED_FLAG_INITIAL)) {\r
-        _reportf(L"Krb5 skipping renewal because this is not an initial credential");\r
-        return 0;\r
-    }\r
-\r
-    memset(&in_creds, 0, sizeof(in_creds));\r
-    memset(&cc_creds, 0, sizeof(cc_creds));\r
-\r
-    if (cred == NULL) {\r
-#ifdef DEBUG\r
-       assert(FALSE);\r
-#endif\r
-       goto cleanup;\r
-    }\r
-\r
-    if (KHM_FAILED(kcdb_cred_get_identity(cred, &identity))) {\r
-#ifdef DEBUG\r
-       assert(FALSE);\r
-#endif\r
-       goto cleanup;\r
-    }\r
-\r
-    code = khm_krb5_initialize(identity, &ctx, &cc);\r
-    if (code)\r
-       goto cleanup;\r
-\r
-    code = pkrb5_cc_get_principal(ctx, cc, &me);\r
-    if (code) \r
-        goto cleanup;\r
-\r
-    cbname = sizeof(wname);\r
-    if (KHM_FAILED(kcdb_cred_get_name(cred, wname, &cbname)))\r
-       goto cleanup;\r
-\r
-    UnicodeStrToAnsi(name, sizeof(name), wname);\r
-\r
-    code = pkrb5_parse_name(ctx, name, &server);\r
-    if (code)\r
-       goto cleanup;\r
-\r
-    in_creds.client = me;\r
-    in_creds.server = server;\r
-\r
-#ifdef KRB5_TC_NOTICKET\r
-    pkrb5_cc_set_flags(ctx, cc, 0);\r
-#endif\r
-\r
-    if (strlen("krbtgt") != krb5_princ_name(ctx, server)->length ||\r
-        strncmp("krbtgt", krb5_princ_name(ctx, server)->data, krb5_princ_name(ctx, server)->length)) \r
-    {\r
-       code = pkrb5_get_renewed_creds(ctx, &cc_creds, me, cc, name);\r
-       if (code) {\r
-           code = pkrb5_cc_retrieve_cred(ctx, cc, 0, &in_creds, &cc_creds);\r
-           if (code == 0) {\r
-               code = pkrb5_cc_remove_cred(ctx, cc, 0, &cc_creds);\r
-               if (code) {\r
-                   brenewIdentity = TRUE;\r
-                   goto cleanup;\r
-               }\r
-           }\r
-       }\r
-\r
-       code = pkrb5_get_credentials(ctx, 0, cc, &in_creds, &out_creds);\r
-    } else {\r
-       istgt = TRUE;\r
-       code = pkrb5_get_renewed_creds(ctx, &cc_creds, me, cc, NULL);\r
-    }\r
-\r
-#ifdef KRB5_TC_NOTICKET\r
-    pkrb5_cc_set_flags(ctx, cc, KRB5_TC_NOTICKET);\r
-#endif\r
-    if (code) {\r
-       if ( code != KRB5KDC_ERR_ETYPE_NOSUPP ||\r
-            code != KRB5_KDC_UNREACH)\r
-           khm_krb5_error(code, "krb5_get_renewed_creds()", 0, &ctx, &cc);\r
-       goto cleanup;\r
-    }\r
-\r
-    if (istgt) {\r
-       code = pkrb5_cc_initialize(ctx, cc, me);\r
-       if (code) goto cleanup;\r
-\r
-    }\r
-\r
-    code = pkrb5_cc_store_cred(ctx, cc, istgt ? &cc_creds : out_creds);\r
-    if (code) goto cleanup;\r
-\r
-\r
- cleanup:\r
-\r
-    if (in_creds.client == me)\r
-        in_creds.client = NULL;\r
-    if (in_creds.server == server)\r
-        in_creds.server = NULL;\r
-\r
-    if (me)\r
-       pkrb5_free_principal(ctx, me);\r
-\r
-    if (server)\r
-       pkrb5_free_principal(ctx, server);\r
-\r
-    pkrb5_free_cred_contents(ctx, &in_creds);\r
-    pkrb5_free_cred_contents(ctx, &cc_creds);                        \r
-\r
-    if (out_creds)\r
-       pkrb5_free_creds(ctx, out_creds);\r
-\r
-    if (cc && ctx)\r
-       pkrb5_cc_close(ctx, cc);\r
-\r
-    if (ctx)\r
-       pkrb5_free_context(ctx);\r
-\r
-    if (identity) {\r
-       if (brenewIdentity)\r
-           code = khm_krb5_renew_ident(identity);\r
-       kcdb_identity_release(identity);\r
-    }\r
-\r
-    return code;\r
-}\r
-\r
-int\r
-khm_krb5_renew_ident(khm_handle identity)\r
-{\r
-    krb5_error_code     code = 0;\r
-    krb5_context        ctx = NULL;\r
-    krb5_ccache         cc = NULL;\r
-    krb5_principal      me = NULL;\r
-    krb5_principal      server = NULL;\r
-    krb5_creds          my_creds;\r
-    krb5_data           *realm = NULL;\r
-    wchar_t             idname[KCDB_IDENT_MAXCCH_NAME];\r
-    khm_size            cb;\r
-    khm_int32           k5_flags;\r
-\r
-    memset(&my_creds, 0, sizeof(krb5_creds));\r
-\r
-    if ( !pkrb5_init_context )\r
-        goto cleanup;\r
-\r
-    cb = sizeof(idname);\r
-    kcdb_identity_get_name(identity, idname, &cb);\r
-\r
-    if (khm_krb5_get_identity_flags(identity) & K5IDFLAG_IMPORTED) {\r
-#ifndef NO_REIMPORT_MSLSA_CREDS\r
-        /* we are trying to renew the identity that was imported from\r
-           MSLSA: */\r
-        BOOL  imported;\r
-        BOOL retry_import = FALSE;\r
-        char  cidname[KCDB_IDENT_MAXCCH_NAME];\r
-        khm_handle imported_id = NULL;\r
-        khm_size cb;\r
-        FILETIME ft_expire;\r
-        FILETIME ft_now;\r
-        FILETIME ft_threshold;\r
-        krb5_principal princ = NULL;\r
-\r
-        UnicodeStrToAnsi(cidname, sizeof(cidname), idname);\r
-\r
-        imported = khm_krb5_ms2mit(cidname, FALSE, TRUE, &imported_id);\r
-\r
-        if (imported == 0)\r
-            goto import_failed;\r
-\r
-        /* if the imported identity has already expired or will soon,\r
-           we clear the cache and try again. */\r
-        khm_krb5_list_tickets(&ctx);\r
-\r
-        cb = sizeof(ft_expire);\r
-        if (KHM_FAILED(kcdb_identity_get_attr(imported_id, KCDB_ATTR_EXPIRE,\r
-                                              NULL, &ft_expire, &cb)))\r
-            goto import_failed;\r
-\r
-        GetSystemTimeAsFileTime(&ft_now);\r
-        TimetToFileTimeInterval(5 * 60, &ft_threshold);\r
-\r
-        ft_now = FtAdd(&ft_now, &ft_threshold);\r
-\r
-        if (CompareFileTime(&ft_expire, &ft_now) < 0) {\r
-            /* the ticket lifetime is not long enough */\r
-\r
-            code = 0;\r
-\r
-            if (ctx == NULL)\r
-                code = pkrb5_init_context(&ctx);\r
-            if (code)\r
-                goto import_failed;\r
-\r
-            code = pkrb5_cc_resolve(ctx, "MSLSA:", &cc);\r
-            if (code)\r
-                goto import_failed;\r
-\r
-            code = pkrb5_cc_get_principal(ctx, cc, &princ);\r
-            if (code)\r
-                goto import_failed;\r
-\r
-            pkrb5_cc_initialize(ctx, cc, princ);\r
-\r
-            retry_import = TRUE;\r
-        }\r
-\r
-    import_failed:\r
-\r
-        if (imported_id) {\r
-            kcdb_identity_release(imported_id);\r
-            imported_id = NULL;\r
-        }\r
-\r
-        if (ctx) {\r
-            if (cc) {\r
-                pkrb5_cc_close(ctx, cc);\r
-                cc = NULL;\r
-            }\r
-\r
-            if (princ) {\r
-                pkrb5_free_principal(ctx, princ);\r
-                princ = NULL;\r
-            }\r
-\r
-            /* leave ctx so we can use it later */\r
-        }\r
-\r
-        if (retry_import)\r
-            imported = khm_krb5_ms2mit(cidname, FALSE, TRUE, NULL);\r
-\r
-        if (imported)\r
-            goto cleanup;\r
-\r
-        /* if the import failed, then we try to renew the identity via\r
-           the usual procedure. */\r
-\r
-#else\r
-        /* if we are suppressing further imports from MSLSA, we just\r
-           skip renewing this identity. */\r
-        goto cleanup;\r
-#endif\r
-    }\r
-\r
-    cb = sizeof(k5_flags);\r
-    if (KHM_SUCCEEDED(kcdb_identity_get_attr(identity,\r
-                                             attr_id_krb5_flags,\r
-                                             NULL,\r
-                                             &k5_flags,\r
-                                             &cb)) &&\r
-        !(k5_flags & TKT_FLG_RENEWABLE)) {\r
-\r
-        code = KRB5KDC_ERR_BADOPTION;\r
-        goto cleanup;\r
-    }\r
-\r
-    {\r
-        FILETIME ft_now;\r
-        FILETIME ft_exp;\r
-\r
-        cb = sizeof(ft_exp);\r
-        GetSystemTimeAsFileTime(&ft_now);\r
-        if (KHM_SUCCEEDED(kcdb_identity_get_attr(identity,\r
-                                                 KCDB_ATTR_EXPIRE,\r
-                                                 NULL,\r
-                                                 &ft_exp,\r
-                                                 &cb)) &&\r
-            CompareFileTime(&ft_exp, &ft_now) < 0) {\r
-\r
-            code = KRB5KRB_AP_ERR_TKT_EXPIRED;\r
-            goto cleanup;\r
-\r
-        }\r
-    }\r
-\r
-    code = khm_krb5_initialize(identity, &ctx, &cc);\r
-    if (code) \r
-        goto cleanup;\r
-\r
-    code = pkrb5_cc_get_principal(ctx, cc, &me);\r
-    if (code) \r
-        goto cleanup;\r
-\r
-    realm = krb5_princ_realm(ctx, me);\r
-\r
-    code = pkrb5_build_principal_ext(ctx, &server,\r
-                                     realm->length,realm->data,\r
-                                     KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,\r
-                                     realm->length,realm->data,\r
-                                     0);\r
-\r
-    if (code) \r
-        goto cleanup;\r
-\r
-    my_creds.client = me;\r
-    my_creds.server = server;\r
-\r
-#ifdef KRB5_TC_NOTICKET\r
-    pkrb5_cc_set_flags(ctx, cc, 0);\r
-#endif\r
-    code = pkrb5_get_renewed_creds(ctx, &my_creds, me, cc, NULL);\r
-#ifdef KRB5_TC_NOTICKET\r
-    pkrb5_cc_set_flags(ctx, cc, KRB5_TC_NOTICKET);\r
-#endif\r
-    if (code) {\r
-        if ( code != KRB5KDC_ERR_ETYPE_NOSUPP ||\r
-            code != KRB5_KDC_UNREACH)\r
-            khm_krb5_error(code, "krb5_get_renewed_creds()", 0, &ctx, &cc);\r
-        goto cleanup;\r
-    }\r
-\r
-    code = pkrb5_cc_initialize(ctx, cc, me);\r
-    if (code) goto cleanup;\r
-\r
-    code = pkrb5_cc_store_cred(ctx, cc, &my_creds);\r
-    if (code) goto cleanup;\r
-\r
-cleanup:\r
-    if (my_creds.client == me)\r
-        my_creds.client = NULL;\r
-    if (my_creds.server == server)\r
-        my_creds.server = NULL;\r
-\r
-    if (ctx) {\r
-        pkrb5_free_cred_contents(ctx, &my_creds);\r
-\r
-        if (me)\r
-            pkrb5_free_principal(ctx, me);\r
-        if (server)\r
-            pkrb5_free_principal(ctx, server);\r
-        if (cc)\r
-            pkrb5_cc_close(ctx, cc);\r
-        pkrb5_free_context(ctx);\r
-    }\r
-\r
-    return(code);\r
-}\r
-\r
-int\r
-khm_krb5_kinit(krb5_context       alt_ctx,\r
-               char *             principal_name,\r
-               char *             password,\r
-               char *             ccache,\r
-               krb5_deltat        lifetime,\r
-               DWORD              forwardable,\r
-               DWORD              proxiable,\r
-               krb5_deltat        renew_life,\r
-               DWORD              addressless,\r
-               DWORD              publicIP,\r
-               krb5_prompter_fct  prompter,\r
-               void *             p_data)\r
-{\r
-    krb5_error_code                    code = 0;\r
-    krb5_context                       ctx = NULL;\r
-    krb5_ccache                                cc = NULL;\r
-    krb5_principal                     me = NULL;\r
-    char*                       name = NULL;\r
-    krb5_creds                         my_creds;\r
-    krb5_get_init_creds_opt     options;\r
-    krb5_address **             addrs = NULL;\r
-    int                         i = 0, addr_count = 0;\r
-\r
-    if (!pkrb5_init_context)\r
-        return 0;\r
-\r
-    _reportf(L"In khm_krb5_kinit");\r
-\r
-    pkrb5_get_init_creds_opt_init(&options);\r
-    pkrb5_get_init_creds_opt_set_change_password_prompt(&options, 0);\r
-\r
-    memset(&my_creds, 0, sizeof(my_creds));\r
-\r
-    if (alt_ctx) {\r
-        ctx = alt_ctx;\r
-    } else {\r
-        code = pkrb5_init_context(&ctx);\r
-        if (code)\r
-            goto cleanup;\r
-    }\r
-\r
-    if (ccache) {\r
-        _reportf(L"Using supplied ccache name %S", ccache);\r
-        code = pkrb5_cc_resolve(ctx, ccache, &cc);\r
-    } else {\r
-       khm_handle identity = NULL;\r
-       khm_handle csp_ident = NULL;\r
-       khm_handle csp_k5 = NULL;\r
-       wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-       wchar_t wccname[MAX_PATH];\r
-       char ccname[MAX_PATH];\r
-       char * pccname = principal_name;\r
-       khm_size cb;\r
-\r
-       idname[0] = L'\0';\r
-       AnsiStrToUnicode(idname, sizeof(idname), principal_name);\r
-\r
-       cb = sizeof(wccname);\r
-\r
-       if (KHM_SUCCEEDED(kcdb_identity_create(idname, 0, &identity)) &&\r
-\r
-           KHM_SUCCEEDED(kcdb_identity_get_config(identity, 0, &csp_ident)) &&\r
-\r
-           KHM_SUCCEEDED(khc_open_space(csp_ident, CSNAME_KRB5CRED, 0,\r
-                                        &csp_k5)) &&\r
-\r
-           KHM_SUCCEEDED(khc_read_string(csp_k5, L"DefaultCCName",\r
-                                         wccname, &cb)) &&\r
-\r
-           cb > sizeof(wchar_t)) {\r
-\r
-            _reportf(L"Using DefaultCCName [%s] from identity", wccname);\r
-\r
-           UnicodeStrToAnsi(ccname, sizeof(ccname), wccname);\r
-           pccname = ccname;\r
-       }\r
-\r
-       if (csp_k5)\r
-           khc_close_space(csp_k5);\r
-       if (csp_ident)\r
-           khc_close_space(csp_ident);\r
-       if (identity)\r
-           kcdb_identity_release(identity);\r
-\r
-        code = pkrb5_cc_resolve(ctx, pccname, &cc);\r
-    }\r
-\r
-    _reportf(L"krb5_cc_resolve returns code %d", code);\r
-\r
-    if (code) goto cleanup;\r
-\r
-    code = pkrb5_parse_name(ctx, principal_name, &me);\r
-    if (code) goto cleanup;\r
-\r
-    code = pkrb5_unparse_name(ctx, me, &name);\r
-    if (code) goto cleanup;\r
-\r
-    if (lifetime == 0) {\r
-        khc_read_int32(csp_params, L"DefaultLifetime", &lifetime);\r
-    }\r
-\r
-    if (lifetime)\r
-        pkrb5_get_init_creds_opt_set_tkt_life(&options, lifetime);\r
-\r
-    pkrb5_get_init_creds_opt_set_forwardable(&options,\r
-        forwardable ? 1 : 0);\r
-    pkrb5_get_init_creds_opt_set_proxiable(&options,\r
-        proxiable ? 1 : 0);\r
-    pkrb5_get_init_creds_opt_set_renew_life(&options,\r
-        renew_life);\r
-\r
-    if (addressless)\r
-        pkrb5_get_init_creds_opt_set_address_list(&options,NULL);\r
-    else {\r
-       krb5_address ** local_addrs=NULL;\r
-       DWORD           netIPAddr;\r
-\r
-       pkrb5_os_localaddr(ctx, &local_addrs);\r
-       i = 0;\r
-       while ( local_addrs[i++] );\r
-       addr_count = i + 1;\r
-\r
-       addrs = (krb5_address **) PMALLOC((addr_count+1) * sizeof(krb5_address *));\r
-       if ( !addrs ) {\r
-           pkrb5_free_addresses(ctx, local_addrs);\r
-           assert(0);\r
-       }\r
-       memset(addrs, 0, sizeof(krb5_address *) * (addr_count+1));\r
-       i = 0;\r
-       while ( local_addrs[i] ) {\r
-           addrs[i] = (krb5_address *)PMALLOC(sizeof(krb5_address));\r
-           if (addrs[i] == NULL) {\r
-               pkrb5_free_addresses(ctx, local_addrs);\r
-               assert(0);\r
-           }\r
-\r
-           addrs[i]->magic = local_addrs[i]->magic;\r
-           addrs[i]->addrtype = local_addrs[i]->addrtype;\r
-           addrs[i]->length = local_addrs[i]->length;\r
-           addrs[i]->contents = (unsigned char *)PMALLOC(addrs[i]->length);\r
-           if (!addrs[i]->contents) {\r
-               pkrb5_free_addresses(ctx, local_addrs);\r
-               assert(0);\r
-           }\r
-\r
-           memcpy(addrs[i]->contents,local_addrs[i]->contents,\r
-                  local_addrs[i]->length);        /* safe */\r
-           i++;\r
-       }\r
-       pkrb5_free_addresses(ctx, local_addrs);\r
-\r
-        if (publicIP) {\r
-            // we are going to add the public IP address specified by the user\r
-            // to the list provided by the operating system\r
-            addrs[i] = (krb5_address *)PMALLOC(sizeof(krb5_address));\r
-            if (addrs[i] == NULL)\r
-                assert(0);\r
-\r
-            addrs[i]->magic = KV5M_ADDRESS;\r
-            addrs[i]->addrtype = AF_INET;\r
-            addrs[i]->length = 4;\r
-            addrs[i]->contents = (unsigned char *)PMALLOC(addrs[i]->length);\r
-            if (!addrs[i]->contents)\r
-                assert(0);\r
-\r
-            netIPAddr = htonl(publicIP);\r
-            memcpy(addrs[i]->contents,&netIPAddr,4);\r
-        }\r
-\r
-       pkrb5_get_init_creds_opt_set_address_list(&options,addrs);\r
-    }\r
-\r
-    code =\r
-        pkrb5_get_init_creds_password(ctx,\r
-                                      &my_creds,\r
-                                      me,\r
-                                      password, // password\r
-                                      prompter, // prompter\r
-                                      p_data, // prompter data\r
-                                      0, // start time\r
-                                      0, // service name\r
-                                      &options);\r
-    _reportf(L"krb5_get_init_creds_password returns code %d", code);\r
-\r
-    if (code) goto cleanup;\r
-\r
-    code = pkrb5_cc_initialize(ctx, cc, me);\r
-    _reportf(L"krb5_cc_initialize returns code %d", code);\r
-    if (code) goto cleanup;\r
-\r
-    code = pkrb5_cc_store_cred(ctx, cc, &my_creds);\r
-    _reportf(L"krb5_cc_store_cred returns code %d", code);\r
-    if (code) goto cleanup;\r
-\r
-cleanup:\r
-    if ( addrs ) {\r
-        for ( i=0;i<addr_count;i++ ) {\r
-            if ( addrs[i] ) {\r
-                if ( addrs[i]->contents )\r
-                    PFREE(addrs[i]->contents);\r
-                PFREE(addrs[i]);\r
-            }\r
-        }\r
-    }\r
-    if (my_creds.client == me)\r
-        my_creds.client = 0;\r
-    pkrb5_free_cred_contents(ctx, &my_creds);\r
-    if (name)\r
-        pkrb5_free_unparsed_name(ctx, name);\r
-    if (me)\r
-        pkrb5_free_principal(ctx, me);\r
-    if (cc)\r
-        pkrb5_cc_close(ctx, cc);\r
-    if (ctx && (ctx != alt_ctx))\r
-        pkrb5_free_context(ctx);\r
-    return(code);\r
-}\r
-\r
-long\r
-khm_krb5_copy_ccache_by_name(krb5_context in_ctx,\r
-                             wchar_t * wscc_dest,\r
-                             wchar_t * wscc_src) {\r
-    krb5_context ctx = NULL;\r
-    krb5_error_code code = 0;\r
-    khm_boolean free_ctx;\r
-    krb5_ccache cc_src = NULL;\r
-    krb5_ccache cc_dest = NULL;\r
-    krb5_principal princ_src = NULL;\r
-    char scc_dest[KRB5_MAXCCH_CCNAME];\r
-    char scc_src[KRB5_MAXCCH_CCNAME];\r
-    int t;\r
-\r
-    t = UnicodeStrToAnsi(scc_dest, sizeof(scc_dest), wscc_dest);\r
-    if (t == 0)\r
-        return KHM_ERROR_TOO_LONG;\r
-    t = UnicodeStrToAnsi(scc_src, sizeof(scc_src), wscc_src);\r
-    if (t == 0)\r
-        return KHM_ERROR_TOO_LONG;\r
-\r
-    if (in_ctx) {\r
-        ctx = in_ctx;\r
-        free_ctx = FALSE;\r
-    } else {\r
-        code = pkrb5_init_context(&ctx);\r
-        if (code) {\r
-            if (ctx)\r
-                pkrb5_free_context(ctx);\r
-            return code;\r
-        }\r
-        free_ctx = TRUE;\r
-    }\r
-\r
-    code = pkrb5_cc_resolve(ctx, scc_dest, &cc_dest);\r
-    if (code)\r
-        goto _cleanup;\r
-\r
-    code = pkrb5_cc_resolve(ctx, scc_src, &cc_src);\r
-    if (code)\r
-        goto _cleanup;\r
-\r
-    code = pkrb5_cc_get_principal(ctx, cc_src, &princ_src);\r
-    if (code)\r
-        goto _cleanup;\r
-\r
-    code = pkrb5_cc_initialize(ctx, cc_dest, princ_src);\r
-    if (code)\r
-        goto _cleanup;\r
-\r
-    code = pkrb5_cc_copy_creds(ctx, cc_src, cc_dest);\r
-\r
- _cleanup:\r
-    if (princ_src)\r
-        pkrb5_free_principal(ctx, princ_src);\r
-\r
-    if (cc_dest)\r
-        pkrb5_cc_close(ctx, cc_dest);\r
-\r
-    if (cc_src)\r
-        pkrb5_cc_close(ctx, cc_src);\r
-\r
-    if (free_ctx && ctx)\r
-        pkrb5_free_context(ctx);\r
-\r
-    return code;\r
-}\r
-\r
-long\r
-khm_krb5_canon_cc_name(wchar_t * wcc_name,\r
-                       size_t cb_cc_name) {\r
-    size_t cb_len;\r
-    wchar_t * colon;\r
-\r
-    if (FAILED(StringCbLength(wcc_name, \r
-                              cb_cc_name,\r
-                              &cb_len))) {\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#else\r
-        return KHM_ERROR_TOO_LONG;\r
-#endif\r
-    }\r
-\r
-    cb_len += sizeof(wchar_t);\r
-\r
-    colon = wcschr(wcc_name, L':');\r
-\r
-    if (colon) {\r
-        /* if the colon is just 1 character away from the beginning,\r
-           it's a FILE: cc */\r
-        if (colon - wcc_name == 1) {\r
-            if (cb_len + 5 * sizeof(wchar_t) > cb_cc_name)\r
-                return KHM_ERROR_TOO_LONG;\r
-\r
-            memmove(&wcc_name[5], &wcc_name[0], cb_len);\r
-            memmove(&wcc_name[0], L"FILE:", sizeof(wchar_t) * 5);\r
-        }\r
-\r
-        return 0;\r
-    }\r
-\r
-    if (cb_len + 4 * sizeof(wchar_t) > cb_cc_name)\r
-        return KHM_ERROR_TOO_LONG;\r
-\r
-    memmove(&wcc_name[4], &wcc_name[0], cb_len);\r
-    memmove(&wcc_name[0], L"API:", sizeof(wchar_t) * 4);\r
-\r
-    return 0;\r
-}\r
-\r
-int \r
-khm_krb5_cc_name_cmp(const wchar_t * cc_name_1,\r
-                     const wchar_t * cc_name_2) {\r
-    if (!wcsncmp(cc_name_1, L"API:", 4))\r
-        cc_name_1 += 4;\r
-\r
-    if (!wcsncmp(cc_name_2, L"API:", 4))\r
-        cc_name_2 += 4;\r
-\r
-    return wcscmp(cc_name_1, cc_name_2);\r
-}\r
-\r
-static khm_int32 KHMAPI\r
-khmint_location_comp_func(khm_handle cred1,\r
-                          khm_handle cred2,\r
-                          void * rock) {\r
-    return kcdb_creds_comp_attr(cred1, cred2, KCDB_ATTR_LOCATION);\r
-}\r
-\r
-struct khmint_location_check {\r
-    khm_handle credset;\r
-    khm_handle cred;\r
-    wchar_t * ccname;\r
-    khm_boolean success;\r
-};\r
-\r
-static khm_int32 KHMAPI\r
-khmint_find_matching_cred_func(khm_handle cred,\r
-                               void * rock) {\r
-    struct khmint_location_check * lc;\r
-\r
-    lc = (struct khmint_location_check *) rock;\r
-\r
-    if (!kcdb_creds_is_equal(cred, lc->cred))\r
-        return KHM_ERROR_SUCCESS;\r
-    if (kcdb_creds_comp_attr(cred, lc->cred, KCDB_ATTR_LOCATION))\r
-        return KHM_ERROR_SUCCESS;\r
-\r
-    /* found it */\r
-    lc->success = TRUE;\r
-\r
-    /* break the search */\r
-    return !KHM_ERROR_SUCCESS;\r
-}\r
-\r
-static khm_int32 KHMAPI\r
-khmint_location_check_func(khm_handle cred,\r
-                           void * rock) {\r
-    khm_int32 t;\r
-    khm_size cb;\r
-    wchar_t ccname[KRB5_MAXCCH_CCNAME];\r
-    struct khmint_location_check * lc;\r
-\r
-    lc = (struct khmint_location_check *) rock;\r
-\r
-    if (KHM_FAILED(kcdb_cred_get_type(cred, &t)))\r
-        return KHM_ERROR_SUCCESS;\r
-\r
-    if (t != credtype_id_krb5)\r
-        return KHM_ERROR_SUCCESS;\r
-\r
-    cb = sizeof(ccname);\r
-    if (KHM_FAILED(kcdb_cred_get_attr(cred,\r
-                                      KCDB_ATTR_LOCATION,\r
-                                      NULL,\r
-                                      ccname,\r
-                                      &cb)))\r
-        return KHM_ERROR_SUCCESS;\r
-\r
-    if(wcscmp(ccname, lc->ccname))\r
-        return KHM_ERROR_SUCCESS;\r
-\r
-    lc->cred = cred;\r
-\r
-    lc->success = FALSE;\r
-\r
-    kcdb_credset_apply(lc->credset,\r
-                       khmint_find_matching_cred_func,\r
-                       (void *) lc);\r
-\r
-    if (!lc->success)\r
-        return KHM_ERROR_NOT_FOUND;\r
-    else\r
-        return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-static khm_int32 KHMAPI\r
-khmint_delete_location_func(khm_handle cred,\r
-                            void * rock) {\r
-    wchar_t cc_cred[KRB5_MAXCCH_CCNAME];\r
-    struct khmint_location_check * lc;\r
-    khm_size cb;\r
-\r
-    lc = (struct khmint_location_check *) rock;\r
-\r
-    cb = sizeof(cc_cred);\r
-\r
-    if (KHM_FAILED(kcdb_cred_get_attr(cred,\r
-                                      KCDB_ATTR_LOCATION,\r
-                                      NULL,\r
-                                      cc_cred,\r
-                                      &cb)))\r
-        return KHM_ERROR_SUCCESS;\r
-\r
-    if (wcscmp(cc_cred, lc->ccname))\r
-        return KHM_ERROR_SUCCESS;\r
-\r
-    kcdb_credset_del_cred_ref(lc->credset,\r
-                              cred);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-int\r
-khm_krb5_destroy_by_credset(khm_handle p_cs)\r
-{\r
-    khm_handle d_cs = NULL;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    khm_size s, cb;\r
-    krb5_context ctx = NULL;\r
-    krb5_error_code code = 0;\r
-    int i;\r
-    wchar_t ccname[KRB5_MAXCCH_CCNAME];\r
-    struct khmint_location_check lc;\r
-\r
-    rv = kcdb_credset_create(&d_cs);\r
-\r
-    assert(KHM_SUCCEEDED(rv) && d_cs != NULL);\r
-\r
-    kcdb_credset_extract(d_cs, p_cs, NULL, credtype_id_krb5);\r
-\r
-    kcdb_credset_get_size(d_cs, &s);\r
-\r
-    if (s == 0) {\r
-        _reportf(L"No tickets to delete");\r
-\r
-        kcdb_credset_delete(d_cs);\r
-        return 0;\r
-    }\r
-\r
-    code = pkrb5_init_context(&ctx);\r
-    if (code != 0) {\r
-        rv = code;\r
-        goto _cleanup;\r
-    }\r
-\r
-    /* we should synchronize the credential lists before we attempt to\r
-       make any assumptions on the state of the root credset */\r
-    khm_krb5_list_tickets(&ctx);\r
-\r
-    /* so, we need to make a decision about whether to destroy entire\r
-       ccaches or just individual credentials.  Therefore we first\r
-       sort them by ccache. */\r
-    kcdb_credset_sort(d_cs,\r
-                      khmint_location_comp_func,\r
-                      NULL);\r
-\r
-    /* now, for each ccache we encounter, we check if we have all the\r
-       credentials from that ccache in the to-be-deleted list. */\r
-    for (i=0; i < (int) s; i++) {\r
-        khm_handle cred;\r
-\r
-        if (KHM_FAILED(kcdb_credset_get_cred(d_cs,\r
-                                             i,\r
-                                             &cred)))\r
-            continue;\r
-\r
-        cb = sizeof(ccname);\r
-        rv = kcdb_cred_get_attr(cred,\r
-                                KCDB_ATTR_LOCATION,\r
-                                NULL,\r
-                                ccname,\r
-                                &cb);\r
-\r
-#ifdef DEBUG\r
-        assert(KHM_SUCCEEDED(rv));\r
-#endif\r
-        kcdb_cred_release(cred);\r
-\r
-        lc.credset = d_cs;\r
-        lc.cred = NULL;\r
-        lc.ccname = ccname;\r
-        lc.success = FALSE;\r
-\r
-        kcdb_credset_apply(NULL,\r
-                           khmint_location_check_func,\r
-                           (void *) &lc);\r
-\r
-        if (lc.success) {\r
-            /* ok the destroy the ccache */\r
-            char a_ccname[KRB5_MAXCCH_CCNAME];\r
-            krb5_ccache cc = NULL;\r
-\r
-            _reportf(L"Destroying ccache [%s]", ccname);\r
-\r
-            UnicodeStrToAnsi(a_ccname,\r
-                             sizeof(a_ccname),\r
-                             ccname);\r
-\r
-            code = pkrb5_cc_resolve(ctx,\r
-                                    a_ccname,\r
-                                    &cc);\r
-            if (code)\r
-                goto _delete_this_set;\r
-\r
-            code = pkrb5_cc_destroy(ctx, cc);\r
-\r
-            if (code) {\r
-                _reportf(L"krb5_cc_destroy returns code %d", code);\r
-            }\r
-\r
-        _delete_this_set:\r
-\r
-            lc.credset = d_cs;\r
-            lc.ccname = ccname;\r
-\r
-            /* note that although we are deleting credentials off the\r
-               credential set, the size of the credential set does not\r
-               decrease since we are doing it from inside\r
-               kcdb_credset_apply().  The deleted creds will simply be\r
-               marked as deleted until kcdb_credset_purge() is\r
-               called. */\r
-\r
-            kcdb_credset_apply(d_cs,\r
-                               khmint_delete_location_func,\r
-                               (void *) &lc);\r
-        }\r
-    }\r
-\r
-    kcdb_credset_purge(d_cs);\r
-\r
-    /* the remainder need to be deleted one by one */\r
-\r
-    kcdb_credset_get_size(d_cs, &s);\r
-\r
-    for (i=0; i < (int) s; ) {\r
-        khm_handle cred;\r
-        char a_ccname[KRB5_MAXCCH_CCNAME];\r
-        char a_srvname[KCDB_CRED_MAXCCH_NAME];\r
-        wchar_t srvname[KCDB_CRED_MAXCCH_NAME];\r
-        krb5_ccache cc;\r
-        krb5_creds in_cred, out_cred;\r
-        krb5_principal princ;\r
-        khm_int32 etype;\r
-\r
-        if (KHM_FAILED(kcdb_credset_get_cred(d_cs,\r
-                                             i,\r
-                                             &cred))) {\r
-            i++;\r
-            continue;\r
-        }\r
-\r
-        cb = sizeof(ccname);\r
-        if (KHM_FAILED(kcdb_cred_get_attr(cred,\r
-                                          KCDB_ATTR_LOCATION,\r
-                                          NULL,\r
-                                          ccname,\r
-                                          &cb)))\r
-            goto _done_with_this_cred;\r
-\r
-        _reportf(L"Looking at ccache [%s]", ccname);\r
-\r
-        UnicodeStrToAnsi(a_ccname,\r
-                         sizeof(a_ccname),\r
-                         ccname);\r
-\r
-        code = pkrb5_cc_resolve(ctx,\r
-                                a_ccname,\r
-                                &cc);\r
-\r
-        if (code)\r
-            goto _skip_similar;\r
-\r
-        code = pkrb5_cc_get_principal(ctx, cc, &princ);\r
-\r
-        if (code) {\r
-            pkrb5_cc_close(ctx, cc);\r
-            goto _skip_similar;\r
-        }\r
-\r
-    _del_this_cred:\r
-\r
-        cb = sizeof(etype);\r
-\r
-        if (KHM_FAILED(kcdb_cred_get_attr(cred,\r
-                                          attr_id_key_enctype,\r
-                                          NULL,\r
-                                          &etype,\r
-                                          &cb)))\r
-            goto _do_next_cred;\r
-\r
-        cb = sizeof(srvname);\r
-        if (KHM_FAILED(kcdb_cred_get_name(cred,\r
-                                          srvname,\r
-                                          &cb)))\r
-            goto _do_next_cred;\r
-\r
-        _reportf(L"Attempting to delete ticket %s", srvname);\r
-\r
-        UnicodeStrToAnsi(a_srvname, sizeof(a_srvname), srvname);\r
-\r
-        ZeroMemory(&in_cred, sizeof(in_cred));\r
-\r
-        code = pkrb5_parse_name(ctx, a_srvname, &in_cred.server);\r
-        if (code)\r
-            goto _do_next_cred;\r
-        in_cred.client = princ;\r
-        in_cred.keyblock.enctype = etype;\r
-\r
-        code = pkrb5_cc_retrieve_cred(ctx,\r
-                                      cc,\r
-                                      KRB5_TC_MATCH_SRV_NAMEONLY |\r
-                                      KRB5_TC_SUPPORTED_KTYPES,\r
-                                      &in_cred,\r
-                                      &out_cred);\r
-        if (code)\r
-            goto _do_next_cred_0;\r
-\r
-        code = pkrb5_cc_remove_cred(ctx, cc,\r
-                                    KRB5_TC_MATCH_SRV_NAMEONLY |\r
-                                    KRB5_TC_SUPPORTED_KTYPES |\r
-                                    KRB5_TC_MATCH_AUTHDATA,\r
-                                    &out_cred);\r
-\r
-        pkrb5_free_cred_contents(ctx, &out_cred);\r
-    _do_next_cred_0:\r
-        pkrb5_free_principal(ctx, in_cred.server);\r
-    _do_next_cred:\r
-\r
-        /* check if the next cred is also of the same ccache */\r
-        kcdb_cred_release(cred);\r
-\r
-        for (i++; i < (int) s; i++) {\r
-            if (KHM_FAILED(kcdb_credset_get_cred(d_cs,\r
-                                                 i,\r
-                                                 &cred)))\r
-                continue;\r
-        }\r
-\r
-        if (i < (int) s) {\r
-            wchar_t newcc[KRB5_MAXCCH_CCNAME];\r
-\r
-            cb = sizeof(newcc);\r
-            if (KHM_FAILED(kcdb_cred_get_attr(cred,\r
-                                              KCDB_ATTR_LOCATION,\r
-                                              NULL,\r
-                                              newcc,\r
-                                              &cb)) ||\r
-                wcscmp(newcc, ccname)) {\r
-                i--;            /* we have to look at this again */\r
-                goto _done_with_this_set;\r
-            }\r
-            goto _del_this_cred;\r
-        }\r
-        \r
-\r
-    _done_with_this_set:\r
-        pkrb5_free_principal(ctx, princ);\r
-\r
-        pkrb5_cc_close(ctx, cc);\r
-\r
-    _done_with_this_cred:\r
-        kcdb_cred_release(cred);\r
-        i++;\r
-        continue;\r
-\r
-    _skip_similar:\r
-        kcdb_cred_release(cred);\r
-\r
-        for (++i; i < (int) s; i++) {\r
-            wchar_t newcc[KRB5_MAXCCH_CCNAME];\r
-\r
-            if (KHM_FAILED(kcdb_credset_get_cred(d_cs,\r
-                                                 i,\r
-                                                 &cred)))\r
-                continue;\r
-\r
-            cb = sizeof(newcc);\r
-            if (KHM_FAILED(kcdb_cred_get_attr(cred,\r
-                                              KCDB_ATTR_LOCATION,\r
-                                              NULL,\r
-                                              &newcc,\r
-                                              &cb))) {\r
-                kcdb_cred_release(cred);\r
-                continue;\r
-            }\r
-\r
-            if (wcscmp(newcc, ccname)) {\r
-                kcdb_cred_release(cred);\r
-                break;\r
-            }\r
-        }\r
-    }\r
-\r
- _cleanup:\r
-\r
-    if (d_cs)\r
-        kcdb_credset_delete(&d_cs);\r
-\r
-    if (ctx != NULL)\r
-        pkrb5_free_context(ctx);\r
-\r
-    return rv;\r
-}\r
-\r
-int\r
-khm_krb5_destroy_identity(khm_handle identity)\r
-{\r
-    krb5_context               ctx;\r
-    krb5_ccache                        cache;\r
-    krb5_error_code            rc;\r
-\r
-    ctx = NULL;\r
-    cache = NULL;\r
-\r
-    if (rc = khm_krb5_initialize(identity, &ctx, &cache))\r
-        return(rc);\r
-\r
-    rc = pkrb5_cc_destroy(ctx, cache);\r
-\r
-    if (ctx != NULL)\r
-        pkrb5_free_context(ctx);\r
-\r
-    return(rc);\r
-}\r
-\r
-static BOOL\r
-GetSecurityLogonSessionData(PSECURITY_LOGON_SESSION_DATA * ppSessionData)\r
-{\r
-    NTSTATUS Status = 0;\r
-    HANDLE  TokenHandle;\r
-    TOKEN_STATISTICS Stats;\r
-    DWORD   ReqLen;\r
-    BOOL    Success;\r
-\r
-    if (!ppSessionData)\r
-        return FALSE;\r
-    *ppSessionData = NULL;\r
-\r
-    Success = OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &TokenHandle );\r
-    if ( !Success )\r
-        return FALSE;\r
-\r
-    Success = GetTokenInformation( TokenHandle, TokenStatistics, &Stats, sizeof(TOKEN_STATISTICS), &ReqLen );\r
-    CloseHandle( TokenHandle );\r
-    if ( !Success )\r
-        return FALSE;\r
-\r
-    Status = pLsaGetLogonSessionData( &Stats.AuthenticationId, ppSessionData );\r
-    if ( FAILED(Status) || !ppSessionData )\r
-        return FALSE;\r
-\r
-    return TRUE;\r
-}\r
-\r
-// IsKerberosLogon() does not validate whether or not there are valid\r
-// tickets in the cache.  It validates whether or not it is reasonable\r
-// to assume that if we attempted to retrieve valid tickets we could\r
-// do so.  Microsoft does not automatically renew expired tickets.\r
-// Therefore, the cache could contain expired or invalid tickets.\r
-// Microsoft also caches the user's password and will use it to\r
-// retrieve new TGTs if the cache is empty and tickets are requested.\r
-\r
-static BOOL\r
-IsKerberosLogon(VOID)\r
-{\r
-    PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;\r
-    BOOL    Success = FALSE;\r
-\r
-    if ( GetSecurityLogonSessionData(&pSessionData) ) {\r
-        if ( pSessionData->AuthenticationPackage.Buffer ) {\r
-            WCHAR buffer[256];\r
-            WCHAR *usBuffer;\r
-            int usLength;\r
-\r
-            Success = FALSE;\r
-            usBuffer = (pSessionData->AuthenticationPackage).Buffer;\r
-            usLength = (pSessionData->AuthenticationPackage).Length;\r
-            if (usLength < 256)\r
-            {\r
-                lstrcpynW (buffer, usBuffer, usLength);\r
-                StringCbCatW (buffer, sizeof(buffer), L"");\r
-                if ( !lstrcmpW(L"Kerberos",buffer) )\r
-                    Success = TRUE;\r
-            }\r
-        }\r
-        pLsaFreeReturnBuffer(pSessionData);\r
-    }\r
-    return Success;\r
-}\r
-\r
-\r
-BOOL\r
-khm_krb5_ms2mit(char * match_princ, BOOL match_realm, BOOL save_creds,\r
-                khm_handle * ret_ident)\r
-{\r
-#ifdef NO_KRB5\r
-    return(FALSE);\r
-#else /* NO_KRB5 */\r
-    krb5_context kcontext = 0;\r
-    krb5_error_code code = 0;\r
-    krb5_ccache ccache=0;\r
-    krb5_ccache mslsa_ccache=0;\r
-    krb5_creds creds;\r
-    krb5_cc_cursor cursor=0;\r
-    krb5_principal princ = 0;\r
-    khm_handle ident = NULL;\r
-    wchar_t wname[KCDB_IDENT_MAXCCH_NAME];\r
-    char    cname[KCDB_IDENT_MAXCCH_NAME];\r
-    char *cache_name = NULL;\r
-    char *princ_name = NULL;\r
-    BOOL rc = FALSE;\r
-\r
-    kherr_reportf(L"Begin : khm_krb5_ms2mit. save_cred=%d\n", (int) save_creds);\r
-\r
-    if ( !pkrb5_init_context )\r
-        goto cleanup;\r
-\r
-    if (code = pkrb5_init_context(&kcontext))\r
-        goto cleanup;\r
-\r
-    kherr_reportf(L"Resolving MSLSA\n");\r
-\r
-    if (code = pkrb5_cc_resolve(kcontext, "MSLSA:", &mslsa_ccache))\r
-        goto cleanup;\r
-\r
-    if ( save_creds ) {\r
-        kherr_reportf(L"Getting principal\n");\r
-        if (code = pkrb5_cc_get_principal(kcontext, mslsa_ccache, &princ))\r
-            goto cleanup;\r
-\r
-        kherr_reportf(L"Unparsing name\n");\r
-        if (code = pkrb5_unparse_name(kcontext, princ, &princ_name))\r
-            goto cleanup;\r
-\r
-        AnsiStrToUnicode(wname, sizeof(wname), princ_name);\r
-\r
-        kherr_reportf(L"Unparsed name [%s]", wname);\r
-\r
-        /* see if we have to match a specific principal */\r
-        if (match_princ != NULL) {\r
-            if (strcmp(princ_name, match_princ)) {\r
-                kherr_reportf(L"Principal mismatch.  Wanted [%S], found [%S]",\r
-                              match_princ, princ_name);\r
-                goto cleanup;\r
-            }\r
-        } else if (match_realm) {\r
-            wchar_t * wdefrealm;\r
-            char defrealm[256];\r
-            krb5_data * princ_realm;\r
-\r
-            wdefrealm = khm_krb5_get_default_realm();\r
-            if (wdefrealm == NULL) {\r
-                kherr_reportf(L"Can't determine default realm");\r
-                goto cleanup;\r
-            }\r
-\r
-            princ_realm = krb5_princ_realm(kcontext, princ);\r
-            UnicodeStrToAnsi(defrealm, sizeof(defrealm), wdefrealm);\r
-\r
-            if (strncmp(defrealm, princ_realm->data, princ_realm->length)) {\r
-                kherr_reportf(L"Realm mismatch.  Wanted [%S], found [%*S]",\r
-                              defrealm, princ_realm->length, princ_realm->data);\r
-                PFREE(wdefrealm);\r
-                goto cleanup;\r
-            }\r
-\r
-            PFREE(wdefrealm);\r
-        }\r
-\r
-        if (KHM_SUCCEEDED(kcdb_identity_create(wname,\r
-                                               KCDB_IDENT_FLAG_CREATE,\r
-                                               &ident))) {\r
-            khm_handle idconfig = NULL;\r
-            khm_handle k5config = NULL;\r
-            khm_size cb;\r
-\r
-            wname[0] = L'\0';\r
-\r
-            kcdb_identity_get_config(ident, KHM_FLAG_CREATE, &idconfig);\r
-            if (idconfig == NULL)\r
-                goto _done_checking_config;\r
-\r
-            khc_open_space(idconfig, CSNAME_KRB5CRED, KHM_FLAG_CREATE, &k5config);\r
-            if (k5config == NULL)\r
-                goto _done_checking_config;\r
-\r
-            cb = sizeof(wname);\r
-            khc_read_string(k5config,\r
-                            L"DefaultCCName",\r
-                            wname, &cb);\r
-\r
-        _done_checking_config:\r
-\r
-            if (idconfig)\r
-                khc_close_space(idconfig);\r
-            if (k5config)\r
-                khc_close_space(k5config);\r
-\r
-            if (wname[0]) {\r
-                UnicodeStrToAnsi(cname, sizeof(cname), wname);\r
-            } else {\r
-                StringCbPrintfA(cname, sizeof(cname), "API:%s", princ_name);\r
-            }\r
-\r
-            cache_name = cname;\r
-\r
-        } else {\r
-            /* the identity could not be created.  we just use the\r
-               name of the principal as the ccache name. */\r
-            StringCbPrintfA(cname, sizeof(cname), "API:%s", princ_name);\r
-            cache_name = cname;\r
-        }\r
-\r
-        kherr_reportf(L"Resolving target cache [%S]\n", cache_name);\r
-\r
-        if (code = pkrb5_cc_resolve(kcontext, cache_name, &ccache)) {\r
-            kherr_reportf(L"Cannot resolve cache [%S] with code=%d.  Trying default.\n", cache_name, code);\r
-\r
-            if (code = pkrb5_cc_default(kcontext, &ccache)) {\r
-                kherr_reportf(L"Failed to resolve default ccache. Code=%d", code);\r
-                goto cleanup;\r
-            }\r
-        }\r
-\r
-        kherr_reportf(L"Initializing ccache\n");\r
-        if (code = pkrb5_cc_initialize(kcontext, ccache, princ))\r
-            goto cleanup;\r
-\r
-        kherr_reportf(L"Copying credentials\n");\r
-        if (code = pkrb5_cc_copy_creds(kcontext, mslsa_ccache, ccache))\r
-            goto cleanup;\r
-\r
-        /* and mark the identity as having been imported */\r
-        if (ident) {\r
-            khm_krb5_set_identity_flags(ident, K5IDFLAG_IMPORTED, K5IDFLAG_IMPORTED);\r
-\r
-            if (ret_ident) {\r
-                *ret_ident = ident;\r
-                kcdb_identity_hold(*ret_ident);\r
-            }\r
-        }\r
-\r
-        rc = TRUE;\r
-\r
-    } else {\r
-        /* Enumerate tickets from cache looking for an initial ticket */\r
-        if ((code = pkrb5_cc_start_seq_get(kcontext, mslsa_ccache, &cursor))) \r
-            goto cleanup;\r
-\r
-        while (!(code = pkrb5_cc_next_cred(kcontext, mslsa_ccache, \r
-                                           &cursor, &creds))) {\r
-            if ( creds.ticket_flags & TKT_FLG_INITIAL ) {\r
-                rc = TRUE;\r
-                pkrb5_free_cred_contents(kcontext, &creds);\r
-                break;\r
-            }\r
-            pkrb5_free_cred_contents(kcontext, &creds);\r
-        }\r
-        pkrb5_cc_end_seq_get(kcontext, mslsa_ccache, &cursor);\r
-    }\r
-\r
-cleanup:\r
-    kherr_reportf(L"  Received code=%d", code);\r
-\r
-    if (princ_name)\r
-        pkrb5_free_unparsed_name(kcontext, princ_name);\r
-    if (princ)\r
-        pkrb5_free_principal(kcontext, princ);\r
-    if (ccache)\r
-        pkrb5_cc_close(kcontext, ccache);\r
-    if (mslsa_ccache)\r
-        pkrb5_cc_close(kcontext, mslsa_ccache);\r
-    if (kcontext)\r
-        pkrb5_free_context(kcontext);\r
-    if (ident)\r
-        kcdb_identity_release(ident);\r
-\r
-    return(rc);\r
-#endif /* NO_KRB5 */\r
-}\r
-\r
-#define KRB_FILE                "KRB.CON"\r
-#define KRBREALM_FILE           "KRBREALM.CON"\r
-#define KRB5_FILE               "KRB5.INI"\r
-#define KRB5_TMP_FILE           "KRB5.INI.TMP"\r
-\r
-BOOL \r
-khm_krb5_get_temp_profile_file(LPSTR confname, UINT szConfname)\r
-{\r
-    GetTempPathA(szConfname, confname);\r
-    confname[szConfname-1] = '\0';\r
-    StringCchCatA(confname, szConfname, KRB5_TMP_FILE);\r
-    confname[szConfname-1] = '\0';\r
-    return FALSE;\r
-}\r
-\r
-#ifdef NOT_QUITE_IMPLEMENTED_YET\r
-BOOL\r
-khm_krb5_set_profile_file(krb5_context ctx, LPSTR confname)\r
-{\r
-    char *conffiles[2];\r
-\r
-    if (confname == NULL ||\r
-        pkrb5_set_config_files == NULL ||\r
-        ctx == NULL)\r
-        return FALSE;\r
-\r
-    conffiles[0] = confname;\r
-    conffiles[1] = NULL;\r
-\r
-    if (pkrb5_set_config_files(ctx, conffiles))\r
-        return FALSE;\r
-    else\r
-        return TRUE;\r
-}\r
-#endif\r
-\r
-BOOL \r
-khm_krb5_get_profile_file(LPSTR confname, UINT szConfname)\r
-{\r
-    char **configFile = NULL;\r
-    if (pkrb5_get_default_config_files(&configFile)) \r
-    {\r
-        GetWindowsDirectoryA(confname,szConfname);\r
-        confname[szConfname-1] = '\0';\r
-\r
-        StringCchCatA(confname, szConfname, "\\");\r
-        StringCchCatA(confname, szConfname, KRB5_FILE);\r
-\r
-        return FALSE;\r
-    }\r
-    \r
-    *confname = 0;\r
-    \r
-    if (configFile)\r
-    {\r
-        StringCchCopyA(confname, szConfname, *configFile);\r
-        pkrb5_free_config_files(configFile); \r
-    }\r
-    \r
-    if (!*confname)\r
-    {\r
-        GetWindowsDirectoryA(confname,szConfname);\r
-        confname[szConfname-1] = '\0';\r
-        StringCchCatA(confname, szConfname, "\\");\r
-        StringCchCatA(confname, szConfname, KRB5_FILE);\r
-    }\r
-    \r
-    return FALSE;\r
-}\r
-\r
-BOOL\r
-khm_get_krb4_con_file(LPSTR confname, UINT szConfname)\r
-{\r
-    if (hKrb5 && !hKrb4) { // hold krb.con where krb5.ini is located\r
-        CHAR krbConFile[MAX_PATH]="";\r
-        LPSTR pFind;\r
-\r
-        //strcpy(krbConFile, CLeashApp::m_krbv5_profile->first_file->filename);\r
-        if (khm_krb5_get_profile_file(krbConFile, sizeof(krbConFile))) {\r
-            GetWindowsDirectoryA(krbConFile,sizeof(krbConFile));\r
-            krbConFile[MAX_PATH-1] = '\0';\r
-            StringCchCatA(confname, szConfname, "\\");\r
-        }\r
-        \r
-        pFind = strrchr(krbConFile, '\\');\r
-        if (pFind) {\r
-            *pFind = '\0';\r
-            StringCchCatA(krbConFile, ARRAYLENGTH(krbConFile), "\\");\r
-            StringCchCatA(krbConFile, ARRAYLENGTH(krbConFile), KRB_FILE);\r
-        }\r
-        else\r
-            krbConFile[0] = '\0';\r
-\r
-        StringCchCopyA(confname, szConfname, krbConFile);\r
-    }\r
-    else if (hKrb4) { \r
-        unsigned int size = szConfname;\r
-        memset(confname, '\0', szConfname);\r
-        if (!pkrb_get_krbconf2(confname, &size))\r
-            { // Error has happened\r
-                GetWindowsDirectoryA(confname,szConfname);\r
-                confname[szConfname-1] = '\0';\r
-                StringCchCatA(confname, szConfname, "\\");\r
-                StringCchCatA(confname, szConfname, KRB_FILE);\r
-            }\r
-    }\r
-    return FALSE;\r
-}\r
-\r
-int\r
-readstring(FILE * file, char * buf, int len)\r
-{\r
-    int  c,i;\r
-    memset(buf, '\0', sizeof(buf));\r
-    for (i=0, c=fgetc(file); c != EOF ; c=fgetc(file), i++) {  \r
-        if (i < sizeof(buf)) {\r
-            if (c == '\n') {\r
-                buf[i] = '\0';\r
-                return i;\r
-            } else {\r
-                buf[i] = c;\r
-            }\r
-        } else {\r
-            if (c == '\n') {\r
-                buf[len-1] = '\0';\r
-                return(i);\r
-            }\r
-        }\r
-    }\r
-    if (c == EOF) {\r
-        if (i > 0 && i < len) {\r
-            buf[i] = '\0';\r
-            return(i);\r
-        } else {\r
-            buf[len-1] = '\0';\r
-            return(-1);\r
-        }\r
-    }\r
-    return(-1);\r
-}\r
-\r
-/*! \internal\r
-    \brief Return a list of configured realms\r
-\r
-    The string that is returned is a set of null terminated unicode\r
-    strings, each of which denotes one realm.  The set is terminated\r
-    by a zero length null terminated string.\r
-\r
-    The caller should free the returned string using free()\r
-\r
-    \return The string with the list of realms or NULL if the\r
-    operation fails.\r
-*/\r
-wchar_t * \r
-khm_krb5_get_realm_list(void) \r
-{\r
-    wchar_t * rlist = NULL;\r
-\r
-    if (pprofile_get_subsection_names && pprofile_free_list) {\r
-        const char*  rootSection[] = {"realms", NULL};\r
-        const char** rootsec = rootSection;\r
-        char **sections = NULL, **cpp = NULL, *value = NULL;\r
-\r
-        char krb5_conf[MAX_PATH+1];\r
-\r
-        if (!khm_krb5_get_profile_file(krb5_conf,sizeof(krb5_conf))) {\r
-            profile_t profile;\r
-            long retval;\r
-            const char *filenames[2];\r
-            wchar_t * d;\r
-            size_t cbsize;\r
-            size_t t;\r
-\r
-            filenames[0] = krb5_conf;\r
-            filenames[1] = NULL;\r
-            retval = pprofile_init(filenames, &profile);\r
-            if (!retval) {\r
-                retval = pprofile_get_subsection_names(profile,        rootsec, \r
-                                                       &sections);\r
-\r
-                if (!retval)\r
-                    {\r
-                    /* first figure out how much space to allocate */\r
-                    cbsize = 0;\r
-                    for (cpp = sections; *cpp; cpp++) \r
-                    {\r
-                        cbsize += sizeof(wchar_t) * (strlen(*cpp) + 1);\r
-                    }\r
-                    cbsize += sizeof(wchar_t); /* double null terminated */\r
-\r
-                    rlist = PMALLOC(cbsize);\r
-                    d = rlist;\r
-                    for (cpp = sections; *cpp; cpp++)\r
-                    {\r
-                        AnsiStrToUnicode(d, cbsize, *cpp);\r
-                        t = wcslen(d) + 1;\r
-                        d += t;\r
-                        cbsize -= sizeof(wchar_t) * t;\r
-                    }\r
-                    *d = L'\0';\r
-                }\r
-\r
-                pprofile_free_list(sections);\r
-\r
-#if 0\r
-                retval = pprofile_get_string(profile, "libdefaults","noaddresses", 0, "true", &value);\r
-                if ( value ) {\r
-                    disable_noaddresses = config_boolean_to_int(value);\r
-                    pprofile_release_string(value);\r
-                }\r
-#endif\r
-                pprofile_release(profile);\r
-            }\r
-        }\r
-    } else {\r
-        FILE * file;\r
-        char krb_conf[MAX_PATH+1];\r
-        char * p;\r
-        size_t cbsize, t;\r
-        wchar_t * d;\r
-\r
-        if (!khm_get_krb4_con_file(krb_conf,sizeof(krb_conf)) && \r
-#if _MSC_VER >= 1400\r
-            !fopen_s(&file, krb_conf, "rt")\r
-#else\r
-            (file = fopen(krb_conf, "rt"))\r
-#endif\r
-            )\r
-        {\r
-            char lineBuf[256];\r
-\r
-            /*TODO: compute the actual required buffer size instead of hardcoding */\r
-            cbsize = 16384; // arbitrary\r
-            rlist = PMALLOC(cbsize);\r
-            d = rlist;\r
-\r
-            // Skip the default realm\r
-            readstring(file,lineBuf,sizeof(lineBuf));\r
-\r
-            // Read the defined realms\r
-            while (TRUE)\r
-            {\r
-                if (readstring(file,lineBuf,sizeof(lineBuf)) < 0)\r
-                    break;\r
-\r
-                if (*(lineBuf + strlen(lineBuf) - 1) == '\r')\r
-                    *(lineBuf + strlen(lineBuf) - 1) = 0;\r
-\r
-                for (p=lineBuf; *p ; p++)\r
-                {\r
-                    if (isspace(*p)) {\r
-                        *p = 0;\r
-                        break;\r
-                    }\r
-                }\r
-\r
-                if ( strncmp(".KERBEROS.OPTION.",lineBuf,17) ) {\r
-                    t = strlen(lineBuf) + 1;\r
-                    if(cbsize > (1 + t*sizeof(wchar_t))) {\r
-                        AnsiStrToUnicode(d, cbsize, lineBuf);\r
-                        d += t;\r
-                        cbsize -= t * sizeof(wchar_t);\r
-                    } else\r
-                        break;\r
-                }\r
-            }\r
-\r
-            *d = L'\0';\r
-\r
-            fclose(file);\r
-        }\r
-    }\r
-\r
-    return rlist;\r
-}\r
-\r
-/*! \internal\r
-    \brief Get the default realm\r
-\r
-    A string will be returned that specifies the default realm.  The\r
-    caller should free the string using PFREE().\r
-\r
-    Returns NULL if the operation fails.\r
-*/\r
-wchar_t * \r
-khm_krb5_get_default_realm(void)\r
-{\r
-    wchar_t * realm;\r
-    size_t cch;\r
-    krb5_context ctx=0;\r
-    char * def = 0;\r
-\r
-    pkrb5_init_context(&ctx);\r
-\r
-    if (ctx == 0)\r
-        return NULL;\r
-\r
-    pkrb5_get_default_realm(ctx,&def);\r
-    \r
-    if (def) {\r
-        cch = strlen(def) + 1;\r
-        realm = PMALLOC(sizeof(wchar_t) * cch);\r
-        AnsiStrToUnicode(realm, sizeof(wchar_t) * cch, def);\r
-        pkrb5_free_default_realm(ctx, def);\r
-    } else\r
-        realm = NULL;\r
-\r
-    pkrb5_free_context(ctx);\r
-\r
-    return realm;\r
-}\r
-\r
-long\r
-khm_krb5_set_default_realm(wchar_t * realm) {\r
-    krb5_context ctx=0;\r
-    char * def = 0;\r
-    long rv = 0;\r
-    char astr[K5_MAXCCH_REALM];\r
-\r
-    UnicodeStrToAnsi(astr, sizeof(astr), realm);\r
-\r
-    pkrb5_init_context(&ctx);\r
-    pkrb5_get_default_realm(ctx,&def);\r
-\r
-    if ((def && strcmp(def, astr)) ||\r
-        !def) {\r
-        rv = pkrb5_set_default_realm(ctx, astr);\r
-    }\r
-\r
-    if (def) {\r
-        pkrb5_free_default_realm(ctx, def);\r
-    }\r
-\r
-    pkrb5_free_context(ctx);\r
-\r
-    return rv;\r
-}\r
-\r
-wchar_t * \r
-khm_get_realm_from_princ(wchar_t * princ) {\r
-    wchar_t * t;\r
-\r
-    if(!princ)\r
-        return NULL;\r
-\r
-    for (t = princ; *t; t++) {\r
-        if(*t == L'\\') {       /* escape */\r
-            t++;\r
-            if(! *t)            /* malformed */\r
-                break;\r
-        } else if (*t == L'@')\r
-            break;\r
-    }\r
-\r
-    if (*t == '@' && *(t+1) != L'\0')\r
-        return (t+1);\r
-    else\r
-        return NULL;\r
-}\r
-\r
-long\r
-khm_krb5_changepwd(char * principal,\r
-                   char * password,\r
-                   char * newpassword,\r
-                   char** error_str)\r
-{\r
-    krb5_error_code rc = 0;\r
-    int result_code = 0;\r
-    krb5_data result_code_string, result_string;\r
-    krb5_context context = 0;\r
-    krb5_principal princ = 0;\r
-    krb5_get_init_creds_opt opts;\r
-    krb5_creds creds;\r
-\r
-    result_string.data = 0;\r
-    result_code_string.data = 0;\r
-\r
-    if ( !pkrb5_init_context )\r
-        goto cleanup;\r
-\r
-    if (rc = pkrb5_init_context(&context)) {\r
-        goto cleanup;\r
-    }\r
-\r
-    if (rc = pkrb5_parse_name(context, principal, &princ)) {\r
-        goto cleanup;\r
-    }\r
-\r
-    pkrb5_get_init_creds_opt_init(&opts);\r
-    pkrb5_get_init_creds_opt_set_tkt_life(&opts, 5*60);\r
-    pkrb5_get_init_creds_opt_set_renew_life(&opts, 0);\r
-    pkrb5_get_init_creds_opt_set_forwardable(&opts, 0);\r
-    pkrb5_get_init_creds_opt_set_proxiable(&opts, 0);\r
-    pkrb5_get_init_creds_opt_set_address_list(&opts,NULL);\r
-\r
-    if (rc = pkrb5_get_init_creds_password(context, &creds, princ, \r
-                                           password, 0, 0, 0, \r
-                                           "kadmin/changepw", &opts)) {\r
-        if (rc == KRB5KRB_AP_ERR_BAD_INTEGRITY) {\r
-#if 0\r
-            com_err(argv[0], 0,\r
-                    "Password incorrect while getting initial ticket");\r
-#endif\r
-        } else {\r
-#if 0\r
-            com_err(argv[0], ret, "getting initial ticket");\r
-#endif\r
-        }\r
-        goto cleanup;\r
-    }\r
-\r
-    if (rc = pkrb5_change_password(context, &creds, newpassword,\r
-                                   &result_code, &result_code_string,\r
-                                   &result_string)) {\r
-#if 0\r
-        com_err(argv[0], ret, "changing password");\r
-#endif\r
-        goto cleanup;\r
-    }\r
-\r
-    if (result_code) {\r
-        int len = result_code_string.length + \r
-            (result_string.length ? (sizeof(": ") - 1) : 0) +\r
-            result_string.length;\r
-        if (len && error_str) {\r
-            *error_str = PMALLOC(len + 1);\r
-            if (*error_str)\r
-                StringCchPrintfA(*error_str, len+1,\r
-                                 "%.*s%s%.*s",\r
-                                 result_code_string.length, \r
-                                 result_code_string.data,\r
-                                 result_string.length?": ":"",\r
-                                 result_string.length, \r
-                                 result_string.data);\r
-        }\r
-        rc = result_code;\r
-        goto cleanup;\r
-    }\r
-\r
- cleanup:\r
-    if (result_string.data)\r
-        pkrb5_free_data_contents(context, &result_string);\r
-\r
-    if (result_code_string.data)\r
-        pkrb5_free_data_contents(context, &result_code_string);\r
-\r
-    if (princ)\r
-        pkrb5_free_principal(context, princ);\r
-\r
-    if (context)\r
-        pkrb5_free_context(context);\r
-\r
-    return rc;\r
-}\r
-\r
-khm_int32 KHMAPI\r
-khm_krb5_creds_is_equal(khm_handle vcred1, khm_handle vcred2, void * dummy) {\r
-    if (kcdb_creds_comp_attr(vcred1, vcred2, KCDB_ATTR_LOCATION) ||\r
-        kcdb_creds_comp_attr(vcred1, vcred2, attr_id_key_enctype) ||\r
-        kcdb_creds_comp_attr(vcred1, vcred2, attr_id_tkt_enctype) ||\r
-        kcdb_creds_comp_attr(vcred1, vcred2, attr_id_kvno))\r
-        return 1;\r
-    else\r
-        return 0;\r
-}\r
-\r
-void\r
-khm_krb5_set_identity_flags(khm_handle identity,\r
-                            khm_int32  flag_mask,\r
-                            khm_int32  flag_value) {\r
-\r
-    khm_int32 t = 0;\r
-    khm_size  cb;\r
-\r
-    cb = sizeof(t);\r
-    if (KHM_FAILED(kcdb_identity_get_attr(identity,\r
-                                          attr_id_krb5_idflags,\r
-                                          NULL,\r
-                                          &t, &cb))) {\r
-        t = 0;\r
-    }\r
-\r
-    t &= ~flag_mask;\r
-    t |= (flag_value & flag_mask);\r
-\r
-    kcdb_identity_set_attr(identity,\r
-                           attr_id_krb5_idflags,\r
-                           &t, sizeof(t));\r
-}\r
-\r
-khm_int32\r
-khm_krb5_get_identity_flags(khm_handle identity) {\r
-    khm_int32 t = 0;\r
-    khm_size  cb;\r
-\r
-    cb = sizeof(t);\r
-    kcdb_identity_get_attr(identity,\r
-                           attr_id_krb5_idflags,\r
-                           NULL, &t, &cb);\r
-\r
-    return t;\r
-}\r
-\r
-long\r
-khm_krb5_get_temp_ccache(krb5_context ctx,\r
-                         krb5_ccache * prcc) {\r
-    int  rnd = rand();\r
-    char ccname[MAX_PATH];\r
-    long code = 0;\r
-    krb5_ccache cc = 0;\r
-\r
-    StringCbPrintfA(ccname, sizeof(ccname), "MEMORY:TempCache%8x", rnd);\r
-\r
-    code = pkrb5_cc_resolve(ctx, ccname, &cc);\r
-\r
-    if (code == 0)\r
-        *prcc = cc;\r
-\r
-    return code;\r
-}\r
-\r
-/*\r
-\r
-  The configuration information for each identity comes from a\r
-  multitude of layers organized as follows.  The ordering is\r
-  decreasing in priority.  When looking up a value, the value will be\r
-  looked up in each layer in turn starting at level 0.  The first\r
-  instance of the value found will be the effective value.\r
-\r
-  0  : <identity configuration>\Krb5Cred\r
-\r
-  0.1: per user\r
-\r
-  0.2: per machine\r
-\r
-  1  : <plugin configuration>\Parameters\Realms\<realm of identity>\r
-\r
-  1.1: per user\r
-\r
-  1.2: per machine\r
-\r
-  2  : <plugin configuration>\Parameters\r
-\r
-  2.1: per user\r
-\r
-  2.2: per machine\r
-\r
-  2.3: schema\r
-\r
- */\r
-khm_int32\r
-khm_krb5_get_identity_config(khm_handle ident,\r
-                            khm_int32 flags,\r
-                            khm_handle * ret_csp) {\r
-\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    khm_handle csp_i = NULL;\r
-    khm_handle csp_ik5 = NULL;\r
-    khm_handle csp_realms = NULL;\r
-    khm_handle csp_realm = NULL;\r
-    khm_handle csp_plugins = NULL;\r
-    khm_handle csp_krbcfg = NULL;\r
-    khm_handle csp_rv = NULL;\r
-    wchar_t realm[KCDB_IDENT_MAXCCH_NAME];\r
-\r
-    realm[0] = L'\0';\r
-\r
-    if (ident) {\r
-        wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-        wchar_t * trealm;\r
-        khm_size cb_idname = sizeof(idname);\r
-\r
-        rv = kcdb_identity_get_name(ident, idname, &cb_idname);\r
-        if (KHM_SUCCEEDED(rv) &&\r
-            (trealm = khm_get_realm_from_princ(idname)) != NULL) {\r
-            StringCbCopy(realm, sizeof(realm), trealm);\r
-        }\r
-    }\r
-\r
-    if (ident) {\r
-        rv = kcdb_identity_get_config(ident, flags, &csp_i);\r
-        if (KHM_FAILED(rv))\r
-            goto try_realm;\r
-\r
-        rv = khc_open_space(csp_i, CSNAME_KRB5CRED, flags, &csp_ik5);\r
-        if (KHM_FAILED(rv))\r
-            goto try_realm;\r
-\r
-    try_realm:\r
-\r
-        if (realm[0] == L'\0')\r
-            goto done_shadow_realm;\r
-\r
-        rv = khc_open_space(csp_params, CSNAME_REALMS, flags, &csp_realms);\r
-        if (KHM_FAILED(rv))\r
-            goto done_shadow_realm;\r
-\r
-        rv = khc_open_space(csp_realms, realm, flags, &csp_realm);\r
-        if (KHM_FAILED(rv))\r
-            goto done_shadow_realm;\r
-\r
-        rv = khc_shadow_space(csp_realm, csp_params);\r
-\r
-    done_shadow_realm:\r
-\r
-        if (csp_ik5) {\r
-            if (csp_realm)\r
-                rv = khc_shadow_space(csp_ik5, csp_realm);\r
-            else\r
-                rv = khc_shadow_space(csp_ik5, csp_params);\r
-\r
-            csp_rv = csp_ik5;\r
-        } else {\r
-            if (csp_realm)\r
-                csp_rv = csp_realm;\r
-        }\r
-    }\r
-\r
-    if (csp_rv == NULL) {\r
-\r
-        /* No valid identity specified or the specified identity\r
-           doesn't have any configuration. We default to the\r
-           parameters key. */\r
-\r
-        /* we don't just return csp_params since that's a global\r
-           handle that we shouldn't close until the plugin is\r
-           unloaded.  The caller is going to close the returned handle\r
-           when it is done.  So we need to create a new csp_params\r
-           that can safely be closed. */\r
-\r
-        rv = kmm_get_plugins_config(0, &csp_plugins);\r
-        if (KHM_FAILED(rv))\r
-            goto done;\r
-\r
-        rv = khc_open_space(csp_plugins, CSNAME_KRB5CRED, flags, &csp_krbcfg);\r
-        if (KHM_FAILED(rv))\r
-            goto done;\r
-\r
-        rv = khc_open_space(csp_krbcfg, CSNAME_PARAMS, flags, &csp_rv);\r
-    }\r
-\r
- done:\r
-\r
-    *ret_csp = csp_rv;\r
-\r
-    /* leave csp_ik5.  If it's non-NULL, then it's the return value */\r
-    /* leave csp_rv.  It's the return value. */\r
-    if (csp_i)\r
-        khc_close_space(csp_i);\r
-    if (csp_realms)\r
-        khc_close_space(csp_realms);\r
-\r
-    /* csp_realm can also be a return value if csp_ik5 was NULL */\r
-    if (csp_realm && csp_realm != csp_rv)\r
-        khc_close_space(csp_realm);\r
-\r
-    if (csp_plugins)\r
-        khc_close_space(csp_plugins);\r
-    if (csp_krbcfg)\r
-        khc_close_space(csp_krbcfg);\r
-\r
-    return rv;\r
-}\r
-\r
-/* from get_in_tkt.c */\r
-static krb5_error_code\r
-get_libdefault_string(profile_t profile, const char * realm,\r
-                      const char * option, char ** ret_val) {\r
-    char realmstr[K5_MAXCCH_REALM];\r
-    char **nameval = NULL;\r
-    const char * names[4];\r
-    krb5_error_code code = 0;\r
-\r
-    names[0] = "libdefaults";\r
-\r
-    if (!realm || !realm[0])\r
-        goto try_number_two;\r
-\r
-    StringCbCopyA(realmstr, sizeof(realmstr), realm);\r
-\r
-    /*\r
-     * Try number one:\r
-     *\r
-     * [libdefaults]\r
-     *         REALM = {\r
-     *                 option = <boolean>\r
-     *         }\r
-     */\r
-\r
-    names[1] = realmstr;\r
-    names[2] = option;\r
-    names[3] = 0;\r
-    code = pprofile_get_values(profile, names, &nameval);\r
-    if (code == 0 && nameval && nameval[0])\r
-       goto goodbye;\r
-\r
- try_number_two:\r
-\r
-    /*\r
-     * Try number two:\r
-     *\r
-     * [libdefaults]\r
-     *         option = <boolean>\r
-     */\r
-    \r
-    names[1] = option;\r
-    names[2] = 0;\r
-    code = pprofile_get_values(profile, names, &nameval);\r
-    if (code == 0 && nameval && nameval[0])\r
-       goto goodbye;\r
-\r
- goodbye:\r
-    if (!nameval) \r
-       return(ENOENT);\r
-\r
-    if (!nameval[0]) {\r
-        code = ENOENT;\r
-    } else {\r
-        size_t cb;\r
-\r
-        if (FAILED(StringCbLengthA(nameval[0], K5_MAXCCH_REALM * sizeof(char), &cb))) {\r
-            code = ENOMEM;\r
-        } else {\r
-            cb += sizeof(char);\r
-            *ret_val = PMALLOC(cb);\r
-\r
-            if (!*ret_val)\r
-                code = ENOMEM;\r
-            else {\r
-                StringCbCopyA(*ret_val, cb, nameval[0]);\r
-                code = 0;\r
-            }\r
-        }\r
-    }\r
-\r
-    pprofile_free_list(nameval);\r
-\r
-    return code;\r
-}\r
-\r
-khm_int32\r
-khm_krb5_get_identity_params(khm_handle ident, k5_params * p) {\r
-\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    khm_handle csp_id = NULL;\r
-    khm_int32 regf = 0;\r
-    khm_int32 proff = 0;\r
-    khm_int32 e;\r
-    khm_int32 v;\r
-    CHAR confname[MAX_PATH];\r
-    CHAR realmname[K5_MAXCCH_REALM];\r
-\r
-    ZeroMemory(p, sizeof(*p));\r
-\r
-    rv = khm_krb5_get_identity_config(ident, 0, &csp_id);\r
-    if (KHM_FAILED(rv))\r
-        goto done_reg;\r
-\r
-\r
-#define GETVAL(vname, vfield, flag) \\r
-    do {                            \\r
-    e = khc_value_exists(csp_id, vname);                               \\r
-    rv = khc_read_int32(csp_id, vname, &v);                            \\r
-    if (KHM_FAILED(rv)) goto done_reg;                                 \\r
-    p->vfield = v;                                                     \\r
-    if ((e & ~KCONF_FLAG_SCHEMA) != 0) regf |= flag;                   \\r
-    } while(FALSE)\r
-\r
-    /* Flags */\r
-    GETVAL(L"Renewable", renewable, K5PARAM_F_RENEW);\r
-    GETVAL(L"Forwardable", forwardable, K5PARAM_F_FORW);\r
-    GETVAL(L"Proxiable", proxiable, K5PARAM_F_PROX);\r
-    GETVAL(L"Addressless", addressless, K5PARAM_F_ADDL);\r
-    GETVAL(L"PublicIP", publicIP, K5PARAM_F_PUBIP);\r
-\r
-    /* Lifetime */\r
-    GETVAL(L"DefaultLifetime", lifetime, K5PARAM_F_LIFE);\r
-    GETVAL(L"MaxLifetime", lifetime_max, K5PARAM_F_LIFE_H);\r
-    GETVAL(L"MinLifetime", lifetime_min, K5PARAM_F_LIFE_L);\r
-\r
-    /* Renewable lifetime */\r
-    GETVAL(L"DefaultRenewLifetime", renew_life, K5PARAM_F_RLIFE);\r
-    GETVAL(L"MaxRenewLifetime", renew_life_max, K5PARAM_F_RLIFE_H);\r
-    GETVAL(L"MinRenewLifetime", renew_life_min, K5PARAM_F_RLIFE_L);\r
-\r
-#undef GETVAL\r
-\r
- done_reg:\r
-\r
-    if (csp_id)\r
-        khc_close_space(csp_id);\r
-\r
-    /* if all the parameters were read from the registry, then we have\r
-       no reason to read from the profile file. */\r
-    if (regf == K5PARAM_FM_ALL) {\r
-        p->source_reg = regf;\r
-        return KHM_ERROR_SUCCESS;\r
-    }\r
-\r
-    if (rv)\r
-        return rv;\r
-\r
-    /* we need to figure out the realm name, since there might be\r
-       per-realm configuration in the profile file. */\r
-\r
-    realmname[0] = '\0';\r
-\r
-    if (ident) {\r
-        wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-        khm_size cb;\r
-\r
-        idname[0] = L'\0';\r
-        cb = sizeof(idname);\r
-        rv = kcdb_identity_get_name(ident, idname, &cb);\r
-        if (KHM_SUCCEEDED(rv)) {\r
-            wchar_t * wrealm;\r
-\r
-            wrealm = khm_get_realm_from_princ(idname);\r
-            if (wrealm) {\r
-                UnicodeStrToAnsi(realmname, sizeof(realmname), wrealm);\r
-            }\r
-        }\r
-    }\r
-\r
-    /* If we get here, then some of the settings we read from the\r
-       configuration actually came from the schema.  In other words,\r
-       the values weren't really defined for this identity.  So we now\r
-       have to read the values from the krb5 configuration file. */\r
-\r
-    if (!khm_krb5_get_profile_file(confname, sizeof(confname))) {\r
-        profile_t profile;\r
-        const char * filenames[2];\r
-        long retval;\r
-\r
-        filenames[0] = confname;\r
-        filenames[1] = NULL;\r
-\r
-        if (!pprofile_init(filenames, &profile)) {\r
-\r
-            /* default ticket lifetime */\r
-            if (!(regf & K5PARAM_F_LIFE)) {\r
-                char * value = NULL;\r
-                retval = get_libdefault_string(profile, realmname,\r
-                                               "ticket_lifetime", &value);\r
-\r
-                if (retval == 0 && value) {\r
-                    krb5_deltat d;\r
-\r
-                    retval = pkrb5_string_to_deltat(value, &d);\r
-                    if (retval == KRB5_DELTAT_BADFORMAT) {\r
-                        /* Historically some sites use relations of\r
-                           the form 'ticket_lifetime = 24000' where\r
-                           the unit is left out but is assumed to be\r
-                           seconds. Then there are other sites which\r
-                           use the form 'ticket_lifetime = 600' where\r
-                           the unit is assumed to be minutes.  While\r
-                           these are technically wrong (a unit needs\r
-                           to be specified), we try to accomodate for\r
-                           this using the safe assumption that the\r
-                           unit is seconds and tack an 's' to the end\r
-                           and see if that works. */\r
-\r
-                        size_t cch;\r
-                        char tmpbuf[256];\r
-                        char * buf;\r
-\r
-                        do {\r
-                            if (FAILED(StringCchLengthA(value, 1024 /* unresonably large size */,\r
-                                                        &cch)))\r
-                                break;\r
-\r
-                            cch += sizeof(char) * 2; /* NULL and new 's' */\r
-                            if (cch > ARRAYLENGTH(tmpbuf))\r
-                                buf = PMALLOC(cch * sizeof(char));\r
-                            else\r
-                                buf = tmpbuf;\r
-\r
-                            StringCchCopyA(buf, cch, value);\r
-                            StringCchCatA(buf, cch, "s");\r
-\r
-                            retval = pkrb5_string_to_deltat(buf, &d);\r
-                            if (retval == 0) {\r
-                                p->lifetime = d;\r
-                                proff |= K5PARAM_F_LIFE;\r
-                            }\r
-\r
-                            if (buf != tmpbuf)\r
-                                PFREE(buf);\r
-\r
-                        } while(0);\r
-\r
-                    } else if (retval == 0) {\r
-                        p->lifetime = d;\r
-                        proff |= K5PARAM_F_LIFE;\r
-                    }\r
-\r
-                    PFREE(value);\r
-                }\r
-            }\r
-\r
-            if (!(regf & K5PARAM_F_RLIFE)) {\r
-                char * value = NULL;\r
-                retval = get_libdefault_string(profile, realmname,\r
-                                               "renew_lifetime", &value);\r
-                if (retval == 0 && value) {\r
-                    krb5_deltat d;\r
-\r
-                    retval = pkrb5_string_to_deltat(value, &d);\r
-                    if (retval == 0) {\r
-                        p->renew_life = d;\r
-                        proff |= K5PARAM_F_RLIFE;\r
-                    }\r
-                    PFREE(value);\r
-                }\r
-            }\r
-\r
-            if (!(regf & K5PARAM_F_FORW)) {\r
-                char * value = NULL;\r
-                retval = get_libdefault_string(profile, realmname,\r
-                                               "forwardable", &value);\r
-                if (retval == 0 && value) {\r
-                    khm_boolean b;\r
-\r
-                    if (!khm_krb5_parse_boolean(value, &b))\r
-                        p->forwardable = b;\r
-                    else\r
-                        p->forwardable = FALSE;\r
-                    PFREE(value);\r
-                    proff |= K5PARAM_F_FORW;\r
-                }\r
-            }\r
-\r
-            if (!(regf & K5PARAM_F_RENEW)) {\r
-                char * value = NULL;\r
-                retval = get_libdefault_string(profile, realmname,\r
-                                               "renewable", &value);\r
-                if (retval == 0 && value) {\r
-                    khm_boolean b;\r
-\r
-                    if (!khm_krb5_parse_boolean(value, &b))\r
-                        p->renewable = b;\r
-                    else\r
-                        p->renewable = TRUE;\r
-                    PFREE(value);\r
-                    proff |= K5PARAM_F_RENEW;\r
-                }\r
-            }\r
-\r
-            if (!(regf & K5PARAM_F_ADDL)) {\r
-                char * value = NULL;\r
-                retval = get_libdefault_string(profile, realmname,\r
-                                               "noaddresses", &value);\r
-                if (retval == 0 && value) {\r
-                    khm_boolean b;\r
-\r
-                    if (!khm_krb5_parse_boolean(value, &b))\r
-                        p->addressless = b;\r
-                    else\r
-                        p->addressless = TRUE;\r
-                    PFREE(value);\r
-                    proff |= K5PARAM_F_ADDL;\r
-                }\r
-            }\r
-\r
-            if (!(regf & K5PARAM_F_PROX)) {\r
-                char * value = NULL;\r
-                retval = get_libdefault_string(profile, realmname,\r
-                                               "proxiable", &value);\r
-                if (retval == 0 && value) {\r
-                    khm_boolean b;\r
-\r
-                    if (!khm_krb5_parse_boolean(value, &b))\r
-                        p->proxiable = b;\r
-                    else\r
-                        p->proxiable = FALSE;\r
-                    PFREE(value);\r
-                    proff |= K5PARAM_F_PROX;\r
-                }\r
-            }\r
-\r
-            pprofile_release(profile);\r
-        }\r
-    }\r
-\r
-    p->source_reg = regf;\r
-    p->source_prof = proff;\r
-\r
-    return rv;\r
-}\r
-\r
-/* Note that p->source_reg and p->source_prof is used in special ways\r
-   here.  All fields that are flagged in source_reg will be written to\r
-   the configuration (if they are different from what\r
-   khm_krb5_get_identity_params() reports).  All fields that are\r
-   flagged in source_prof will be removed from the configuration\r
-   (thereby exposing the value defined in the profile file). */\r
-khm_int32\r
-khm_krb5_set_identity_params(khm_handle ident, const k5_params * p) {\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    khm_handle csp_id = NULL;\r
-    k5_params p_s;\r
-    khm_int32 source_reg = p->source_reg;\r
-    khm_int32 source_prof = p->source_prof;\r
-\r
-    rv = khm_krb5_get_identity_config(ident,\r
-                                      KHM_PERM_WRITE | KHM_FLAG_CREATE |\r
-                                      KCONF_FLAG_WRITEIFMOD,\r
-                                      &csp_id);\r
-    if (KHM_FAILED(rv))\r
-        goto done_reg;\r
-\r
-    khm_krb5_get_identity_params(ident, &p_s);\r
-\r
-    /* Remove any bits that don't make sense.  Not all values can be\r
-       specified in the profile file. */\r
-    source_prof &= K5PARAM_FM_PROF;\r
-\r
-    /* if a flag appears in both source_prof and source_reg, remove\r
-       the flag from source_reg. */\r
-    source_reg &= ~source_prof;\r
-\r
-    /* we only write values that have changed, and that are flagged in\r
-       source_reg */\r
-\r
-    if ((source_reg & K5PARAM_F_RENEW) &&\r
-        !!p_s.renewable != !!p->renewable)\r
-        khc_write_int32(csp_id, L"Renewable", !!p->renewable);\r
-\r
-    if ((source_reg & K5PARAM_F_FORW) &&\r
-        !!p_s.forwardable != !!p->forwardable)\r
-        khc_write_int32(csp_id, L"Forwardable", !!p->forwardable);\r
-\r
-    if ((source_reg & K5PARAM_F_PROX) &&\r
-        !!p_s.proxiable != !!p->proxiable)\r
-        khc_write_int32(csp_id, L"Proxiable", !!p->proxiable);\r
-\r
-    if ((source_reg & K5PARAM_F_ADDL) &&\r
-        !!p_s.addressless != !!p->addressless)\r
-        khc_write_int32(csp_id, L"Addressless", !!p->addressless);\r
-\r
-    if ((source_reg & K5PARAM_F_PUBIP) &&\r
-        p_s.publicIP != p->publicIP)\r
-        khc_write_int32(csp_id, L"PublicIP", p->publicIP);\r
-\r
-    if ((source_reg & K5PARAM_F_LIFE) &&\r
-        p_s.lifetime != p->lifetime)\r
-        khc_write_int32(csp_id, L"DefaultLifetime", p->lifetime);\r
-\r
-    if ((source_reg & K5PARAM_F_LIFE_H) &&\r
-        p_s.lifetime_max != p->lifetime_max)\r
-        khc_write_int32(csp_id, L"MaxLifetime", p->lifetime_max);\r
-\r
-    if ((source_reg & K5PARAM_F_LIFE_L) &&\r
-        p_s.lifetime_min != p->lifetime_min)\r
-        khc_write_int32(csp_id, L"MinLifetime", p->lifetime_min);\r
-\r
-    if ((source_reg & K5PARAM_F_RLIFE) &&\r
-        p_s.renew_life != p->renew_life)\r
-        khc_write_int32(csp_id, L"DefaultRenewLifetime", p->renew_life);\r
-\r
-    if ((source_reg & K5PARAM_F_RLIFE_H) &&\r
-        p_s.renew_life_max != p->renew_life_max)\r
-        khc_write_int32(csp_id, L"MaxRenewLifetime", p->renew_life_max);\r
-\r
-    if ((source_reg & K5PARAM_F_RLIFE_L) &&\r
-        p_s.renew_life_min != p->renew_life_min)\r
-        khc_write_int32(csp_id, L"MinRenewLifetime", p->renew_life_min);\r
-\r
-    /* and now, remove the values that are present in source_prof.\r
-       Not all values are removed since not all values can be\r
-       specified in the profile file. */\r
-    if (source_prof & K5PARAM_F_RENEW)\r
-        khc_remove_value(csp_id, L"Renewable", 0);\r
-\r
-    if (source_prof & K5PARAM_F_FORW)\r
-        khc_remove_value(csp_id, L"Forwardable", 0);\r
-\r
-    if (source_prof & K5PARAM_F_PROX)\r
-        khc_remove_value(csp_id, L"Proxiable", 0);\r
-\r
-    if (source_prof & K5PARAM_F_ADDL)\r
-        khc_remove_value(csp_id, L"Addressless", 0);\r
-\r
-    if (source_prof & K5PARAM_F_LIFE)\r
-        khc_remove_value(csp_id, L"DefaultLifetime", 0);\r
-\r
-    if (source_prof & K5PARAM_F_RLIFE)\r
-        khc_remove_value(csp_id, L"DefaultRenewLifetime", 0);\r
-\r
- done_reg:\r
-    if (csp_id != NULL)\r
-        khc_close_space(csp_id);\r
-\r
-    return rv;\r
-}\r
-\r
-static const char *const conf_yes[] = {\r
-    "y", "yes", "true", "t", "1", "on",\r
-    0,\r
-};\r
-\r
-static const char *const conf_no[] = {\r
-    "n", "no", "false", "nil", "0", "off",\r
-    0,\r
-};\r
-\r
-int\r
-khm_krb5_parse_boolean(const char *s, khm_boolean * b)\r
-{\r
-    const char *const *p;\r
-\r
-    for(p=conf_yes; *p; p++) {\r
-        if (!_stricmp(*p,s)) {\r
-            *b = TRUE;\r
-            return 0;\r
-        }\r
-    }\r
-\r
-    for(p=conf_no; *p; p++) {\r
-        if (!_stricmp(*p,s)) {\r
-            *b = FALSE;\r
-            return 0;\r
-        }\r
-    }\r
-\r
-    /* Default to "no" */\r
-    return KHM_ERROR_INVALID_PARAM;\r
-}\r
+/*
+* Copyright (c) 2005 Massachusetts Institute of Technology
+* Copyright (c) 2006,2007 Secure Endpoints Inc.
+*
+* Permission is hereby granted, free of charge, to any person
+* obtaining a copy of this software and associated documentation
+* files (the "Software"), to deal in the Software without
+* restriction, including without limitation the rights to use, copy,
+* modify, merge, publish, distribute, sublicense, and/or sell copies
+* of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+* ACTION OF 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 <string.h>
+#include <time.h>
+#include <stdlib.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 *) PMALLOC(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));
+    PFREE(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
+
+/* we use these structures to keep track of identities that we find
+   while going through the API, FILE and MSLSA caches and enumerating
+   credentials.  The only identities we want to keep track of are the
+   ones that have an initial ticket.  We collect information for each
+   of the identities we find that we have initial tickets for and
+   then set the properties for the identities at once. */
+
+typedef struct tag_ident_data {
+    khm_handle  ident;          /* handle to the identity */
+    khm_int32 count;            /* number of initial tickets we have
+                                   found for this identity. */
+    wchar_t   ccname[MAX_PATH];
+    FILETIME  ft_issue;
+    FILETIME  ft_expire;
+    FILETIME  ft_renewexpire;
+    khm_int32 krb5_flags;
+} ident_data;
+
+typedef struct tag_identlist {
+    ident_data * list;
+    khm_size     n_list;
+    khm_size     nc_list;
+} identlist;
+
+#define IDLIST_ALLOC_INCR 8
+
+static void
+tc_prep_idlist(identlist * idlist) {
+    khm_int32 rv;
+    khm_size cb_ids = 0;
+    khm_size n_ids = 0;
+    khm_size i;
+    wchar_t * ids = NULL;
+    wchar_t *thisid;
+
+    idlist->list = NULL;
+    idlist->n_list = 0;
+    idlist->nc_list = 0;
+
+    do {
+
+        if (ids) {
+            PFREE(ids);
+            ids = NULL;
+        }
+
+        rv = kcdb_identity_enum(KCDB_IDENT_FLAG_ACTIVE,
+                                KCDB_IDENT_FLAG_ACTIVE,
+                                NULL,
+                                &cb_ids,
+                                &n_ids);
+
+        if (rv != KHM_ERROR_TOO_LONG)
+            break;              /* something else is wrong */
+
+        if (n_ids == 0 || cb_ids == 0)
+            break;              /* no identities to process */
+
+#ifdef DEBUG
+        assert(cb_ids > 0);
+#endif
+
+        ids = PMALLOC(cb_ids);
+#ifdef DEBUG
+        assert(ids != NULL);
+#endif
+        if (ids == NULL)
+            break;
+
+        rv = kcdb_identity_enum(KCDB_IDENT_FLAG_ACTIVE,
+                                KCDB_IDENT_FLAG_ACTIVE,
+                                ids,
+                                &cb_ids,
+                                &n_ids);
+
+        if (KHM_SUCCEEDED(rv))
+            break;
+
+    } while (TRUE);
+
+    if (ids == NULL)
+        return;
+
+    if (KHM_FAILED(rv) || n_ids == 0) {
+        if (ids)
+            PFREE(ids);
+        return;
+    }
+
+    idlist->nc_list = UBOUNDSS(n_ids, IDLIST_ALLOC_INCR, IDLIST_ALLOC_INCR);
+
+    idlist->list = PCALLOC(idlist->nc_list, sizeof(idlist->list[0]));
+
+    for (i = 0, thisid = ids;
+         thisid && thisid[0];
+         thisid = multi_string_next(thisid)) {
+
+        khm_handle ident;
+
+        rv = kcdb_identity_create(thisid, 0, &ident);
+
+        if (KHM_FAILED(rv))
+            continue;
+
+        idlist->list[i].ident = ident;
+        idlist->list[i].count = 0;
+
+        i++;
+    }
+
+    idlist->n_list = i;
+
+    PFREE(ids);
+}
+
+static ident_data *
+tc_add_ident_to_list(identlist * idlist, khm_handle ident) {
+    khm_size i;
+    ident_data * d;
+
+    for (i=0; i < idlist->n_list; i++) {
+        if (kcdb_identity_is_equal(ident, idlist->list[i].ident))
+            break;
+    }
+
+    if (i < idlist->n_list) {
+        /* we already have this identity on our list.  Increment the
+           count */
+        idlist->list[i].count++;
+        return &idlist->list[i];
+    }
+
+    /* it wasn't in our list.  Add it */
+
+    if (idlist->n_list + 1 > idlist->nc_list) {
+        idlist->nc_list = UBOUNDSS(idlist->n_list + 1,
+                                   IDLIST_ALLOC_INCR,
+                                   IDLIST_ALLOC_INCR);
+#ifdef DEBUG
+        assert(idlist->n_list + 1 <= idlist->nc_list);
+#endif
+        idlist->list = PREALLOC(idlist->list,
+                                sizeof(idlist->list[0]) * idlist->nc_list);
+#ifdef DEBUG
+        assert(idlist->list);
+#endif
+        ZeroMemory(&idlist->list[idlist->n_list],
+                   sizeof(idlist->list[0]) *
+                   (idlist->nc_list - idlist->n_list));
+    }
+
+    d = &idlist->list[idlist->n_list];
+
+    ZeroMemory(d, sizeof(*d));
+
+    d->ident = ident;
+    d->count = 1;
+
+    idlist->n_list++;
+
+    kcdb_identity_hold(ident);
+
+    return d;
+}
+
+static void
+tc_set_ident_data(identlist * idlist) {
+    khm_size i;
+    wchar_t k5idtype[KCDB_MAXCCH_NAME];
+
+    k5idtype[0] = L'\0';
+    LoadString(hResModule, IDS_KRB5_NC_NAME,
+               k5idtype, ARRAYLENGTH(k5idtype));
+
+    for (i=0; i < idlist->n_list; i++) {
+#ifdef DEBUG
+        assert(idlist->list[i].ident);
+#endif
+
+        if (idlist->list[i].count > 0) {
+            khm_int32 t;
+
+            t = credtype_id_krb5;
+            kcdb_identity_set_attr(idlist->list[i].ident,
+                                   KCDB_ATTR_TYPE,
+                                   &t,
+                                   sizeof(t));
+
+            /* We need to manually add the type name if we want the
+               name to show up in the property list for the identity.
+               The type name is only automatically calculated for
+               credentials. */
+            kcdb_identity_set_attr(idlist->list[i].ident,
+                                   KCDB_ATTR_TYPE_NAME,
+                                   k5idtype,
+                                   KCDB_CBSIZE_AUTO);
+
+            kcdb_identity_set_attr(idlist->list[i].ident,
+                                   attr_id_krb5_ccname,
+                                   idlist->list[i].ccname,
+                                   KCDB_CBSIZE_AUTO);
+
+            kcdb_identity_set_attr(idlist->list[i].ident,
+                                   KCDB_ATTR_EXPIRE,
+                                   &idlist->list[i].ft_expire,
+                                   sizeof(idlist->list[i].ft_expire));
+
+            kcdb_identity_set_attr(idlist->list[i].ident,
+                                   KCDB_ATTR_ISSUE,
+                                   &idlist->list[i].ft_issue,
+                                   sizeof(idlist->list[i].ft_issue));
+
+            kcdb_identity_set_attr(idlist->list[i].ident,
+                                   attr_id_krb5_flags,
+                                   &idlist->list[i].krb5_flags,
+                                   sizeof(idlist->list[i].krb5_flags));
+
+            if (idlist->list[i].ft_renewexpire.dwLowDateTime == 0 &&
+                idlist->list[i].ft_renewexpire.dwHighDateTime == 0) {
+                kcdb_identity_set_attr(idlist->list[i].ident,
+                                       KCDB_ATTR_RENEW_EXPIRE,
+                                       NULL, 0);
+            } else {
+                kcdb_identity_set_attr(idlist->list[i].ident,
+                                       KCDB_ATTR_RENEW_EXPIRE,
+                                       &idlist->list[i].ft_renewexpire,
+                                       sizeof(idlist->list[i].ft_renewexpire));
+            }
+
+        } else {
+            /* We didn't see any TGTs for this identity.  We have to
+               remove all the Krb5 supplied properties. */
+
+            khm_int32 t;
+            khm_size cb;
+
+            cb = sizeof(t);
+            if (KHM_SUCCEEDED(kcdb_identity_get_attr(idlist->list[i].ident,
+                                                     KCDB_ATTR_TYPE, NULL,
+                                                     &t,
+                                                     &cb)) &&
+                t == credtype_id_krb5) {
+
+                /* disown this and remove all our properties. the
+                   system will GC this identity if nobody claims it.*/
+
+                kcdb_identity_set_attr(idlist->list[i].ident,
+                                       KCDB_ATTR_TYPE, NULL, 0);
+                kcdb_identity_set_attr(idlist->list[i].ident,
+                                       KCDB_ATTR_TYPE_NAME, NULL, 0);
+                kcdb_identity_set_attr(idlist->list[i].ident,
+                                       attr_id_krb5_ccname, NULL, 0);
+                kcdb_identity_set_attr(idlist->list[i].ident,
+                                       KCDB_ATTR_EXPIRE, NULL, 0);
+                kcdb_identity_set_attr(idlist->list[i].ident,
+                                       KCDB_ATTR_ISSUE, NULL, 0);
+                kcdb_identity_set_attr(idlist->list[i].ident,
+                                       attr_id_krb5_flags, NULL, 0);
+                kcdb_identity_set_attr(idlist->list[i].ident,
+                                       KCDB_ATTR_RENEW_EXPIRE, NULL, 0);
+            } else {
+                /* otherwise, this identity doesn't belong to us.  We
+                   should leave it as is. */
+            }
+        }
+    }
+}
+
+static void
+tc_free_idlist(identlist * idlist) {
+    khm_size i;
+
+    for (i=0; i < idlist->n_list; i++) {
+        if (idlist->list[i].ident != NULL) {
+            kcdb_identity_release(idlist->list[i].ident);
+            idlist->list[i].ident = NULL;
+        }
+    }
+
+    if (idlist->list)
+        PFREE(idlist->list);
+    idlist->list = NULL;
+    idlist->n_list = 0;
+    idlist->nc_list = 0;
+}
+
+#ifndef ENCTYPE_LOCAL_RC4_MD4
+#define ENCTYPE_LOCAL_RC4_MD4    0xFFFFFF80
+#endif
+
+#define MAX_ADDRS 256
+
+static long get_tickets_from_cache(krb5_context ctx, 
+                                   krb5_ccache cache,
+                                   identlist * idlist)
+{
+    krb5_error_code code;
+    krb5_principal  KRBv5Principal;
+    krb5_flags     flags = 0;
+    krb5_cc_cursor  KRBv5Cursor;
+    krb5_creds     KRBv5Credentials;
+    krb5_ticket    *tkt=NULL;
+    char          *ClientName = NULL;
+    char          *PrincipalName = NULL;
+    wchar_t         wbuf[256];  /* temporary conversion buffer */
+    wchar_t         wcc_name[KRB5_MAXCCH_CCNAME]; /* credential cache name */
+    char          *sServerName = NULL;
+    khm_handle      ident = NULL;
+    khm_handle      cred = NULL;
+    time_t          tt;
+    FILETIME        ft, eft;
+    khm_int32       ti;
+
+#ifdef KRB5_TC_NOTICKET
+    flags = KRB5_TC_NOTICKET;
+#else
+    flags = 0;
+#endif
+
+    {
+        const char * cc_name;
+        const char * cc_type;
+
+        cc_name = (*pkrb5_cc_get_name)(ctx, cache);
+        if(cc_name) {
+            cc_type = (*pkrb5_cc_get_type)(ctx, cache);
+            if (cc_type) {
+                StringCbPrintf(wcc_name, sizeof(wcc_name), L"%S:%S", cc_type, cc_name);
+            } else {
+                AnsiStrToUnicode(wcc_name, sizeof(wcc_name), cc_name);
+                khm_krb5_canon_cc_name(wcc_name, sizeof(wcc_name));
+            }
+        } else {
+            cc_type = (*pkrb5_cc_get_type)(ctx, cache);
+            if (cc_type) {
+                StringCbPrintf(wcc_name, sizeof(wcc_name), L"%S:", cc_type);
+            } else {
+#ifdef DEBUG
+                assert(FALSE);
+#endif
+                StringCbCopy(wcc_name, sizeof(wcc_name), L"");
+            }
+        }
+    }
+
+    _reportf(L"Getting tickets from cache [%s]", wcc_name);
+
+    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;
+    }
+
+    _reportf(L"Found principal [%s]", wbuf);
+
+    (*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;
+        khm_int32 cred_flags = 0;
+
+        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, &ft);
+        kcdb_cred_set_attr(cred, KCDB_ATTR_ISSUE, &ft, sizeof(ft));
+
+        tt = KRBv5Credentials.times.endtime;
+        TimetToFileTime(tt, &eft);
+        kcdb_cred_set_attr(cred, KCDB_ATTR_EXPIRE, &eft, sizeof(eft));
+
+        {
+            FILETIME ftl;
+
+            ftl = FtSub(&eft, &ft);
+            kcdb_cred_set_attr(cred, KCDB_ATTR_LIFETIME, &ftl, sizeof(ftl));
+        }
+
+        if (KRBv5Credentials.times.renew_till > 0) {
+            FILETIME ftl;
+
+            tt = KRBv5Credentials.times.renew_till;
+            TimetToFileTime(tt, &eft);
+            kcdb_cred_set_attr(cred, KCDB_ATTR_RENEW_EXPIRE, &eft, 
+                               sizeof(eft));
+
+
+            ftl = FtSub(&eft, &ft);
+            kcdb_cred_set_attr(cred, KCDB_ATTR_RENEW_LIFETIME, &ftl, 
+                               sizeof(ftl));
+        }
+
+        ti = KRBv5Credentials.ticket_flags;
+        kcdb_cred_set_attr(cred, attr_id_krb5_flags, &ti, sizeof(ti));
+
+        /* special flags understood by NetIDMgr */
+        {
+            khm_int32 nflags = 0;
+
+            if (ti & TKT_FLG_RENEWABLE)
+                nflags |= KCDB_CRED_FLAG_RENEWABLE;
+            if (ti & TKT_FLG_INITIAL)
+                nflags |= KCDB_CRED_FLAG_INITIAL;
+           else {
+               krb5_data * c0, *c1, *r;
+
+               /* these are macros that do not allocate any memory */
+               c0 = krb5_princ_component(ctx,KRBv5Credentials.server,0);
+               c1 = krb5_princ_component(ctx,KRBv5Credentials.server,1);
+               r  = krb5_princ_realm(ctx,KRBv5Credentials.server);
+
+               if ( c0 && c1 && r && c1->length == r->length && 
+                    !strncmp(c1->data,r->data,r->length) &&
+                    !strncmp("krbtgt",c0->data,c0->length) )
+                   nflags |= KCDB_CRED_FLAG_INITIAL;
+           }
+
+            kcdb_cred_set_flags(cred, nflags, KCDB_CRED_FLAGMASK_EXT);
+
+            cred_flags = nflags;
+        }
+
+        if ( !pkrb5_decode_ticket(&KRBv5Credentials.ticket, &tkt)) {
+            ti = tkt->enc_part.enctype;
+            kcdb_cred_set_attr(cred, attr_id_tkt_enctype, &ti, sizeof(ti));
+            ti = tkt->enc_part.kvno;
+            kcdb_cred_set_attr(cred, attr_id_kvno, &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);
+
+        if ( KRBv5Credentials.addresses && KRBv5Credentials.addresses[0] ) {
+           khm_int32 buffer[1024];
+           void * bufp;
+           khm_size cb;
+           khm_int32 rv;
+
+           bufp = (void *) buffer;
+           cb = sizeof(buffer);
+
+           rv = serialize_krb5_addresses(KRBv5Credentials.addresses,
+                                         bufp,
+                                         &cb);
+           if (rv == KHM_ERROR_TOO_LONG) {
+               bufp = PMALLOC(cb);
+               rv = serialize_krb5_addresses(KRBv5Credentials.addresses,
+                                             bufp,
+                                             &cb);
+           }
+
+           if (KHM_SUCCEEDED(rv)) {
+               kcdb_cred_set_attr(cred, attr_id_addr_list,
+                                  bufp, cb);
+           }
+
+           if (bufp != (void *) buffer)
+               PFREE(bufp);
+        }
+
+        if(cred_flags & KCDB_CRED_FLAG_INITIAL) {
+            FILETIME ft_issue_new;
+            FILETIME ft_expire_old;
+            FILETIME ft_expire_new;
+            ident_data * d;
+
+            /* an initial ticket!  Add it to the list of identities we
+               have seen so far with initial tickets. */
+            d = tc_add_ident_to_list(idlist, ident);
+#ifdef DEBUG
+            assert(d);
+            assert(d->count > 0);
+#endif
+
+            tt = KRBv5Credentials.times.endtime;
+            TimetToFileTime(tt, &ft_expire_new);
+
+            tt = KRBv5Credentials.times.starttime;
+            TimetToFileTime(tt, &ft_issue_new);
+
+            /* so now, we have to set the properties of the identity
+               based on the properties of this credential under the
+               following circumstances:
+
+               - If this is the first time we are hitting this
+                 identity.
+
+               - If this is not the MSLSA: cache and the expiry time
+                 for this credential is longer than the time already
+                 found for this identity.
+            */
+
+            ft_expire_old = d->ft_expire;
+
+            if(d->count == 1
+               || (CompareFileTime(&ft_expire_new, &ft_expire_old) > 0 &&
+                   wcscmp(wcc_name, L"MSLSA:") != 0)) {
+
+                _reportf(L"Setting properties for identity (count=%d)", d->count);
+
+                StringCbCopy(d->ccname, sizeof(d->ccname),
+                             wcc_name);
+                d->ft_expire = ft_expire_new;
+                d->ft_issue = ft_issue_new;
+
+                if (KRBv5Credentials.times.renew_till > 0) {
+                    tt = KRBv5Credentials.times.renew_till;
+                    TimetToFileTime(tt, &ft);
+                    d->ft_renewexpire = ft;
+                } else {
+                    ZeroMemory(&d->ft_renewexpire, sizeof(d->ft_renewexpire));
+                }
+
+                d->krb5_flags = KRBv5Credentials.ticket_flags;
+            }
+        }
+
+        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:
+
+    return code;
+}
+
+long
+khm_krb5_list_tickets(krb5_context *krbv5Context)
+{
+    krb5_context       ctx = NULL;
+    krb5_ccache                cache = NULL;
+    krb5_error_code    code = 0;
+    apiCB *             cc_ctx = NULL;
+    struct _infoNC **   pNCi = NULL;
+    int                 i;
+    khm_int32           t;
+    wchar_t *           ms = NULL;
+    khm_size            cb;
+    identlist           idl;
+
+    kcdb_credset_flush(krb5_credset);
+    tc_prep_idlist(&idl);
+
+    if((*krbv5Context == 0) && (code = (*pkrb5_init_context)(krbv5Context))) {
+        goto _exit;
+    }
+
+    ctx = (*krbv5Context);
+
+    if (!pcc_initialize ||
+        !pcc_get_NC_info ||
+        !pcc_free_NC_info ||
+        !pcc_shutdown)
+        goto _skip_cc_iter;
+
+    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;
+
+    for(i=0; pNCi[i]; i++) {
+        char ccname[KRB5_MAXCCH_CCNAME];
+
+        if (pNCi[i]->vers != CC_CRED_V5)
+            continue;
+
+        if (FAILED(StringCchPrintfA(ccname, sizeof(ccname), "API:%s",
+                                    pNCi[i]->name)))
+            continue;
+
+        code = (*pkrb5_cc_resolve)(ctx, ccname, &cache);
+
+        if (code)
+            continue;
+
+        code = get_tickets_from_cache(ctx, cache, &idl);
+
+        if(ctx != NULL && cache != NULL)
+            (*pkrb5_cc_close)(ctx, cache);
+
+        cache = 0;
+    }
+
+ _skip_cc_iter:
+
+    if (khc_read_multi_string(csp_params, L"FileCCList", NULL, &cb)
+        == KHM_ERROR_TOO_LONG &&
+        cb > sizeof(wchar_t) * 2) {
+        wchar_t * t;
+        char ccname[MAX_PATH + 6];
+
+        ms = PMALLOC(cb);
+#ifdef DEBUG
+        assert(ms);
+#endif
+        khc_read_multi_string(csp_params, L"FileCCList", ms, &cb);
+
+        for(t = ms; t && *t; t = multi_string_next(t)) {
+            StringCchPrintfA(ccname, ARRAYLENGTH(ccname),
+                             "FILE:%S", t);
+
+            code = (*pkrb5_cc_resolve)(ctx, ccname, &cache);
+
+            if (code)
+                continue;
+
+            code = get_tickets_from_cache(ctx, cache, &idl);
+
+            if (ctx != NULL && cache != NULL)
+                (*pkrb5_cc_close)(ctx, cache);
+            cache = 0;
+        }
+
+        PFREE(ms);
+    }
+
+    if (KHM_SUCCEEDED(khc_read_int32(csp_params, L"MsLsaList", &t)) && t) {
+        code = (*pkrb5_cc_resolve)(ctx, "MSLSA:", &cache);
+
+        if (code == 0 && cache) {
+            code = get_tickets_from_cache(ctx, cache, &idl);
+        }
+
+        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);
+    tc_set_ident_data(&idl);
+    tc_free_idlist(&idl);
+
+    return(code);
+}
+
+int
+khm_krb5_renew_cred(khm_handle cred)
+{
+    khm_handle          identity = NULL;
+    krb5_error_code     code = 0;
+    krb5_context        ctx = NULL;
+    krb5_ccache         cc = NULL;
+    krb5_principal     me = NULL, server = NULL;
+    krb5_creds          in_creds, cc_creds;
+    krb5_creds         * out_creds = NULL;
+
+    wchar_t            wname[512];
+    khm_size           cbname;
+    char                name[512];
+    khm_boolean                brenewIdentity = FALSE;
+    khm_boolean                istgt = FALSE;
+
+    khm_int32           flags;
+
+    cbname = sizeof(wname);
+    kcdb_cred_get_name(cred, wname, &cbname);
+    _reportf(L"Krb5 renew cred for %s", wname);
+
+    kcdb_cred_get_flags(cred, &flags);
+
+    if (!(flags & KCDB_CRED_FLAG_INITIAL)) {
+        _reportf(L"Krb5 skipping renewal because this is not an initial credential");
+        return 0;
+    }
+
+    memset(&in_creds, 0, sizeof(in_creds));
+    memset(&cc_creds, 0, sizeof(cc_creds));
+
+    if (cred == NULL) {
+#ifdef DEBUG
+       assert(FALSE);
+#endif
+       goto cleanup;
+    }
+
+    if (KHM_FAILED(kcdb_cred_get_identity(cred, &identity))) {
+#ifdef DEBUG
+       assert(FALSE);
+#endif
+       goto cleanup;
+    }
+
+    code = khm_krb5_initialize(identity, &ctx, &cc);
+    if (code)
+       goto cleanup;
+
+    code = pkrb5_cc_get_principal(ctx, cc, &me);
+    if (code) 
+        goto cleanup;
+
+    cbname = sizeof(wname);
+    if (KHM_FAILED(kcdb_cred_get_name(cred, wname, &cbname)))
+       goto cleanup;
+
+    UnicodeStrToAnsi(name, sizeof(name), wname);
+
+    code = pkrb5_parse_name(ctx, name, &server);
+    if (code)
+       goto cleanup;
+
+    in_creds.client = me;
+    in_creds.server = server;
+
+#ifdef KRB5_TC_NOTICKET
+    pkrb5_cc_set_flags(ctx, cc, 0);
+#endif
+
+    if (strlen("krbtgt") != krb5_princ_name(ctx, server)->length ||
+        strncmp("krbtgt", krb5_princ_name(ctx, server)->data, krb5_princ_name(ctx, server)->length)) 
+    {
+       code = pkrb5_get_renewed_creds(ctx, &cc_creds, me, cc, name);
+       if (code) {
+           code = pkrb5_cc_retrieve_cred(ctx, cc, 0, &in_creds, &cc_creds);
+           if (code == 0) {
+               code = pkrb5_cc_remove_cred(ctx, cc, 0, &cc_creds);
+               if (code) {
+                   brenewIdentity = TRUE;
+                   goto cleanup;
+               }
+           }
+       }
+
+       code = pkrb5_get_credentials(ctx, 0, cc, &in_creds, &out_creds);
+    } else {
+       istgt = TRUE;
+       code = pkrb5_get_renewed_creds(ctx, &cc_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;
+    }
+
+    if (istgt) {
+       code = pkrb5_cc_initialize(ctx, cc, me);
+       if (code) goto cleanup;
+
+    }
+
+    code = pkrb5_cc_store_cred(ctx, cc, istgt ? &cc_creds : out_creds);
+    if (code) goto cleanup;
+
+
+ cleanup:
+
+    if (in_creds.client == me)
+        in_creds.client = NULL;
+    if (in_creds.server == server)
+        in_creds.server = NULL;
+
+    if (me)
+       pkrb5_free_principal(ctx, me);
+
+    if (server)
+       pkrb5_free_principal(ctx, server);
+
+    pkrb5_free_cred_contents(ctx, &in_creds);
+    pkrb5_free_cred_contents(ctx, &cc_creds);                        
+
+    if (out_creds)
+       pkrb5_free_creds(ctx, out_creds);
+
+    if (cc && ctx)
+       pkrb5_cc_close(ctx, cc);
+
+    if (ctx)
+       pkrb5_free_context(ctx);
+
+    if (identity) {
+       if (brenewIdentity)
+           code = khm_krb5_renew_ident(identity);
+       kcdb_identity_release(identity);
+    }
+
+    return code;
+}
+
+int
+khm_krb5_renew_ident(khm_handle identity)
+{
+    krb5_error_code     code = 0;
+    krb5_context        ctx = NULL;
+    krb5_ccache         cc = NULL;
+    krb5_principal      me = NULL;
+    krb5_principal      server = NULL;
+    krb5_creds          my_creds;
+    krb5_data           *realm = NULL;
+    wchar_t             idname[KCDB_IDENT_MAXCCH_NAME];
+    khm_size            cb;
+    khm_int32           k5_flags;
+
+    memset(&my_creds, 0, sizeof(krb5_creds));
+
+    if ( !pkrb5_init_context )
+        goto cleanup;
+
+    cb = sizeof(idname);
+    kcdb_identity_get_name(identity, idname, &cb);
+
+    if (khm_krb5_get_identity_flags(identity) & K5IDFLAG_IMPORTED) {
+#ifndef NO_REIMPORT_MSLSA_CREDS
+        /* we are trying to renew the identity that was imported from
+           MSLSA: */
+        BOOL  imported;
+        BOOL retry_import = FALSE;
+        char  cidname[KCDB_IDENT_MAXCCH_NAME];
+        khm_handle imported_id = NULL;
+        khm_size cb;
+        FILETIME ft_expire;
+        FILETIME ft_now;
+        FILETIME ft_threshold;
+        krb5_principal princ = NULL;
+
+        UnicodeStrToAnsi(cidname, sizeof(cidname), idname);
+
+        imported = khm_krb5_ms2mit(cidname, FALSE, TRUE, &imported_id);
+
+        if (imported == 0)
+            goto import_failed;
+
+        /* if the imported identity has already expired or will soon,
+           we clear the cache and try again. */
+        khm_krb5_list_tickets(&ctx);
+
+        cb = sizeof(ft_expire);
+        if (KHM_FAILED(kcdb_identity_get_attr(imported_id, KCDB_ATTR_EXPIRE,
+                                              NULL, &ft_expire, &cb)))
+            goto import_failed;
+
+        GetSystemTimeAsFileTime(&ft_now);
+        TimetToFileTimeInterval(5 * 60, &ft_threshold);
+
+        ft_now = FtAdd(&ft_now, &ft_threshold);
+
+        if (CompareFileTime(&ft_expire, &ft_now) < 0) {
+            /* the ticket lifetime is not long enough */
+
+            code = 0;
+
+            if (ctx == NULL)
+                code = pkrb5_init_context(&ctx);
+            if (code)
+                goto import_failed;
+
+            code = pkrb5_cc_resolve(ctx, "MSLSA:", &cc);
+            if (code)
+                goto import_failed;
+
+            code = pkrb5_cc_get_principal(ctx, cc, &princ);
+            if (code)
+                goto import_failed;
+
+            pkrb5_cc_initialize(ctx, cc, princ);
+
+            retry_import = TRUE;
+        }
+
+    import_failed:
+
+        if (imported_id) {
+            kcdb_identity_release(imported_id);
+            imported_id = NULL;
+        }
+
+        if (ctx) {
+            if (cc) {
+                pkrb5_cc_close(ctx, cc);
+                cc = NULL;
+            }
+
+            if (princ) {
+                pkrb5_free_principal(ctx, princ);
+                princ = NULL;
+            }
+
+            /* leave ctx so we can use it later */
+        }
+
+        if (retry_import)
+            imported = khm_krb5_ms2mit(cidname, FALSE, TRUE, NULL);
+
+        if (imported)
+            goto cleanup;
+
+        /* if the import failed, then we try to renew the identity via
+           the usual procedure. */
+
+#else
+        /* if we are suppressing further imports from MSLSA, we just
+           skip renewing this identity. */
+        goto cleanup;
+#endif
+    }
+
+    cb = sizeof(k5_flags);
+    if (KHM_SUCCEEDED(kcdb_identity_get_attr(identity,
+                                             attr_id_krb5_flags,
+                                             NULL,
+                                             &k5_flags,
+                                             &cb)) &&
+        !(k5_flags & TKT_FLG_RENEWABLE)) {
+
+        code = KRB5KDC_ERR_BADOPTION;
+        goto cleanup;
+    }
+
+    {
+        FILETIME ft_now;
+        FILETIME ft_exp;
+
+        cb = sizeof(ft_exp);
+        GetSystemTimeAsFileTime(&ft_now);
+        if (KHM_SUCCEEDED(kcdb_identity_get_attr(identity,
+                                                 KCDB_ATTR_EXPIRE,
+                                                 NULL,
+                                                 &ft_exp,
+                                                 &cb)) &&
+            CompareFileTime(&ft_exp, &ft_now) < 0) {
+
+            code = KRB5KRB_AP_ERR_TKT_EXPIRED;
+            goto cleanup;
+
+        }
+    }
+
+    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 = NULL;
+    if (my_creds.server == server)
+        my_creds.server = NULL;
+
+    if (ctx) {
+        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);
+        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 = NULL;
+    krb5_ccache                                cc = NULL;
+    krb5_principal                     me = NULL;
+    char*                       name = NULL;
+    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;
+
+    _reportf(L"In khm_krb5_kinit");
+
+    pkrb5_get_init_creds_opt_init(&options);
+    pkrb5_get_init_creds_opt_set_change_password_prompt(&options, 0);
+
+    memset(&my_creds, 0, sizeof(my_creds));
+
+    if (alt_ctx) {
+        ctx = alt_ctx;
+    } else {
+        code = pkrb5_init_context(&ctx);
+        if (code)
+            goto cleanup;
+    }
+
+    if (ccache) {
+        _reportf(L"Using supplied ccache name %S", ccache);
+        code = pkrb5_cc_resolve(ctx, ccache, &cc);
+    } else {
+       khm_handle identity = NULL;
+       khm_handle csp_ident = NULL;
+       khm_handle csp_k5 = NULL;
+       wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
+       wchar_t wccname[MAX_PATH];
+       char ccname[MAX_PATH];
+       char * pccname = principal_name;
+       khm_size cb;
+
+       idname[0] = L'\0';
+       AnsiStrToUnicode(idname, sizeof(idname), principal_name);
+
+       cb = sizeof(wccname);
+
+       if (KHM_SUCCEEDED(kcdb_identity_create(idname, 0, &identity)) &&
+
+           KHM_SUCCEEDED(kcdb_identity_get_config(identity, 0, &csp_ident)) &&
+
+           KHM_SUCCEEDED(khc_open_space(csp_ident, CSNAME_KRB5CRED, 0,
+                                        &csp_k5)) &&
+
+           KHM_SUCCEEDED(khc_read_string(csp_k5, L"DefaultCCName",
+                                         wccname, &cb)) &&
+
+           cb > sizeof(wchar_t)) {
+
+            _reportf(L"Using DefaultCCName [%s] from identity", wccname);
+
+           UnicodeStrToAnsi(ccname, sizeof(ccname), wccname);
+           pccname = ccname;
+       }
+
+       if (csp_k5)
+           khc_close_space(csp_k5);
+       if (csp_ident)
+           khc_close_space(csp_ident);
+       if (identity)
+           kcdb_identity_release(identity);
+
+        code = pkrb5_cc_resolve(ctx, pccname, &cc);
+    }
+
+    _reportf(L"krb5_cc_resolve returns code %d", code);
+
+    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 {
+       krb5_address ** local_addrs=NULL;
+       DWORD           netIPAddr;
+
+       pkrb5_os_localaddr(ctx, &local_addrs);
+       i = 0;
+       while ( local_addrs[i++] );
+       addr_count = i + 1;
+
+       addrs = (krb5_address **) PMALLOC((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 *)PMALLOC(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 *)PMALLOC(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);
+
+        if (publicIP) {
+            // we are going to add the public IP address specified by the user
+            // to the list provided by the operating system
+            addrs[i] = (krb5_address *)PMALLOC(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 *)PMALLOC(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);
+    _reportf(L"krb5_get_init_creds_password returns code %d", code);
+
+    if (code) goto cleanup;
+
+    code = pkrb5_cc_initialize(ctx, cc, me);
+    _reportf(L"krb5_cc_initialize returns code %d", code);
+    if (code) goto cleanup;
+
+    code = pkrb5_cc_store_cred(ctx, cc, &my_creds);
+    _reportf(L"krb5_cc_store_cred returns code %d", code);
+    if (code) goto cleanup;
+
+cleanup:
+    if ( addrs ) {
+        for ( i=0;i<addr_count;i++ ) {
+            if ( addrs[i] ) {
+                if ( addrs[i]->contents )
+                    PFREE(addrs[i]->contents);
+                PFREE(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) {
+        /* if the colon is just 1 character away from the beginning,
+           it's a FILE: cc */
+        if (colon - wcc_name == 1) {
+            if (cb_len + 5 * sizeof(wchar_t) > cb_cc_name)
+                return KHM_ERROR_TOO_LONG;
+
+            memmove(&wcc_name[5], &wcc_name[0], cb_len);
+            memmove(&wcc_name[0], L"FILE:", sizeof(wchar_t) * 5);
+        }
+
+        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 = NULL;
+    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) {
+        _reportf(L"No tickets to delete");
+
+        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;
+
+            _reportf(L"Destroying ccache [%s]", ccname);
+
+            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) {
+                _reportf(L"krb5_cc_destroy returns code %d", code);
+            }
+
+        _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;
+
+        _reportf(L"Looking at ccache [%s]", ccname);
+
+        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;
+
+        _reportf(L"Attempting to delete ticket %s", srvname);
+
+        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);
+
+    if (ctx != NULL)
+        pkrb5_free_context(ctx);
+
+    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(char * match_princ, BOOL match_realm, BOOL save_creds,
+                khm_handle * ret_ident)
+{
+#ifdef NO_KRB5
+    return(FALSE);
+#else /* NO_KRB5 */
+    krb5_context kcontext = 0;
+    krb5_error_code code = 0;
+    krb5_ccache ccache=0;
+    krb5_ccache mslsa_ccache=0;
+    krb5_creds creds;
+    krb5_cc_cursor cursor=0;
+    krb5_principal princ = 0;
+    khm_handle ident = NULL;
+    wchar_t wname[KCDB_IDENT_MAXCCH_NAME];
+    char    cname[KCDB_IDENT_MAXCCH_NAME];
+    char *cache_name = NULL;
+    char *princ_name = NULL;
+    BOOL rc = FALSE;
+
+    kherr_reportf(L"Begin : khm_krb5_ms2mit. save_cred=%d\n", (int) save_creds);
+
+    if ( !pkrb5_init_context )
+        goto cleanup;
+
+    if (code = pkrb5_init_context(&kcontext))
+        goto cleanup;
+
+    kherr_reportf(L"Resolving MSLSA\n");
+
+    if (code = pkrb5_cc_resolve(kcontext, "MSLSA:", &mslsa_ccache))
+        goto cleanup;
+
+    if ( save_creds ) {
+        kherr_reportf(L"Getting principal\n");
+        if (code = pkrb5_cc_get_principal(kcontext, mslsa_ccache, &princ))
+            goto cleanup;
+
+        kherr_reportf(L"Unparsing name\n");
+        if (code = pkrb5_unparse_name(kcontext, princ, &princ_name))
+            goto cleanup;
+
+        AnsiStrToUnicode(wname, sizeof(wname), princ_name);
+
+        kherr_reportf(L"Unparsed name [%s]", wname);
+
+        /* see if we have to match a specific principal */
+        if (match_princ != NULL) {
+            if (strcmp(princ_name, match_princ)) {
+                kherr_reportf(L"Principal mismatch.  Wanted [%S], found [%S]",
+                              match_princ, princ_name);
+                goto cleanup;
+            }
+        } else if (match_realm) {
+            wchar_t * wdefrealm;
+            char defrealm[256];
+            krb5_data * princ_realm;
+
+            wdefrealm = khm_krb5_get_default_realm();
+            if (wdefrealm == NULL) {
+                kherr_reportf(L"Can't determine default realm");
+                goto cleanup;
+            }
+
+            princ_realm = krb5_princ_realm(kcontext, princ);
+            UnicodeStrToAnsi(defrealm, sizeof(defrealm), wdefrealm);
+
+            if (strncmp(defrealm, princ_realm->data, princ_realm->length)) {
+                kherr_reportf(L"Realm mismatch.  Wanted [%S], found [%*S]",
+                              defrealm, princ_realm->length, princ_realm->data);
+                PFREE(wdefrealm);
+                goto cleanup;
+            }
+
+            PFREE(wdefrealm);
+        }
+
+        if (KHM_SUCCEEDED(kcdb_identity_create(wname,
+                                               KCDB_IDENT_FLAG_CREATE,
+                                               &ident))) {
+            khm_handle idconfig = NULL;
+            khm_handle k5config = NULL;
+            khm_size cb;
+
+            wname[0] = L'\0';
+
+            kcdb_identity_get_config(ident, KHM_FLAG_CREATE, &idconfig);
+            if (idconfig == NULL)
+                goto _done_checking_config;
+
+            khc_open_space(idconfig, CSNAME_KRB5CRED, KHM_FLAG_CREATE, &k5config);
+            if (k5config == NULL)
+                goto _done_checking_config;
+
+            cb = sizeof(wname);
+            khc_read_string(k5config,
+                            L"DefaultCCName",
+                            wname, &cb);
+
+        _done_checking_config:
+
+            if (idconfig)
+                khc_close_space(idconfig);
+            if (k5config)
+                khc_close_space(k5config);
+
+            if (wname[0]) {
+                UnicodeStrToAnsi(cname, sizeof(cname), wname);
+            } else {
+                StringCbPrintfA(cname, sizeof(cname), "API:%s", princ_name);
+            }
+
+            cache_name = cname;
+
+        } else {
+            /* the identity could not be created.  we just use the
+               name of the principal as the ccache name. */
+            StringCbPrintfA(cname, sizeof(cname), "API:%s", princ_name);
+            cache_name = cname;
+        }
+
+        kherr_reportf(L"Resolving target cache [%S]\n", cache_name);
+
+        if (code = pkrb5_cc_resolve(kcontext, cache_name, &ccache)) {
+            kherr_reportf(L"Cannot resolve cache [%S] with code=%d.  Trying default.\n", cache_name, code);
+
+            if (code = pkrb5_cc_default(kcontext, &ccache)) {
+                kherr_reportf(L"Failed to resolve default ccache. Code=%d", code);
+                goto cleanup;
+            }
+        }
+
+        kherr_reportf(L"Initializing ccache\n");
+        if (code = pkrb5_cc_initialize(kcontext, ccache, princ))
+            goto cleanup;
+
+        kherr_reportf(L"Copying credentials\n");
+        if (code = pkrb5_cc_copy_creds(kcontext, mslsa_ccache, ccache))
+            goto cleanup;
+
+        /* and mark the identity as having been imported */
+        if (ident) {
+            khm_krb5_set_identity_flags(ident, K5IDFLAG_IMPORTED, K5IDFLAG_IMPORTED);
+
+            if (ret_ident) {
+                *ret_ident = ident;
+                kcdb_identity_hold(*ret_ident);
+            }
+        }
+
+        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:
+    kherr_reportf(L"  Received code=%d", code);
+
+    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);
+    if (ident)
+        kcdb_identity_release(ident);
+
+    return(rc);
+#endif /* NO_KRB5 */
+}
+
+#define KRB_FILE                "KRB.CON"
+#define KRBREALM_FILE           "KRBREALM.CON"
+#define KRB5_FILE               "KRB5.INI"
+#define KRB5_TMP_FILE           "KRB5.INI.TMP"
+
+BOOL 
+khm_krb5_get_temp_profile_file(LPSTR confname, UINT szConfname)
+{
+    GetTempPathA(szConfname, confname);
+    confname[szConfname-1] = '\0';
+    StringCchCatA(confname, szConfname, KRB5_TMP_FILE);
+    confname[szConfname-1] = '\0';
+    return FALSE;
+}
+
+#ifdef NOT_QUITE_IMPLEMENTED_YET
+BOOL
+khm_krb5_set_profile_file(krb5_context ctx, LPSTR confname)
+{
+    char *conffiles[2];
+
+    if (confname == NULL ||
+        pkrb5_set_config_files == NULL ||
+        ctx == NULL)
+        return FALSE;
+
+    conffiles[0] = confname;
+    conffiles[1] = NULL;
+
+    if (pkrb5_set_config_files(ctx, conffiles))
+        return FALSE;
+    else
+        return TRUE;
+}
+#endif
+
+BOOL 
+khm_krb5_get_profile_file(LPSTR confname, UINT szConfname)
+{
+    char **configFile = NULL;
+    if (pkrb5_get_default_config_files(&configFile)) 
+    {
+        GetWindowsDirectoryA(confname,szConfname);
+        confname[szConfname-1] = '\0';
+
+        StringCchCatA(confname, szConfname, "\\");
+        StringCchCatA(confname, szConfname, KRB5_FILE);
+
+        return FALSE;
+    }
+    
+    *confname = 0;
+    
+    if (configFile)
+    {
+        StringCchCopyA(confname, szConfname, *configFile);
+        pkrb5_free_config_files(configFile); 
+    }
+    
+    if (!*confname)
+    {
+        GetWindowsDirectoryA(confname,szConfname);
+        confname[szConfname-1] = '\0';
+        StringCchCatA(confname, szConfname, "\\");
+        StringCchCatA(confname, szConfname, KRB5_FILE);
+    }
+    
+    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_krb5_get_profile_file(krbConFile, sizeof(krbConFile))) {
+            GetWindowsDirectoryA(krbConFile,sizeof(krbConFile));
+            krbConFile[MAX_PATH-1] = '\0';
+            StringCchCatA(confname, szConfname, "\\");
+        }
+        
+        pFind = strrchr(krbConFile, '\\');
+        if (pFind) {
+            *pFind = '\0';
+            StringCchCatA(krbConFile, ARRAYLENGTH(krbConFile), "\\");
+            StringCchCatA(krbConFile, ARRAYLENGTH(krbConFile), KRB_FILE);
+        }
+        else
+            krbConFile[0] = '\0';
+
+        StringCchCopyA(confname, szConfname, krbConFile);
+    }
+    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';
+                StringCchCatA(confname, szConfname, "\\");
+                StringCchCatA(confname, szConfname, KRB_FILE);
+            }
+    }
+    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_krb5_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 = PMALLOC(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)) && 
+#if _MSC_VER >= 1400
+            !fopen_s(&file, krb_conf, "rt")
+#else
+            (file = fopen(krb_conf, "rt"))
+#endif
+            )
+        {
+            char lineBuf[256];
+
+            /*TODO: compute the actual required buffer size instead of hardcoding */
+            cbsize = 16384; // arbitrary
+            rlist = PMALLOC(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 PFREE().
+
+    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);
+
+    if (ctx == 0)
+        return NULL;
+
+    pkrb5_get_default_realm(ctx,&def);
+    
+    if (def) {
+        cch = strlen(def) + 1;
+        realm = PMALLOC(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;
+}
+
+long
+khm_krb5_set_default_realm(wchar_t * realm) {
+    krb5_context ctx=0;
+    char * def = 0;
+    long rv = 0;
+    char astr[K5_MAXCCH_REALM];
+
+    UnicodeStrToAnsi(astr, sizeof(astr), realm);
+
+    pkrb5_init_context(&ctx);
+    pkrb5_get_default_realm(ctx,&def);
+
+    if ((def && strcmp(def, astr)) ||
+        !def) {
+        rv = pkrb5_set_default_realm(ctx, astr);
+    }
+
+    if (def) {
+        pkrb5_free_default_realm(ctx, def);
+    }
+
+    pkrb5_free_context(ctx);
+
+    return rv;
+}
+
+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 = 0;
+    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 = PMALLOC(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;
+}
+
+khm_int32 KHMAPI
+khm_krb5_creds_is_equal(khm_handle vcred1, khm_handle vcred2, void * dummy) {
+    if (kcdb_creds_comp_attr(vcred1, vcred2, KCDB_ATTR_LOCATION) ||
+        kcdb_creds_comp_attr(vcred1, vcred2, attr_id_key_enctype) ||
+        kcdb_creds_comp_attr(vcred1, vcred2, attr_id_tkt_enctype) ||
+        kcdb_creds_comp_attr(vcred1, vcred2, attr_id_kvno))
+        return 1;
+    else
+        return 0;
+}
+
+void
+khm_krb5_set_identity_flags(khm_handle identity,
+                            khm_int32  flag_mask,
+                            khm_int32  flag_value) {
+
+    khm_int32 t = 0;
+    khm_size  cb;
+
+    cb = sizeof(t);
+    if (KHM_FAILED(kcdb_identity_get_attr(identity,
+                                          attr_id_krb5_idflags,
+                                          NULL,
+                                          &t, &cb))) {
+        t = 0;
+    }
+
+    t &= ~flag_mask;
+    t |= (flag_value & flag_mask);
+
+    kcdb_identity_set_attr(identity,
+                           attr_id_krb5_idflags,
+                           &t, sizeof(t));
+}
+
+khm_int32
+khm_krb5_get_identity_flags(khm_handle identity) {
+    khm_int32 t = 0;
+    khm_size  cb;
+
+    cb = sizeof(t);
+    kcdb_identity_get_attr(identity,
+                           attr_id_krb5_idflags,
+                           NULL, &t, &cb);
+
+    return t;
+}
+
+long
+khm_krb5_get_temp_ccache(krb5_context ctx,
+                         krb5_ccache * prcc) {
+    int  rnd = rand();
+    char ccname[MAX_PATH];
+    long code = 0;
+    krb5_ccache cc = 0;
+
+    StringCbPrintfA(ccname, sizeof(ccname), "MEMORY:TempCache%8x", rnd);
+
+    code = pkrb5_cc_resolve(ctx, ccname, &cc);
+
+    if (code == 0)
+        *prcc = cc;
+
+    return code;
+}
+
+/*
+
+  The configuration information for each identity comes from a
+  multitude of layers organized as follows.  The ordering is
+  decreasing in priority.  When looking up a value, the value will be
+  looked up in each layer in turn starting at level 0.  The first
+  instance of the value found will be the effective value.
+
+  0  : <identity configuration>\Krb5Cred
+
+  0.1: per user
+
+  0.2: per machine
+
+  1  : <plugin configuration>\Parameters\Realms\<realm of identity>
+
+  1.1: per user
+
+  1.2: per machine
+
+  2  : <plugin configuration>\Parameters
+
+  2.1: per user
+
+  2.2: per machine
+
+  2.3: schema
+
+ */
+khm_int32
+khm_krb5_get_identity_config(khm_handle ident,
+                            khm_int32 flags,
+                            khm_handle * ret_csp) {
+
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+    khm_handle csp_i = NULL;
+    khm_handle csp_ik5 = NULL;
+    khm_handle csp_realms = NULL;
+    khm_handle csp_realm = NULL;
+    khm_handle csp_plugins = NULL;
+    khm_handle csp_krbcfg = NULL;
+    khm_handle csp_rv = NULL;
+    wchar_t realm[KCDB_IDENT_MAXCCH_NAME];
+
+    realm[0] = L'\0';
+
+    if (ident) {
+        wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
+        wchar_t * trealm;
+        khm_size cb_idname = sizeof(idname);
+
+        rv = kcdb_identity_get_name(ident, idname, &cb_idname);
+        if (KHM_SUCCEEDED(rv) &&
+            (trealm = khm_get_realm_from_princ(idname)) != NULL) {
+            StringCbCopy(realm, sizeof(realm), trealm);
+        }
+    }
+
+    if (ident) {
+        rv = kcdb_identity_get_config(ident, flags, &csp_i);
+        if (KHM_FAILED(rv))
+            goto try_realm;
+
+        rv = khc_open_space(csp_i, CSNAME_KRB5CRED, flags, &csp_ik5);
+        if (KHM_FAILED(rv))
+            goto try_realm;
+
+    try_realm:
+
+        if (realm[0] == L'\0')
+            goto done_shadow_realm;
+
+        rv = khc_open_space(csp_params, CSNAME_REALMS, flags, &csp_realms);
+        if (KHM_FAILED(rv))
+            goto done_shadow_realm;
+
+        rv = khc_open_space(csp_realms, realm, flags, &csp_realm);
+        if (KHM_FAILED(rv))
+            goto done_shadow_realm;
+
+        rv = khc_shadow_space(csp_realm, csp_params);
+
+    done_shadow_realm:
+
+        if (csp_ik5) {
+            if (csp_realm)
+                rv = khc_shadow_space(csp_ik5, csp_realm);
+            else
+                rv = khc_shadow_space(csp_ik5, csp_params);
+
+            csp_rv = csp_ik5;
+        } else {
+            if (csp_realm)
+                csp_rv = csp_realm;
+        }
+    }
+
+    if (csp_rv == NULL) {
+
+        /* No valid identity specified or the specified identity
+           doesn't have any configuration. We default to the
+           parameters key. */
+
+        /* we don't just return csp_params since that's a global
+           handle that we shouldn't close until the plugin is
+           unloaded.  The caller is going to close the returned handle
+           when it is done.  So we need to create a new csp_params
+           that can safely be closed. */
+
+        rv = kmm_get_plugins_config(0, &csp_plugins);
+        if (KHM_FAILED(rv))
+            goto done;
+
+        rv = khc_open_space(csp_plugins, CSNAME_KRB5CRED, flags, &csp_krbcfg);
+        if (KHM_FAILED(rv))
+            goto done;
+
+        rv = khc_open_space(csp_krbcfg, CSNAME_PARAMS, flags, &csp_rv);
+    }
+
+ done:
+
+    *ret_csp = csp_rv;
+
+    /* leave csp_ik5.  If it's non-NULL, then it's the return value */
+    /* leave csp_rv.  It's the return value. */
+    if (csp_i)
+        khc_close_space(csp_i);
+    if (csp_realms)
+        khc_close_space(csp_realms);
+
+    /* csp_realm can also be a return value if csp_ik5 was NULL */
+    if (csp_realm && csp_realm != csp_rv)
+        khc_close_space(csp_realm);
+
+    if (csp_plugins)
+        khc_close_space(csp_plugins);
+    if (csp_krbcfg)
+        khc_close_space(csp_krbcfg);
+
+    return rv;
+}
+
+/* from get_in_tkt.c */
+static krb5_error_code
+get_libdefault_string(profile_t profile, const char * realm,
+                      const char * option, char ** ret_val) {
+    char realmstr[K5_MAXCCH_REALM];
+    char **nameval = NULL;
+    const char * names[4];
+    krb5_error_code code = 0;
+
+    names[0] = "libdefaults";
+
+    if (!realm || !realm[0])
+        goto try_number_two;
+
+    StringCbCopyA(realmstr, sizeof(realmstr), realm);
+
+    /*
+     * Try number one:
+     *
+     * [libdefaults]
+     *         REALM = {
+     *                 option = <boolean>
+     *         }
+     */
+
+    names[1] = realmstr;
+    names[2] = option;
+    names[3] = 0;
+    code = pprofile_get_values(profile, names, &nameval);
+    if (code == 0 && nameval && nameval[0])
+       goto goodbye;
+
+ try_number_two:
+
+    /*
+     * Try number two:
+     *
+     * [libdefaults]
+     *         option = <boolean>
+     */
+    
+    names[1] = option;
+    names[2] = 0;
+    code = pprofile_get_values(profile, names, &nameval);
+    if (code == 0 && nameval && nameval[0])
+       goto goodbye;
+
+ goodbye:
+    if (!nameval) 
+       return(ENOENT);
+
+    if (!nameval[0]) {
+        code = ENOENT;
+    } else {
+        size_t cb;
+
+        if (FAILED(StringCbLengthA(nameval[0], K5_MAXCCH_REALM * sizeof(char), &cb))) {
+            code = ENOMEM;
+        } else {
+            cb += sizeof(char);
+            *ret_val = PMALLOC(cb);
+
+            if (!*ret_val)
+                code = ENOMEM;
+            else {
+                StringCbCopyA(*ret_val, cb, nameval[0]);
+                code = 0;
+            }
+        }
+    }
+
+    pprofile_free_list(nameval);
+
+    return code;
+}
+
+khm_int32
+khm_krb5_get_identity_params(khm_handle ident, k5_params * p) {
+
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+    khm_handle csp_id = NULL;
+    khm_int32 regf = 0;
+    khm_int32 proff = 0;
+    khm_int32 e;
+    khm_int32 v;
+    CHAR confname[MAX_PATH];
+    CHAR realmname[K5_MAXCCH_REALM];
+
+    ZeroMemory(p, sizeof(*p));
+
+    rv = khm_krb5_get_identity_config(ident, 0, &csp_id);
+    if (KHM_FAILED(rv))
+        goto done_reg;
+
+
+#define GETVAL(vname, vfield, flag) \
+    do {                            \
+    e = khc_value_exists(csp_id, vname);                               \
+    rv = khc_read_int32(csp_id, vname, &v);                            \
+    if (KHM_FAILED(rv)) goto done_reg;                                 \
+    p->vfield = v;                                                     \
+    if ((e & ~KCONF_FLAG_SCHEMA) != 0) regf |= flag;                   \
+    } while(FALSE)
+
+    /* Flags */
+    GETVAL(L"Renewable", renewable, K5PARAM_F_RENEW);
+    GETVAL(L"Forwardable", forwardable, K5PARAM_F_FORW);
+    GETVAL(L"Proxiable", proxiable, K5PARAM_F_PROX);
+    GETVAL(L"Addressless", addressless, K5PARAM_F_ADDL);
+    GETVAL(L"PublicIP", publicIP, K5PARAM_F_PUBIP);
+
+    /* Lifetime */
+    GETVAL(L"DefaultLifetime", lifetime, K5PARAM_F_LIFE);
+    GETVAL(L"MaxLifetime", lifetime_max, K5PARAM_F_LIFE_H);
+    GETVAL(L"MinLifetime", lifetime_min, K5PARAM_F_LIFE_L);
+
+    /* Renewable lifetime */
+    GETVAL(L"DefaultRenewLifetime", renew_life, K5PARAM_F_RLIFE);
+    GETVAL(L"MaxRenewLifetime", renew_life_max, K5PARAM_F_RLIFE_H);
+    GETVAL(L"MinRenewLifetime", renew_life_min, K5PARAM_F_RLIFE_L);
+
+#undef GETVAL
+
+ done_reg:
+
+    if (csp_id)
+        khc_close_space(csp_id);
+
+    /* if all the parameters were read from the registry, then we have
+       no reason to read from the profile file. */
+    if (regf == K5PARAM_FM_ALL) {
+        p->source_reg = regf;
+        return KHM_ERROR_SUCCESS;
+    }
+
+    if (rv)
+        return rv;
+
+    /* we need to figure out the realm name, since there might be
+       per-realm configuration in the profile file. */
+
+    realmname[0] = '\0';
+
+    if (ident) {
+        wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
+        khm_size cb;
+
+        idname[0] = L'\0';
+        cb = sizeof(idname);
+        rv = kcdb_identity_get_name(ident, idname, &cb);
+        if (KHM_SUCCEEDED(rv)) {
+            wchar_t * wrealm;
+
+            wrealm = khm_get_realm_from_princ(idname);
+            if (wrealm) {
+                UnicodeStrToAnsi(realmname, sizeof(realmname), wrealm);
+            }
+        }
+    }
+
+    /* If we get here, then some of the settings we read from the
+       configuration actually came from the schema.  In other words,
+       the values weren't really defined for this identity.  So we now
+       have to read the values from the krb5 configuration file. */
+
+    if (!khm_krb5_get_profile_file(confname, sizeof(confname))) {
+        profile_t profile;
+        const char * filenames[2];
+        long retval;
+
+        filenames[0] = confname;
+        filenames[1] = NULL;
+
+        if (!pprofile_init(filenames, &profile)) {
+
+            /* default ticket lifetime */
+            if (!(regf & K5PARAM_F_LIFE)) {
+                char * value = NULL;
+                retval = get_libdefault_string(profile, realmname,
+                                               "ticket_lifetime", &value);
+
+                if (retval == 0 && value) {
+                    krb5_deltat d;
+
+                    retval = pkrb5_string_to_deltat(value, &d);
+                    if (retval == KRB5_DELTAT_BADFORMAT) {
+                        /* Historically some sites use relations of
+                           the form 'ticket_lifetime = 24000' where
+                           the unit is left out but is assumed to be
+                           seconds. Then there are other sites which
+                           use the form 'ticket_lifetime = 600' where
+                           the unit is assumed to be minutes.  While
+                           these are technically wrong (a unit needs
+                           to be specified), we try to accomodate for
+                           this using the safe assumption that the
+                           unit is seconds and tack an 's' to the end
+                           and see if that works. */
+
+                        size_t cch;
+                        char tmpbuf[256];
+                        char * buf;
+
+                        do {
+                            if (FAILED(StringCchLengthA(value, 1024 /* unresonably large size */,
+                                                        &cch)))
+                                break;
+
+                            cch += sizeof(char) * 2; /* NULL and new 's' */
+                            if (cch > ARRAYLENGTH(tmpbuf))
+                                buf = PMALLOC(cch * sizeof(char));
+                            else
+                                buf = tmpbuf;
+
+                            StringCchCopyA(buf, cch, value);
+                            StringCchCatA(buf, cch, "s");
+
+                            retval = pkrb5_string_to_deltat(buf, &d);
+                            if (retval == 0) {
+                                p->lifetime = d;
+                                proff |= K5PARAM_F_LIFE;
+                            }
+
+                            if (buf != tmpbuf)
+                                PFREE(buf);
+
+                        } while(0);
+
+                    } else if (retval == 0) {
+                        p->lifetime = d;
+                        proff |= K5PARAM_F_LIFE;
+                    }
+
+                    PFREE(value);
+                }
+            }
+
+            if (!(regf & K5PARAM_F_RLIFE)) {
+                char * value = NULL;
+                retval = get_libdefault_string(profile, realmname,
+                                               "renew_lifetime", &value);
+                if (retval == 0 && value) {
+                    krb5_deltat d;
+
+                    retval = pkrb5_string_to_deltat(value, &d);
+                    if (retval == 0) {
+                        p->renew_life = d;
+                        proff |= K5PARAM_F_RLIFE;
+                    }
+                    PFREE(value);
+                }
+            }
+
+            if (!(regf & K5PARAM_F_FORW)) {
+                char * value = NULL;
+                retval = get_libdefault_string(profile, realmname,
+                                               "forwardable", &value);
+                if (retval == 0 && value) {
+                    khm_boolean b;
+
+                    if (!khm_krb5_parse_boolean(value, &b))
+                        p->forwardable = b;
+                    else
+                        p->forwardable = FALSE;
+                    PFREE(value);
+                    proff |= K5PARAM_F_FORW;
+                }
+            }
+
+            if (!(regf & K5PARAM_F_RENEW)) {
+                char * value = NULL;
+                retval = get_libdefault_string(profile, realmname,
+                                               "renewable", &value);
+                if (retval == 0 && value) {
+                    khm_boolean b;
+
+                    if (!khm_krb5_parse_boolean(value, &b))
+                        p->renewable = b;
+                    else
+                        p->renewable = TRUE;
+                    PFREE(value);
+                    proff |= K5PARAM_F_RENEW;
+                }
+            }
+
+            if (!(regf & K5PARAM_F_ADDL)) {
+                char * value = NULL;
+                retval = get_libdefault_string(profile, realmname,
+                                               "noaddresses", &value);
+                if (retval == 0 && value) {
+                    khm_boolean b;
+
+                    if (!khm_krb5_parse_boolean(value, &b))
+                        p->addressless = b;
+                    else
+                        p->addressless = TRUE;
+                    PFREE(value);
+                    proff |= K5PARAM_F_ADDL;
+                }
+            }
+
+            if (!(regf & K5PARAM_F_PROX)) {
+                char * value = NULL;
+                retval = get_libdefault_string(profile, realmname,
+                                               "proxiable", &value);
+                if (retval == 0 && value) {
+                    khm_boolean b;
+
+                    if (!khm_krb5_parse_boolean(value, &b))
+                        p->proxiable = b;
+                    else
+                        p->proxiable = FALSE;
+                    PFREE(value);
+                    proff |= K5PARAM_F_PROX;
+                }
+            }
+
+            pprofile_release(profile);
+        }
+    }
+
+    p->source_reg = regf;
+    p->source_prof = proff;
+
+    return rv;
+}
+
+/* Note that p->source_reg and p->source_prof is used in special ways
+   here.  All fields that are flagged in source_reg will be written to
+   the configuration (if they are different from what
+   khm_krb5_get_identity_params() reports).  All fields that are
+   flagged in source_prof will be removed from the configuration
+   (thereby exposing the value defined in the profile file). */
+khm_int32
+khm_krb5_set_identity_params(khm_handle ident, const k5_params * p) {
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+    khm_handle csp_id = NULL;
+    k5_params p_s;
+    khm_int32 source_reg = p->source_reg;
+    khm_int32 source_prof = p->source_prof;
+
+    rv = khm_krb5_get_identity_config(ident,
+                                      KHM_PERM_WRITE | KHM_FLAG_CREATE |
+                                      KCONF_FLAG_WRITEIFMOD,
+                                      &csp_id);
+    if (KHM_FAILED(rv))
+        goto done_reg;
+
+    khm_krb5_get_identity_params(ident, &p_s);
+
+    /* Remove any bits that don't make sense.  Not all values can be
+       specified in the profile file. */
+    source_prof &= K5PARAM_FM_PROF;
+
+    /* if a flag appears in both source_prof and source_reg, remove
+       the flag from source_reg. */
+    source_reg &= ~source_prof;
+
+    /* we only write values that have changed, and that are flagged in
+       source_reg */
+
+    if ((source_reg & K5PARAM_F_RENEW) &&
+        !!p_s.renewable != !!p->renewable)
+        khc_write_int32(csp_id, L"Renewable", !!p->renewable);
+
+    if ((source_reg & K5PARAM_F_FORW) &&
+        !!p_s.forwardable != !!p->forwardable)
+        khc_write_int32(csp_id, L"Forwardable", !!p->forwardable);
+
+    if ((source_reg & K5PARAM_F_PROX) &&
+        !!p_s.proxiable != !!p->proxiable)
+        khc_write_int32(csp_id, L"Proxiable", !!p->proxiable);
+
+    if ((source_reg & K5PARAM_F_ADDL) &&
+        !!p_s.addressless != !!p->addressless)
+        khc_write_int32(csp_id, L"Addressless", !!p->addressless);
+
+    if ((source_reg & K5PARAM_F_PUBIP) &&
+        p_s.publicIP != p->publicIP)
+        khc_write_int32(csp_id, L"PublicIP", p->publicIP);
+
+    if ((source_reg & K5PARAM_F_LIFE) &&
+        p_s.lifetime != p->lifetime)
+        khc_write_int32(csp_id, L"DefaultLifetime", p->lifetime);
+
+    if ((source_reg & K5PARAM_F_LIFE_H) &&
+        p_s.lifetime_max != p->lifetime_max)
+        khc_write_int32(csp_id, L"MaxLifetime", p->lifetime_max);
+
+    if ((source_reg & K5PARAM_F_LIFE_L) &&
+        p_s.lifetime_min != p->lifetime_min)
+        khc_write_int32(csp_id, L"MinLifetime", p->lifetime_min);
+
+    if ((source_reg & K5PARAM_F_RLIFE) &&
+        p_s.renew_life != p->renew_life)
+        khc_write_int32(csp_id, L"DefaultRenewLifetime", p->renew_life);
+
+    if ((source_reg & K5PARAM_F_RLIFE_H) &&
+        p_s.renew_life_max != p->renew_life_max)
+        khc_write_int32(csp_id, L"MaxRenewLifetime", p->renew_life_max);
+
+    if ((source_reg & K5PARAM_F_RLIFE_L) &&
+        p_s.renew_life_min != p->renew_life_min)
+        khc_write_int32(csp_id, L"MinRenewLifetime", p->renew_life_min);
+
+    /* and now, remove the values that are present in source_prof.
+       Not all values are removed since not all values can be
+       specified in the profile file. */
+    if (source_prof & K5PARAM_F_RENEW)
+        khc_remove_value(csp_id, L"Renewable", 0);
+
+    if (source_prof & K5PARAM_F_FORW)
+        khc_remove_value(csp_id, L"Forwardable", 0);
+
+    if (source_prof & K5PARAM_F_PROX)
+        khc_remove_value(csp_id, L"Proxiable", 0);
+
+    if (source_prof & K5PARAM_F_ADDL)
+        khc_remove_value(csp_id, L"Addressless", 0);
+
+    if (source_prof & K5PARAM_F_LIFE)
+        khc_remove_value(csp_id, L"DefaultLifetime", 0);
+
+    if (source_prof & K5PARAM_F_RLIFE)
+        khc_remove_value(csp_id, L"DefaultRenewLifetime", 0);
+
+ done_reg:
+    if (csp_id != NULL)
+        khc_close_space(csp_id);
+
+    return rv;
+}
+
+static const char *const conf_yes[] = {
+    "y", "yes", "true", "t", "1", "on",
+    0,
+};
+
+static const char *const conf_no[] = {
+    "n", "no", "false", "nil", "0", "off",
+    0,
+};
+
+int
+khm_krb5_parse_boolean(const char *s, khm_boolean * b)
+{
+    const char *const *p;
+
+    for(p=conf_yes; *p; p++) {
+        if (!_stricmp(*p,s)) {
+            *b = TRUE;
+            return 0;
+        }
+    }
+
+    for(p=conf_no; *p; p++) {
+        if (!_stricmp(*p,s)) {
+            *b = FALSE;
+            return 0;
+        }
+    }
+
+    /* Default to "no" */
+    return KHM_ERROR_INVALID_PARAM;
+}
index b4ab452b315dbc7e6e6548011afd608cdd777fea..d2ec28b47f97be4e93826810e40bd7467a12d4d0 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/* Adapted from multiple Leash header files */\r
-\r
-#ifndef __KHIMAIRA_KRB5FUNCS_H\r
-#define __KHIMAIRA_KRB5FUNCS_H\r
-\r
-#include<stdlib.h>\r
-#include<krb5.h>\r
-\r
-#include <windows.h>\r
-#define SECURITY_WIN32\r
-#include <security.h>\r
-\r
-#if _WIN32_WINNT < 0x0501\r
-#define KHM_SAVE_WIN32_WINNT _WIN32_WINNT\r
-#undef _WIN32_WINNT\r
-#define _WIN32_WINNT 0x0501\r
-#endif\r
-#include<ntsecapi.h>\r
-#ifdef KHM_SAVE_WIN32_WINNT\r
-#undef _WIN32_WINNT\r
-#define _WIN32_WINNT KHM_SAVE_WIN32_WINNT\r
-#undef KHM_SAVE_WIN32_WINNT\r
-#endif\r
-\r
-#include <krb5common.h>\r
-\r
-#define LEASH_DEBUG_CLASS_GENERIC   0\r
-#define LEASH_DEBUG_CLASS_KRB4      1\r
-#define LEASH_DEBUG_CLASS_KRB4_APP  2\r
-\r
-#define LEASH_PRIORITY_LOW  0\r
-#define LEASH_PRIORITY_HIGH 1\r
-\r
-#define KRB5_DEFAULT_LIFE            60*60*10 /* 10 hours */\r
-\r
-#define KRB5_MAXCCH_CCNAME           1024\r
-\r
-#define KRB5_CONF_YES                "yes"\r
-#define KRB5_CONF_NO                 "no"\r
-\r
-typedef struct tag_k5params {\r
-\r
-    khm_int32   source_reg;     /* flags indicating which fields were\r
-                                   retrieved using the registry */\r
-    khm_int32   source_prof;    /* flags indicating which fields were\r
-                                   retrieved using krb5.ini */\r
-\r
-    khm_boolean renewable;\r
-    khm_boolean forwardable;\r
-    khm_boolean proxiable;\r
-    khm_boolean addressless;\r
-\r
-    khm_ui_4    publicIP;\r
-\r
-    krb5_deltat lifetime;\r
-    krb5_deltat lifetime_min;\r
-    krb5_deltat lifetime_max;\r
-\r
-    krb5_deltat renew_life;\r
-    krb5_deltat renew_life_min;\r
-    krb5_deltat renew_life_max;\r
-\r
-} k5_params;\r
-\r
-#define K5PARAM_F_RENEW   0x00000001\r
-#define K5PARAM_F_FORW    0x00000002\r
-#define K5PARAM_F_PROX    0x00000004\r
-#define K5PARAM_F_ADDL    0x00000008\r
-#define K5PARAM_F_PUBIP   0x00000010\r
-#define K5PARAM_F_LIFE    0x00000020\r
-#define K5PARAM_F_RLIFE   0x00000040\r
-#define K5PARAM_F_LIFE_L  0x00000080\r
-#define K5PARAM_F_LIFE_H  0x00000100\r
-#define K5PARAM_F_RLIFE_L 0x00000200\r
-#define K5PARAM_F_RLIFE_H 0x00000400\r
-\r
-#define K5PARAM_FM_ALL    0x000007ff\r
-#define K5PARAM_FM_PROF   0x0000007f\r
\r
-/* Credential and principal operations */\r
-\r
-BOOL \r
-khm_krb5_ms2mit(char * match_princ,\r
-                BOOL   match_realm,\r
-                BOOL   save_creds,\r
-                khm_handle * ret_ident);\r
-\r
-int\r
-khm_krb5_kinit(krb5_context       alt_ctx,\r
-               char *             principal_name,\r
-               char *             password,\r
-               char *             ccache,\r
-               krb5_deltat        lifetime,\r
-               DWORD              forwardable,\r
-               DWORD              proxiable,\r
-               krb5_deltat        renew_life,\r
-               DWORD              addressless,\r
-               DWORD              publicIP,\r
-               krb5_prompter_fct  prompter,\r
-               void *             p_data);\r
-\r
-long\r
-khm_krb5_changepwd(char * principal,\r
-                   char * password,\r
-                   char * newpassword,\r
-                   char** error_str);\r
-\r
-int\r
-khm_krb5_destroy_by_credset(khm_handle p_cs);\r
-\r
-int\r
-khm_krb5_destroy_identity(khm_handle identity);\r
-\r
-long\r
-khm_convert524(krb5_context ctx);\r
-\r
-int\r
-khm_krb5_renew_cred(khm_handle cred);\r
-\r
-int \r
-khm_krb5_renew_ident(khm_handle identity);\r
-\r
-long \r
-khm_krb5_list_tickets(krb5_context *krbv5Context);\r
-\r
-long\r
-khm_krb5_copy_ccache_by_name(krb5_context in_ctx,\r
-                             wchar_t * wscc_dest,\r
-                             wchar_t * wscc_src);\r
-\r
-long\r
-khm_krb5_get_temp_ccache(krb5_context ctx,\r
-                         krb5_ccache * cc);\r
-\r
-khm_int32 KHMAPI\r
-khm_krb5_creds_is_equal(khm_handle vcred1, khm_handle vcred2, void * dummy);\r
-\r
-\r
-/* Configuration */\r
-\r
-BOOL \r
-khm_krb5_get_profile_file(LPSTR confname, UINT szConfname);\r
-\r
-BOOL \r
-khm_krb5_get_temp_profile_file(LPSTR confname, UINT szConfname);\r
-\r
-wchar_t * \r
-khm_krb5_get_default_realm(void);\r
-\r
-long\r
-khm_krb5_set_default_realm(wchar_t * realm);\r
-\r
-wchar_t * \r
-khm_krb5_get_realm_list(void);\r
-\r
-khm_int32\r
-khm_krb5_get_identity_config(khm_handle ident,\r
-                             khm_int32 flags,\r
-                             khm_handle * ret_csp);\r
-\r
-void\r
-khm_krb5_set_identity_flags(khm_handle identity,\r
-                            khm_int32  flag_mask,\r
-                            khm_int32  flag_value);\r
-\r
-khm_int32\r
-khm_krb5_get_identity_flags(khm_handle identity);\r
-\r
-khm_int32\r
-khm_krb5_set_identity_params(khm_handle ident, const k5_params * p);\r
-\r
-khm_int32\r
-khm_krb5_get_identity_params(khm_handle ident, k5_params * p);\r
-\r
-/* Utility */\r
-\r
-wchar_t * \r
-khm_get_realm_from_princ(wchar_t * princ);\r
-\r
-long\r
-khm_krb5_canon_cc_name(wchar_t * wcc_name,\r
-                       size_t cb_cc_name);\r
-\r
-int \r
-khm_krb5_cc_name_cmp(const wchar_t * cc_name_1,\r
-                     const wchar_t * cc_name_2);\r
-\r
-int\r
-khm_krb5_parse_boolean(const char *s, khm_boolean * b);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+
+#if _WIN32_WINNT < 0x0501
+#define KHM_SAVE_WIN32_WINNT _WIN32_WINNT
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501
+#endif
+#include<ntsecapi.h>
+#ifdef KHM_SAVE_WIN32_WINNT
+#undef _WIN32_WINNT
+#define _WIN32_WINNT KHM_SAVE_WIN32_WINNT
+#undef KHM_SAVE_WIN32_WINNT
+#endif
+
+#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
+
+#define KRB5_CONF_YES                "yes"
+#define KRB5_CONF_NO                 "no"
+
+typedef struct tag_k5params {
+
+    khm_int32   source_reg;     /* flags indicating which fields were
+                                   retrieved using the registry */
+    khm_int32   source_prof;    /* flags indicating which fields were
+                                   retrieved using krb5.ini */
+
+    khm_boolean renewable;
+    khm_boolean forwardable;
+    khm_boolean proxiable;
+    khm_boolean addressless;
+
+    khm_ui_4    publicIP;
+
+    krb5_deltat lifetime;
+    krb5_deltat lifetime_min;
+    krb5_deltat lifetime_max;
+
+    krb5_deltat renew_life;
+    krb5_deltat renew_life_min;
+    krb5_deltat renew_life_max;
+
+} k5_params;
+
+#define K5PARAM_F_RENEW   0x00000001
+#define K5PARAM_F_FORW    0x00000002
+#define K5PARAM_F_PROX    0x00000004
+#define K5PARAM_F_ADDL    0x00000008
+#define K5PARAM_F_PUBIP   0x00000010
+#define K5PARAM_F_LIFE    0x00000020
+#define K5PARAM_F_RLIFE   0x00000040
+#define K5PARAM_F_LIFE_L  0x00000080
+#define K5PARAM_F_LIFE_H  0x00000100
+#define K5PARAM_F_RLIFE_L 0x00000200
+#define K5PARAM_F_RLIFE_H 0x00000400
+
+#define K5PARAM_FM_ALL    0x000007ff
+#define K5PARAM_FM_PROF   0x0000007f
+/* Credential and principal operations */
+
+BOOL 
+khm_krb5_ms2mit(char * match_princ,
+                BOOL   match_realm,
+                BOOL   save_creds,
+                khm_handle * ret_ident);
+
+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_cred(khm_handle cred);
+
+int 
+khm_krb5_renew_ident(khm_handle identity);
+
+long 
+khm_krb5_list_tickets(krb5_context *krbv5Context);
+
+long
+khm_krb5_copy_ccache_by_name(krb5_context in_ctx,
+                             wchar_t * wscc_dest,
+                             wchar_t * wscc_src);
+
+long
+khm_krb5_get_temp_ccache(krb5_context ctx,
+                         krb5_ccache * cc);
+
+khm_int32 KHMAPI
+khm_krb5_creds_is_equal(khm_handle vcred1, khm_handle vcred2, void * dummy);
+
+
+/* Configuration */
+
+BOOL 
+khm_krb5_get_profile_file(LPSTR confname, UINT szConfname);
+
+BOOL 
+khm_krb5_get_temp_profile_file(LPSTR confname, UINT szConfname);
+
+wchar_t * 
+khm_krb5_get_default_realm(void);
+
+long
+khm_krb5_set_default_realm(wchar_t * realm);
+
+wchar_t * 
+khm_krb5_get_realm_list(void);
+
+khm_int32
+khm_krb5_get_identity_config(khm_handle ident,
+                             khm_int32 flags,
+                             khm_handle * ret_csp);
+
+void
+khm_krb5_set_identity_flags(khm_handle identity,
+                            khm_int32  flag_mask,
+                            khm_int32  flag_value);
+
+khm_int32
+khm_krb5_get_identity_flags(khm_handle identity);
+
+khm_int32
+khm_krb5_set_identity_params(khm_handle ident, const k5_params * p);
+
+khm_int32
+khm_krb5_get_identity_params(khm_handle ident, k5_params * p);
+
+/* Utility */
+
+wchar_t * 
+khm_get_realm_from_princ(wchar_t * princ);
+
+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);
+
+int
+khm_krb5_parse_boolean(const char *s, khm_boolean * b);
+
+#endif
index 0072b967d27c873a6ecfacb397ee547e624077fa..4d1120f39be6f587f60ddded64607258d943d6b5 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<krbcred.h>\r
-#include<kherror.h>\r
-#include<khmsgtypes.h>\r
-#include<commctrl.h>\r
-#include<strsafe.h>\r
-#include<krb5.h>\r
-#include<assert.h>\r
-\r
-#define K5_NCID_UN_LABEL    (KHUI_CW_ID_MIN + 0)\r
-#define K5_NCID_UN          (KHUI_CW_ID_MIN + 1)\r
-#define K5_NCID_REALM_LABEL (KHUI_CW_ID_MIN + 2)\r
-#define K5_NCID_REALM       (KHUI_CW_ID_MIN + 3)\r
-\r
-#define NC_UNCHANGE_TIMEOUT 3000\r
-#define NC_UNCHANGE_TIMER   2\r
-#define NC_REALMCHANGE_TIMEOUT NC_UNCHANGE_TIMEOUT\r
-#define NC_REALMCHANGE_TIMER 3\r
-\r
-typedef struct tag_k5_new_cred_data {\r
-    HWND hw_username_label;\r
-    HWND hw_username;\r
-    HWND hw_realm_label;\r
-    HWND hw_realm;\r
-} k5_new_cred_data;\r
-\r
-static\r
-void\r
-trim_str(wchar_t * s, khm_size cch) {\r
-    wchar_t * c, * last_ws;\r
-\r
-    for (c = s; *c && iswspace(*c) && ((khm_size)(c - s)) < cch; c++);\r
-\r
-    if (((khm_size)(c - s)) >= cch)\r
-        return;\r
-\r
-    if (c != s && ((khm_size)(c - s)) < cch) {\r
-#if _MSC_VER >= 1400\r
-        wmemmove_s(s, cch, c, cch - ((khm_size)(c - s)));\r
-#else\r
-        memmove(s, c, (cch - ((khm_size)(c - s))) * sizeof(wchar_t));\r
-#endif\r
-    }\r
-\r
-    last_ws = NULL;\r
-    for (c = s; *c && ((khm_size)(c - s)) < cch; c++) {\r
-        if (!iswspace(*c))\r
-            last_ws = NULL;\r
-        else if (last_ws == NULL)\r
-            last_ws = c;\r
-    }\r
-\r
-    if (last_ws)\r
-        *last_ws = L'\0';\r
-}\r
-\r
-/* Runs in the UI thread */\r
-int \r
-k5_get_realm_from_nc(khui_new_creds * nc, \r
-                     wchar_t * buf, \r
-                     khm_size cch_buf) {\r
-    k5_new_cred_data * d;\r
-    khm_size s;\r
-\r
-    d = (k5_new_cred_data *) nc->ident_aux;\r
-    buf[0] = L'\0';\r
-    GetWindowText(d->hw_realm, buf, (int) cch_buf);\r
-    trim_str(buf, cch_buf);\r
-\r
-    StringCchLength(buf, cch_buf, &s);\r
-\r
-    return (int) s;\r
-}\r
-\r
-/* set the primary identity of a new credentials dialog depending on\r
-   the selection of the username and realm\r
-\r
-   Runs in the UI thread\r
-*/\r
-static void \r
-set_identity_from_ui(khui_new_creds * nc,\r
-                     k5_new_cred_data * d) {\r
-    wchar_t un[KCDB_IDENT_MAXCCH_NAME];\r
-    wchar_t * realm;\r
-    khm_size cch;\r
-    khm_size cch_left;\r
-    khm_handle ident;\r
-    LRESULT idx = CB_ERR;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    cch = GetWindowTextLength(d->hw_username);\r
-\r
-    /* we already set the max length of the edit control to be this.\r
-       shouldn't exceed it unless the edit control is confused. */\r
-    assert(cch < KCDB_IDENT_MAXCCH_NAME - 1);\r
-\r
-    GetWindowText(d->hw_username, un, ARRAYLENGTH(un));\r
-    trim_str(un, ARRAYLENGTH(un));\r
-\r
-    realm = khm_get_realm_from_princ(un);\r
-    if (realm)          /* realm was specified */\r
-        goto _set_ident;\r
-\r
-    /* the cch we got from GetWindowTextLength can not be trusted to\r
-       be exact.  For caveats see MSDN for GetWindowTextLength. */\r
-    StringCchLength(un, KCDB_IDENT_MAXCCH_NAME, &cch);\r
-\r
-    if (cch >= KCDB_IDENT_MAXCCH_NAME - 3) {\r
-        /* has to allow space for the '@' and at least a single\r
-           character realm, and the NULL terminator. */\r
-        rv = KHM_ERROR_TOO_LONG;\r
-        goto _set_null_ident;\r
-    }\r
-\r
-    realm = un + cch;   /* now points at terminating NULL */\r
-    cch_left = KCDB_IDENT_MAXCCH_NAME - cch;\r
-\r
-    *realm++ = L'@';\r
-    *realm = L'\0';\r
-    cch_left--;\r
-\r
-    cch = GetWindowTextLength(d->hw_realm);\r
-    if (cch == 0 || cch >= cch_left) {\r
-        rv = KHM_ERROR_INVALID_NAME;\r
-        goto _set_null_ident;\r
-    }\r
-\r
-    GetWindowText(d->hw_realm, realm, (int) cch_left);\r
-    trim_str(realm, cch_left);\r
-\r
- _set_ident:\r
-    if (KHM_FAILED(rv = kcdb_identity_create(un,\r
-                                             KCDB_IDENT_FLAG_CREATE,\r
-                                             &ident))) {\r
-        goto _set_null_ident;\r
-    }\r
-\r
-    khui_cw_set_primary_id(nc, ident);\r
-\r
-    kcdb_identity_release(ident);\r
-    return;\r
-\r
- _set_null_ident:\r
-    {\r
-        khui_new_creds_by_type * nct = NULL;\r
-        wchar_t cmsg[256];\r
-\r
-        khui_cw_find_type(nc, credtype_id_krb5, &nct);\r
-        if (nct && nct->hwnd_panel) {\r
-\r
-            switch(rv) {\r
-            case KHM_ERROR_TOO_LONG:\r
-                LoadString(hResModule, IDS_NCERR_IDENT_TOO_LONG,\r
-                           cmsg, ARRAYLENGTH(cmsg));\r
-                break;\r
-\r
-            case KHM_ERROR_INVALID_NAME:\r
-                LoadString(hResModule, IDS_NCERR_IDENT_INVALID,\r
-                           cmsg, ARRAYLENGTH(cmsg));\r
-                break;\r
-\r
-            default:\r
-                LoadString(hResModule, IDS_NCERR_IDENT_UNKNOWN,\r
-                           cmsg, ARRAYLENGTH(cmsg));\r
-            }\r
-\r
-            SendMessage(nct->hwnd_panel,\r
-                        KHUI_WM_NC_NOTIFY,\r
-                        MAKEWPARAM(0, K5_SET_CRED_MSG),\r
-                        (LPARAM) cmsg);\r
-        }\r
-\r
-        khui_cw_set_primary_id(nc, NULL);\r
-    }\r
-    return;\r
-}\r
-\r
-/* runs in the UI thread */\r
-static BOOL\r
-update_crossfeed(khui_new_creds * nc,\r
-                 k5_new_cred_data * d,\r
-                 int ctrl_id_src) {\r
-    wchar_t un[KCDB_IDENT_MAXCCH_NAME];\r
-    wchar_t * un_realm;\r
-    wchar_t realm[KCDB_IDENT_MAXCCH_NAME];\r
-    khm_size cch;\r
-    khm_size cch_left;\r
-    int idx;\r
-\r
-    cch = (khm_size) GetWindowTextLength(d->hw_username);\r
-#ifdef DEBUG\r
-    assert(cch < KCDB_IDENT_MAXCCH_NAME);\r
-#endif\r
-    if (cch == 0)\r
-        return FALSE;\r
-\r
-    GetWindowText(d->hw_username,\r
-                  un,\r
-                  ARRAYLENGTH(un));\r
-    trim_str(un, ARRAYLENGTH(un));\r
-\r
-    un_realm = khm_get_realm_from_princ(un);\r
-\r
-    if (un_realm == NULL) {\r
-        EnableWindow(d->hw_realm, TRUE);\r
-        return FALSE;\r
-    }\r
-\r
-    if (ctrl_id_src == K5_NCID_UN) {\r
-\r
-        idx = (int)SendMessage(d->hw_realm,\r
-                               CB_FINDSTRINGEXACT,\r
-                               (WPARAM) -1,\r
-                               (LPARAM) un_realm);\r
-\r
-        if (idx != CB_ERR) {\r
-            wchar_t srealm[KCDB_IDENT_MAXCCH_NAME];\r
-\r
-            cch = SendMessage(d->hw_realm,\r
-                              CB_GETLBTEXTLEN,\r
-                              (WPARAM) idx,\r
-                              0);\r
-\r
-#ifdef DEBUG\r
-            assert(cch < ARRAYLENGTH(srealm) - 1);\r
-#endif\r
-            SendMessage(d->hw_realm,\r
-                        CB_GETLBTEXT,\r
-                        (WPARAM) idx,\r
-                        (LPARAM) srealm);\r
-\r
-            if (!_wcsicmp(srealm, un_realm) && wcscmp(srealm, un_realm)) {\r
-                /* differ only by case */\r
-\r
-                StringCchCopy(un_realm, ARRAYLENGTH(un) - (un_realm - un),\r
-                              srealm);\r
-\r
-                SetWindowText(d->hw_username, un);\r
-            }\r
-        }\r
-\r
-        SendMessage(d->hw_realm,\r
-                    CB_SELECTSTRING,\r
-                    (WPARAM) -1,\r
-                    (LPARAM) un_realm);\r
-\r
-        SetWindowText(d->hw_realm,\r
-                      un_realm);\r
-\r
-        if (GetFocus() == d->hw_realm) {\r
-            HWND hw_next = GetNextDlgTabItem(nc->hwnd, d->hw_realm,\r
-                                             FALSE);\r
-            if (hw_next)\r
-                SetFocus(hw_next);\r
-        }\r
-\r
-        EnableWindow(d->hw_realm, FALSE);\r
-\r
-        return TRUE;\r
-    }\r
-    /* else... */\r
-\r
-    cch_left = KCDB_IDENT_MAXCCH_NAME - (un_realm - un);\r
-\r
-    cch = (khm_size) GetWindowTextLength(d->hw_realm);\r
-\r
-#ifdef DEBUG\r
-    assert(cch < KCDB_IDENT_MAXCCH_NAME);\r
-#endif\r
-    if (cch == 0)\r
-        return FALSE;\r
-\r
-    GetWindowText(d->hw_realm, realm,\r
-                  ARRAYLENGTH(realm));\r
-    trim_str(realm, ARRAYLENGTH(realm));\r
-\r
-    idx = (int)SendMessage(d->hw_realm,\r
-                           CB_FINDSTRINGEXACT,\r
-                           (WPARAM) -1,\r
-                           (LPARAM) realm);\r
-\r
-    if (idx != CB_ERR) {\r
-        wchar_t srealm[KCDB_IDENT_MAXCCH_NAME];\r
-\r
-        SendMessage(d->hw_realm,\r
-                    CB_GETLBTEXT,\r
-                    (WPARAM) idx,\r
-                    (LPARAM) srealm);\r
-\r
-        if (!_wcsicmp(srealm, realm) && wcscmp(srealm, realm)) {\r
-            StringCbCopy(realm, sizeof(realm), srealm);\r
-\r
-            SetWindowText(d->hw_realm, srealm);\r
-        }\r
-    }\r
-\r
-    StringCchCopy(un_realm, cch_left, realm);\r
-\r
-    SendMessage(d->hw_username,\r
-                CB_SELECTSTRING,\r
-                (WPARAM) -1,\r
-                (LPARAM) un);\r
-\r
-    SetWindowText(d->hw_username, un);\r
-\r
-    return TRUE;    \r
-}\r
-\r
-/* Handle window messages for the identity specifiers\r
-\r
-   runs in UI thread */\r
-static LRESULT \r
-handle_wnd_msg(khui_new_creds * nc,\r
-               HWND hwnd,\r
-               UINT uMsg,\r
-               WPARAM wParam,\r
-               LPARAM lParam) {\r
-    k5_new_cred_data * d;\r
-\r
-    d = (k5_new_cred_data *) nc->ident_aux;\r
-\r
-    switch(uMsg) {\r
-    case WM_COMMAND:\r
-        switch(wParam) {\r
-        case MAKEWPARAM(K5_NCID_UN, CBN_EDITCHANGE):\r
-            /* the username has changed.  Instead of handling this\r
-               for every keystroke, set a timer that elapses some\r
-               time afterwards and then handle the event. */\r
-            SetTimer(hwnd, NC_UNCHANGE_TIMER, \r
-                     NC_UNCHANGE_TIMEOUT, NULL);\r
-            return TRUE;\r
-\r
-        case MAKEWPARAM(K5_NCID_UN, CBN_KILLFOCUS):\r
-        case MAKEWPARAM(K5_NCID_UN, CBN_CLOSEUP):\r
-            KillTimer(hwnd, NC_UNCHANGE_TIMER);\r
-\r
-            update_crossfeed(nc,d,K5_NCID_UN);\r
-            set_identity_from_ui(nc,d);\r
-            return TRUE;\r
-\r
-        case MAKEWPARAM(K5_NCID_REALM,CBN_EDITCHANGE):\r
-            SetTimer(hwnd, NC_REALMCHANGE_TIMER,\r
-                     NC_REALMCHANGE_TIMEOUT, NULL);\r
-            return TRUE;\r
-\r
-        case MAKEWPARAM(K5_NCID_REALM,CBN_KILLFOCUS):\r
-        case MAKEWPARAM(K5_NCID_REALM,CBN_CLOSEUP):\r
-            KillTimer(hwnd, NC_REALMCHANGE_TIMER);\r
-\r
-            update_crossfeed(nc,d,K5_NCID_REALM);\r
-            set_identity_from_ui(nc, d);\r
-            return TRUE;\r
-        }\r
-        break;\r
-\r
-    case WM_TIMER:\r
-        if(wParam == NC_UNCHANGE_TIMER) {\r
-            KillTimer(hwnd, NC_UNCHANGE_TIMER);\r
-\r
-            update_crossfeed(nc, d, K5_NCID_UN);\r
-            set_identity_from_ui(nc,d);\r
-            return TRUE;\r
-        } else if (wParam == NC_REALMCHANGE_TIMER) {\r
-            KillTimer(hwnd, NC_REALMCHANGE_TIMER);\r
-\r
-            update_crossfeed(nc, d, K5_NCID_REALM);\r
-            set_identity_from_ui(nc, d);\r
-            return TRUE;\r
-        }\r
-        break;\r
-    }\r
-    return FALSE;\r
-}\r
-\r
-/* UI Callback\r
-\r
-   runs in UI thread */\r
-static LRESULT KHMAPI \r
-ui_cb(khui_new_creds * nc,\r
-      UINT cmd,\r
-      HWND hwnd,\r
-      UINT uMsg,\r
-      WPARAM wParam,\r
-      LPARAM lParam) {\r
-\r
-    k5_new_cred_data * d;\r
-\r
-    d = (k5_new_cred_data *) nc->ident_aux;\r
-\r
-    switch(cmd) {\r
-    case WMNC_IDENT_INIT:\r
-        {\r
-            wchar_t defident[KCDB_IDENT_MAXCCH_NAME];\r
-            wchar_t wbuf[1024];\r
-            wchar_t * ms = NULL;\r
-            wchar_t * t;\r
-            wchar_t * defrealm = NULL;\r
-            LRESULT lr;\r
-            khm_size cb_ms;\r
-            khm_size cb;\r
-            HWND hw_parent;\r
-            khm_int32 rv;\r
-            khm_handle hident;\r
-\r
-            hw_parent = (HWND) lParam;\r
-            defident[0] = L'\0';\r
-\r
-#ifdef DEBUG\r
-            assert(d == NULL);\r
-            assert(hw_parent != NULL);\r
-#endif\r
-\r
-            d = PMALLOC(sizeof(*d));\r
-            assert(d);\r
-            ZeroMemory(d, sizeof(*d));\r
-\r
-            khui_cw_lock_nc(nc);\r
-            nc->ident_aux = (LPARAM) d;\r
-            khui_cw_unlock_nc(nc);\r
-\r
-            LoadString(hResModule, IDS_NC_USERNAME, \r
-                       wbuf, ARRAYLENGTH(wbuf));\r
-\r
-            d->hw_username_label = CreateWindow\r
-                (L"STATIC",\r
-                 wbuf,\r
-                 SS_SIMPLE | WS_CHILD | WS_VISIBLE,\r
-                 0, 0, 100, 100, /* bogus values */\r
-                 hw_parent,\r
-                 (HMENU) K5_NCID_UN_LABEL,\r
-                 hInstance,\r
-                 NULL);\r
-            assert(d->hw_username_label != NULL);\r
-\r
-            d->hw_username = CreateWindow\r
-                (L"COMBOBOX",\r
-                 L"",\r
-                 CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_SORT | \r
-                 WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL,\r
-                 0, 0, 100, 100, /* bogus values */\r
-                 hw_parent,\r
-                 (HMENU) K5_NCID_UN,\r
-                 hInstance,\r
-                 NULL);\r
-            assert(d->hw_username != NULL);\r
-\r
-            SendMessage(d->hw_username,\r
-                        CB_LIMITTEXT,\r
-                        (WPARAM)(KCDB_IDENT_MAXCCH_NAME - 1),\r
-                        0);\r
-\r
-            SendMessage(d->hw_username,\r
-                        CB_SETEXTENDEDUI,\r
-                        (WPARAM) TRUE,\r
-                        0);\r
-\r
-            khui_cw_add_control_row(nc,\r
-                                    d->hw_username_label,\r
-                                    d->hw_username,\r
-                                    KHUI_CTRLSIZE_SMALL);\r
-\r
-            LoadString(hResModule, IDS_NC_REALM,\r
-                       wbuf, ARRAYLENGTH(wbuf));\r
-\r
-            d->hw_realm_label = CreateWindow\r
-                (L"STATIC",\r
-                 wbuf,\r
-                 SS_SIMPLE | WS_CHILD | WS_VISIBLE,\r
-                 0, 0, 100, 100, /* bogus */\r
-                 hw_parent,\r
-                 (HMENU) K5_NCID_REALM_LABEL,\r
-                 hInstance,\r
-                 NULL);\r
-            assert(d->hw_realm_label != NULL);\r
-\r
-            d->hw_realm = CreateWindow\r
-                (L"COMBOBOX",\r
-                 L"",\r
-                 CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_SORT | \r
-                 WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL,\r
-                 0, 0, 100, 100, /* bogus */\r
-                 hw_parent,\r
-                 (HMENU) K5_NCID_REALM,\r
-                 hInstance,\r
-                 NULL);\r
-            assert(d->hw_realm != NULL);\r
-\r
-            SendMessage(d->hw_realm,\r
-                        CB_LIMITTEXT,\r
-                        (WPARAM) (KCDB_IDENT_MAXCCH_NAME - 1),\r
-                        0);\r
-\r
-            SendMessage(d->hw_realm,\r
-                        CB_SETEXTENDEDUI,\r
-                        (WPARAM) TRUE,\r
-                        0);\r
-\r
-            khui_cw_add_control_row(nc,\r
-                                    d->hw_realm_label,\r
-                                    d->hw_realm,\r
-                                    KHUI_CTRLSIZE_SMALL);\r
-\r
-            /* add the LRU realms and principals to the dropdown\r
-               lists */\r
-            rv = khc_read_multi_string(csp_params,\r
-                                       L"LRUPrincipals",\r
-                                       NULL,\r
-                                       &cb_ms);\r
-\r
-            if (rv != KHM_ERROR_TOO_LONG || cb_ms <= sizeof(wchar_t) * 2)\r
-                goto _add_lru_realms;\r
-\r
-            ms = PMALLOC(cb_ms);\r
-            assert(ms != NULL);\r
-\r
-            cb = cb_ms;\r
-            rv = khc_read_multi_string(csp_params,\r
-                                       L"LRUPrincipals",\r
-                                       ms,\r
-                                       &cb);\r
-\r
-            assert(KHM_SUCCEEDED(rv));\r
-\r
-            /* the first of these is considered the default identity\r
-               if no other default is known */\r
-            StringCbCopy(defident, sizeof(defident), ms);\r
-\r
-            t = ms;\r
-            while(t && *t) {\r
-                SendMessage(d->hw_username,\r
-                            CB_ADDSTRING,\r
-                            0,\r
-                            (LPARAM) t);\r
-\r
-                t = multi_string_next(t);\r
-            }\r
-\r
-        _add_lru_realms:\r
-            /* add the default realm first */\r
-            defrealm = khm_krb5_get_default_realm();\r
-            if (defrealm) {\r
-                SendMessage(d->hw_realm,\r
-                            CB_ADDSTRING,\r
-                            0,\r
-                            (LPARAM) defrealm);\r
-            }\r
-\r
-            rv = khc_read_multi_string(csp_params,\r
-                                       L"LRURealms",\r
-                                       NULL,\r
-                                       &cb);\r
-\r
-            if (rv != KHM_ERROR_TOO_LONG)\r
-                goto _done_adding_lru;\r
-\r
-            if (ms != NULL) {\r
-                if (cb_ms < cb) {\r
-                    PFREE(ms);\r
-                    ms = PMALLOC(cb);\r
-                    assert(ms);\r
-                    cb_ms = cb;\r
-                }\r
-            } else {\r
-                ms = PMALLOC(cb);\r
-                cb_ms = cb;\r
-            }\r
-\r
-            rv = khc_read_multi_string(csp_params,\r
-                                       L"LRURealms",\r
-                                       ms,\r
-                                       &cb);\r
-\r
-            assert(KHM_SUCCEEDED(rv));\r
-\r
-            for (t = ms; t && *t; t = multi_string_next(t)) {\r
-                lr = SendMessage(d->hw_realm,\r
-                                 CB_FINDSTRINGEXACT,\r
-                                 (WPARAM) -1,\r
-                                 (LPARAM) t);\r
-                if (lr != CB_ERR)\r
-                    continue;\r
-\r
-                SendMessage(d->hw_realm,\r
-                            CB_ADDSTRING,\r
-                            0,\r
-                            (LPARAM) t);\r
-            }\r
-         _done_adding_lru:\r
-\r
-            {\r
-                khm_int32 inc_realms = 0;\r
-\r
-                if (KHM_FAILED(khc_read_int32(csp_params,\r
-                                              L"UseFullRealmList",\r
-                                              &inc_realms)) ||\r
-                    !inc_realms)\r
-                    goto _done_adding_all_realms;\r
-            }\r
-\r
-           if(ms)\r
-               PFREE(ms);\r
-\r
-           ms = khm_krb5_get_realm_list();\r
-           if(ms) {\r
-               for (t = ms; t && *t; t = multi_string_next(t)) {\r
-                   lr = SendMessage(d->hw_realm,\r
-                                     CB_FINDSTRINGEXACT,\r
-                                     (WPARAM) -1,\r
-                                     (LPARAM) t);\r
-                   if (lr != CB_ERR)\r
-                       continue;\r
-\r
-                   SendMessage(d->hw_realm,\r
-                                CB_ADDSTRING,\r
-                                0,\r
-                                (LPARAM) t);\r
-               }\r
-           }\r
-        _done_adding_all_realms:\r
-\r
-            /* set the current selection of the realms list */\r
-            if (defrealm) {\r
-                SendMessage(d->hw_realm,\r
-                            CB_SELECTSTRING,\r
-                            (WPARAM) -1,\r
-                            (LPARAM) defrealm);\r
-            } else {\r
-                SendMessage(d->hw_realm,\r
-                            CB_SETCURSEL,\r
-                            (WPARAM) 0,\r
-                            (LPARAM) 0);\r
-            }\r
-\r
-            if (defrealm)\r
-                PFREE(defrealm);\r
-\r
-            if (ms)\r
-                PFREE(ms);\r
-\r
-            /* now see about that default identity */\r
-            if (nc->ctx.identity) {\r
-                cb = sizeof(defident);\r
-                kcdb_identity_get_name(nc->ctx.identity,\r
-                                       defident,\r
-                                       &cb);\r
-            }\r
-\r
-            if (defident[0] == L'\0' &&\r
-                KHM_SUCCEEDED(kcdb_identity_get_default(&hident))) {\r
-                cb = sizeof(defident);\r
-                kcdb_identity_get_name(hident, defident, &cb);\r
-                kcdb_identity_release(hident);\r
-            }\r
-\r
-            if (defident[0] == L'\0') {\r
-                DWORD dw;\r
-\r
-                dw = ARRAYLENGTH(defident);\r
-                GetUserName(defident, &dw);\r
-            }\r
-\r
-            t = khm_get_realm_from_princ(defident);\r
-            if (t) {\r
-                /* there is a realm */\r
-                assert(t != defident);\r
-                *--t = L'\0';\r
-                t++;\r
-\r
-                SendMessage(d->hw_realm,\r
-                            CB_SELECTSTRING,\r
-                            (WPARAM) -1,\r
-                            (LPARAM) t);\r
-\r
-                SendMessage(d->hw_realm,\r
-                            WM_SETTEXT,\r
-                            0,\r
-                            (LPARAM) t);\r
-            }\r
-\r
-            if (defident[0] != L'\0') {\r
-                /* there is a username */\r
-                SendMessage(d->hw_username,\r
-                            CB_SELECTSTRING,\r
-                            (WPARAM) -1,\r
-                            (LPARAM) defident);\r
-\r
-                SendMessage(d->hw_username,\r
-                            WM_SETTEXT,\r
-                            0,\r
-                            (LPARAM) defident);\r
-            }\r
-\r
-            set_identity_from_ui(nc, d);\r
-        }\r
-        return TRUE;\r
-\r
-    case WMNC_IDENT_WMSG:\r
-        return handle_wnd_msg(nc, hwnd, uMsg, wParam, lParam);\r
-\r
-    case WMNC_IDENT_EXIT:\r
-        {\r
-#ifdef DEBUG\r
-            assert(d != NULL);\r
-#endif\r
-            khui_cw_lock_nc(nc);\r
-            nc->ident_aux = 0;\r
-            khui_cw_unlock_nc(nc);\r
-            \r
-            /* since we created all the windows as child windows of\r
-               the new creds window, they will be destroyed when that\r
-               window is destroyed. */\r
-            PFREE(d);\r
-        }\r
-        return TRUE;\r
-    }\r
-    return FALSE;\r
-}\r
-\r
-static khm_int32\r
-k5_ident_validate_name(khm_int32 msg_type,\r
-                      khm_int32 msg_subtype,\r
-                      khm_ui_4 uparam,\r
-                      void * vparam) {\r
-    krb5_principal princ = NULL;\r
-    char princ_name[KCDB_IDENT_MAXCCH_NAME];\r
-    kcdb_ident_name_xfer * nx;\r
-    krb5_error_code code;\r
-    wchar_t * atsign;\r
-\r
-    nx = (kcdb_ident_name_xfer *) vparam;\r
-\r
-    if(UnicodeStrToAnsi(princ_name, sizeof(princ_name),\r
-                        nx->name_src) == 0) {\r
-        nx->result = KHM_ERROR_INVALID_NAME;\r
-        return KHM_ERROR_SUCCESS;\r
-    }\r
-\r
-    assert(k5_identpro_ctx != NULL);\r
-\r
-    code = pkrb5_parse_name(k5_identpro_ctx,\r
-                            princ_name,\r
-                            &princ);\r
-\r
-    if (code) {\r
-        nx->result = KHM_ERROR_INVALID_NAME;\r
-        return KHM_ERROR_SUCCESS;\r
-    }\r
-\r
-    if (princ != NULL)\r
-        pkrb5_free_principal(k5_identpro_ctx,\r
-                             princ);\r
-\r
-    /* krb5_parse_name() accepts principal names with no realm or an\r
-       empty realm.  We don't. */\r
-    atsign = wcschr(nx->name_src, L'@');\r
-    if (atsign == NULL || atsign[1] == L'\0') {\r
-        nx->result = KHM_ERROR_INVALID_NAME;\r
-    } else {\r
-        nx->result = KHM_ERROR_SUCCESS;\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-static void\r
-k5_update_last_default_identity(khm_handle ident) {\r
-    wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-    khm_size cb;\r
-\r
-    cb = sizeof(idname);\r
-    if (KHM_FAILED(kcdb_identity_get_name(ident, idname, &cb)))\r
-        return;\r
-\r
-    assert(csp_params);\r
-\r
-    khc_write_string(csp_params, L"LastDefaultIdent", idname);\r
-}\r
-\r
-static khm_int32\r
-k5_ident_set_default_int(khm_handle def_ident) {\r
-    wchar_t id_ccname[KRB5_MAXCCH_CCNAME];\r
-    khm_size cb;\r
-    DWORD dw;\r
-    LONG l;\r
-    HKEY hk_ccname;\r
-    DWORD dwType;\r
-    DWORD dwSize;\r
-    wchar_t reg_ccname[KRB5_MAXCCH_CCNAME];\r
-\r
-#ifdef DEBUG\r
-    assert(def_ident != NULL);\r
-#endif\r
-\r
-    cb = sizeof(id_ccname);\r
-    if (KHM_FAILED(kcdb_identity_get_attr(def_ident, attr_id_krb5_ccname, NULL,\r
-                                          id_ccname, &cb))) {\r
-        khm_handle csp_ident = NULL;\r
-        khm_handle csp_k5 = NULL;\r
-\r
-        _reportf(L"The specified identity does not have the Krb5CCName property");\r
-\r
-        cb = sizeof(id_ccname);\r
-        if (KHM_SUCCEEDED(kcdb_identity_get_config(def_ident, 0, &csp_ident)) &&\r
-            KHM_SUCCEEDED(khc_open_space(csp_ident, CSNAME_KRB5CRED, 0, &csp_k5)) &&\r
-            KHM_SUCCEEDED(khc_read_string(csp_k5, L"DefaultCCName",\r
-                                          id_ccname, &cb))) {\r
-\r
-            _reportf(L"Found CC name in configuration [%s]", id_ccname);\r
-        } else {\r
-            /* last resort, use the name of the identity as the cc\r
-               name */\r
-            cb = sizeof(id_ccname);\r
-            if (KHM_FAILED(kcdb_identity_get_name(def_ident, id_ccname, &cb))) {\r
-                _reportf(L"Can't use name of identity as CCName");\r
-                _end_task();\r
-\r
-                id_ccname[0] = L'\0';\r
-            }\r
-        }\r
-\r
-        if (csp_k5)\r
-            khc_close_space(csp_k5);\r
-        if (csp_ident)\r
-            khc_close_space(csp_ident);\r
-\r
-        if (id_ccname[0] == L'\0')\r
-            return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    khm_krb5_canon_cc_name(id_ccname, sizeof(id_ccname));\r
-\r
-    _reportf(L"Found Krb5CCName property : %s", id_ccname);\r
-\r
-    StringCbLength(id_ccname, sizeof(id_ccname), &cb);\r
-    cb += sizeof(wchar_t);\r
-\r
-    _reportf(L"Setting default CC name in the registry");\r
-\r
-    l = RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\MIT\\kerberos5", 0,\r
-                     KEY_READ | KEY_WRITE, &hk_ccname);\r
-\r
-    if (l != ERROR_SUCCESS)\r
-        l = RegCreateKeyEx(HKEY_CURRENT_USER, L"Software\\MIT\\kerberos5", 0,\r
-                           NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,\r
-                           NULL, &hk_ccname, &dw);\r
-\r
-    if (l != ERROR_SUCCESS) {\r
-        _reportf(L"Can't create registry key : %d", l);\r
-        _end_task();\r
-        return KHM_ERROR_UNKNOWN;\r
-    }\r
-\r
-    dwSize = sizeof(reg_ccname);\r
-\r
-    l = RegQueryValueEx(hk_ccname, L"ccname", NULL, &dwType, (LPBYTE) reg_ccname,\r
-                        &dwSize);\r
-\r
-    if (l != ERROR_SUCCESS ||\r
-        dwType != REG_SZ ||\r
-        khm_krb5_cc_name_cmp(reg_ccname, id_ccname)) {\r
-\r
-        /* we have to write the new value in */\r
-            \r
-        l = RegSetValueEx(hk_ccname, L"ccname", 0, REG_SZ, (BYTE *) id_ccname,\r
-                          (DWORD) cb);\r
-    }\r
-\r
-    RegCloseKey(hk_ccname);\r
-\r
-    if (l == ERROR_SUCCESS) {\r
-        _reportf(L"Successfully set the default ccache");\r
-        k5_update_last_default_identity(def_ident);\r
-        return KHM_ERROR_SUCCESS;\r
-    } else {\r
-        _reportf(L"Can't set the registry value : %d", l);\r
-        return KHM_ERROR_UNKNOWN;\r
-    }\r
-}\r
-\r
-static khm_int32\r
-k5_ident_set_default(khm_int32 msg_type,\r
-                     khm_int32 msg_subtype,\r
-                     khm_ui_4 uparam,\r
-                     void * vparam) {\r
-\r
-    /* \r
-       Currently, setting the default identity simply sets the\r
-       "ccname" registry value at "Software\MIT\kerberos5".\r
-    */\r
-\r
-    if (uparam) {\r
-        /* an identity is being made default */\r
-        khm_handle def_ident = (khm_handle) vparam;\r
-        khm_int32 rv;\r
-\r
-#ifdef DEBUG\r
-        assert(def_ident != NULL);\r
-#endif\r
-\r
-        {\r
-            wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-            khm_size cb;\r
-\r
-            cb = sizeof(idname);\r
-            kcdb_identity_get_name(def_ident, idname, &cb);\r
-\r
-            _begin_task(0);\r
-            _report_cs1(KHERR_DEBUG_1, L"Setting default identity [%1!s!]", _cstr(idname));\r
-            _describe();\r
-        }\r
-\r
-        rv = k5_ident_set_default_int(def_ident);\r
-\r
-        _end_task();\r
-\r
-        return rv;\r
-\r
-    } else {\r
-        /* the default identity is being forgotten */\r
-\r
-        /* we don't really do anything about this case */\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-static khm_int32\r
-k5_ident_get_ui_cb(khm_int32 msg_type,\r
-                   khm_int32 msg_subtype,\r
-                   khm_ui_4 uparam,\r
-                   void * vparam) {\r
-    khui_ident_new_creds_cb * cb;\r
-\r
-    cb = (khui_ident_new_creds_cb *) vparam;\r
-\r
-    *cb = ui_cb;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-static khm_int32\r
-k5_ident_notify_create(khm_int32 msg_type,\r
-                       khm_int32 msg_subtype,\r
-                       khm_ui_4 uparam,\r
-                       void * vparam) {\r
-\r
-    /* a new identity has been created.  What we want to do at\r
-       this point is to check if the identity belongs to krb5\r
-       and to see if it is the default. */\r
-\r
-    krb5_ccache cc = NULL;\r
-    krb5_error_code code;\r
-    krb5_principal princ = NULL;\r
-    char * princ_nameA = NULL;\r
-    wchar_t princ_nameW[KCDB_IDENT_MAXCCH_NAME];\r
-    wchar_t id_nameW[KCDB_IDENT_MAXCCH_NAME];\r
-    khm_size cb;\r
-    khm_handle ident;\r
-\r
-    /* if there is a default identity already, we assume we don't need\r
-       to check this one. */\r
-\r
-    khm_handle def_ident;\r
-\r
-    if (KHM_SUCCEEDED(kcdb_identity_get_default(&def_ident))) {\r
-        kcdb_identity_release(def_ident);\r
-\r
-        return KHM_ERROR_SUCCESS;\r
-    }\r
-\r
-    ident = (khm_handle) vparam;\r
-\r
-    assert(k5_identpro_ctx != NULL);\r
-\r
-    code = pkrb5_cc_default(k5_identpro_ctx, &cc);\r
-    if (code)\r
-        goto _nc_cleanup;\r
-\r
-    code = pkrb5_cc_get_principal(k5_identpro_ctx,\r
-                                  cc,\r
-                                  &princ);\r
-    if (code)\r
-        goto _nc_cleanup;\r
-\r
-    code = pkrb5_unparse_name(k5_identpro_ctx,\r
-                              princ,\r
-                              &princ_nameA);\r
-    if (code)\r
-        goto _nc_cleanup;\r
-\r
-    AnsiStrToUnicode(princ_nameW,\r
-                     sizeof(princ_nameW),\r
-                     princ_nameA);\r
-\r
-    cb = sizeof(id_nameW);\r
-\r
-    if (KHM_FAILED(kcdb_identity_get_name(ident,\r
-                                          id_nameW,\r
-                                          &cb)))\r
-        goto _nc_cleanup;\r
-\r
-    if (!wcscmp(id_nameW, princ_nameW)) {\r
-        kcdb_identity_set_default_int(ident);\r
-    }\r
-\r
- _nc_cleanup:\r
-    if (princ_nameA)\r
-        pkrb5_free_unparsed_name(k5_identpro_ctx,\r
-                                 princ_nameA);\r
-    if (princ)\r
-        pkrb5_free_principal(k5_identpro_ctx,\r
-                             princ);\r
-    if (cc)\r
-        pkrb5_cc_close(k5_identpro_ctx, cc);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-struct k5_ident_update_data {\r
-    khm_handle identity;\r
-\r
-    FILETIME   ft_expire;      /* expiration */\r
-    FILETIME   ft_issue;       /* issue */\r
-    FILETIME   ft_rexpire;     /* renew expiration */\r
-    wchar_t    ccname[KRB5_MAXCCH_CCNAME];\r
-    khm_int32  k5_flags;\r
-};\r
-\r
-/* The logic here has to reflect the logic in khm_krb5_list_tickets().\r
-   We use this to handle an identity update request because some other\r
-   plug-in or maybe NetIDMgr itself is about to do something\r
-   important(tm) with the identity and needs to make sure that the\r
-   properties of the identity are up-to-date. */\r
-static khm_int32 KHMAPI\r
-k5_ident_update_apply_proc(khm_handle cred,\r
-                           void * rock) {\r
-    struct k5_ident_update_data * d = (struct k5_ident_update_data *) rock;\r
-    khm_handle ident = NULL;\r
-    khm_int32 t;\r
-    khm_int32 flags;\r
-    FILETIME t_cexpire;\r
-    FILETIME t_rexpire;\r
-    khm_size cb;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if (KHM_FAILED(kcdb_cred_get_type(cred, &t)) ||\r
-        t != credtype_id_krb5 ||\r
-        KHM_FAILED(kcdb_cred_get_identity(cred, &ident)))\r
-\r
-        return KHM_ERROR_SUCCESS;\r
-\r
-    if (!kcdb_identity_is_equal(ident,d->identity))\r
-\r
-        goto _cleanup;\r
-\r
-    if (KHM_FAILED(kcdb_cred_get_flags(cred, &flags)))\r
-\r
-        flags = 0;\r
-\r
-    if (flags & KCDB_CRED_FLAG_INITIAL) {\r
-        cb = sizeof(t_cexpire);\r
-        if (KHM_SUCCEEDED(kcdb_cred_get_attr(cred,\r
-                                             KCDB_ATTR_EXPIRE,\r
-                                             NULL,\r
-                                             &t_cexpire,\r
-                                             &cb))) {\r
-            if ((d->ft_expire.dwLowDateTime == 0 &&\r
-                 d->ft_expire.dwHighDateTime == 0) ||\r
-                CompareFileTime(&t_cexpire, &d->ft_expire) > 0) {\r
-                goto update_identity;\r
-            }\r
-        }\r
-    }\r
-\r
-    goto _cleanup;\r
-\r
- update_identity:\r
-\r
-    d->ft_expire = t_cexpire;\r
-\r
-    cb = sizeof(d->ccname);\r
-    if (KHM_FAILED(kcdb_cred_get_attr(cred, KCDB_ATTR_LOCATION, NULL, d->ccname, &cb))) {\r
-        d->ccname[0] = L'\0';\r
-    }\r
-\r
-    cb = sizeof(d->k5_flags);\r
-    if (KHM_FAILED(kcdb_cred_get_attr(cred, attr_id_krb5_flags, NULL,\r
-                                         &d->k5_flags, &cb))) {\r
-        d->k5_flags = 0;\r
-    }\r
-\r
-    cb = sizeof(d->ft_issue);\r
-    if (KHM_FAILED(kcdb_cred_get_attr(cred, KCDB_ATTR_ISSUE, NULL, &d->ft_issue, &cb))) {\r
-        ZeroMemory(&d->ft_issue, sizeof(d->ft_issue));\r
-    }\r
-\r
-    cb = sizeof(t_rexpire);\r
-    if ((d->k5_flags & TKT_FLG_RENEWABLE) &&\r
-        KHM_SUCCEEDED(kcdb_cred_get_attr(cred,\r
-                                         KCDB_ATTR_RENEW_EXPIRE,\r
-                                         NULL,\r
-                                         &t_rexpire,\r
-                                         &cb))) {\r
-        d->ft_rexpire = t_rexpire;\r
-    } else {\r
-        ZeroMemory(&d->ft_rexpire, sizeof(d->ft_rexpire));\r
-    }\r
-\r
- _cleanup:\r
-    if (ident)\r
-        kcdb_identity_release(ident);\r
-\r
-    return rv;\r
-}\r
-\r
-static khm_int32\r
-k5_ident_update(khm_int32 msg_type,\r
-                khm_int32 msg_subtype,\r
-                khm_ui_4 uparam,\r
-                void * vparam) {\r
-\r
-#if 0\r
-    struct k5_ident_update_data d;\r
-#endif\r
-    khm_handle ident;\r
-    khm_handle tident;\r
-    krb5_ccache cc = NULL;\r
-    char * ccname;\r
-    krb5_error_code code;\r
-    khm_size cb;\r
-    wchar_t wid_ccname[MAX_PATH];\r
-    wchar_t w_ccname[MAX_PATH];\r
-\r
-    ident = (khm_handle) vparam;\r
-    if (ident == NULL)\r
-        return KHM_ERROR_SUCCESS;\r
-\r
-#if 0\r
-    /* we are going to skip doing this here since\r
-       khm_krb5_list_tickets() performs this function for us each time\r
-       we enumerate tickets.  Since it also gets run each time our\r
-       list of tickets changes and since we are basing this operation\r
-       on existing tickets, we are unlikely to find anything new\r
-       here.  */\r
-    ZeroMemory(&d, sizeof(d));\r
-    d.identity = ident;\r
-\r
-    kcdb_credset_apply(NULL,\r
-                       k5_ident_update_apply_proc,\r
-                       (void *) &d);\r
-\r
-    if (d.ft_expire.dwLowDateTime != 0 ||\r
-        d.ft_expire.dwHighDateTime != 0) {\r
-\r
-        /* we found a TGT */\r
-\r
-        kcdb_identity_set_attr(ident, KCDB_ATTR_EXPIRE,\r
-                               &d.ft_expire, sizeof(d.ft_expire));\r
-        if (d.ft_issue.dwLowDateTime != 0 ||\r
-            d.ft_issue.dwHighDateTime != 0)\r
-            kcdb_identity_set_attr(ident, KCDB_ATTR_ISSUE,\r
-                                   &d.ft_issue, sizeof(d.ft_issue));\r
-        else\r
-            kcdb_identity_set_attr(ident, KCDB_ATTR_ISSUE, NULL, 0);\r
-\r
-        if (d.ft_rexpire.dwLowDateTime != 0 ||\r
-            d.ft_rexpire.dwHighDateTime != 0)\r
-            kcdb_identity_set_attr(ident, KCDB_ATTR_RENEW_EXPIRE,\r
-                                   &d.ft_rexpire, sizeof(d.ft_rexpire));\r
-        else\r
-            kcdb_identity_set_attr(ident, KCDB_ATTR_RENEW_EXPIRE, NULL, 0);\r
-\r
-        kcdb_identity_set_attr(ident, attr_id_krb5_flags,\r
-                               &d.k5_flags, sizeof(d.k5_flags));\r
-\r
-        if (d.ccname[0])\r
-            kcdb_identity_set_attr(ident, attr_id_krb5_ccname,\r
-                                   d.ccname, KCDB_CBSIZE_AUTO);\r
-        else\r
-            kcdb_identity_set_attr(ident, attr_id_krb5_ccname, NULL, 0);\r
-\r
-    } else {\r
-        /* Clear out the attributes.  We don't have any information\r
-           about this identity */\r
-        kcdb_identity_set_attr(ident, KCDB_ATTR_EXPIRE, NULL, 0);\r
-        kcdb_identity_set_attr(ident, KCDB_ATTR_ISSUE, NULL, 0);\r
-        kcdb_identity_set_attr(ident, KCDB_ATTR_RENEW_EXPIRE, NULL, 0);\r
-        kcdb_identity_set_attr(ident, attr_id_krb5_flags, NULL, 0);\r
-        kcdb_identity_set_attr(ident, attr_id_krb5_ccname, NULL, 0);\r
-    }\r
-#endif\r
-\r
-    if (KHM_SUCCEEDED(kcdb_identity_get_default(&tident))) {\r
-        kcdb_identity_release(tident);\r
-        goto _iu_cleanup;\r
-    }\r
-\r
-    cb = sizeof(wid_ccname);\r
-    if (KHM_FAILED(kcdb_identity_get_attr(ident,\r
-                                          attr_id_krb5_ccname,\r
-                                          NULL,\r
-                                          wid_ccname,\r
-                                          &cb)))\r
-        goto _iu_cleanup;\r
-\r
-    if(k5_identpro_ctx == NULL)\r
-        goto _iu_cleanup;\r
-\r
-    code = pkrb5_cc_default(k5_identpro_ctx, &cc);\r
-    if (code)\r
-        goto _iu_cleanup;\r
-\r
-    ccname = pkrb5_cc_get_name(k5_identpro_ctx, cc);\r
-    if (ccname == NULL)\r
-        goto _iu_cleanup;\r
-\r
-    AnsiStrToUnicode(w_ccname, sizeof(w_ccname), ccname);\r
-\r
-    khm_krb5_canon_cc_name(w_ccname, sizeof(w_ccname));\r
-    khm_krb5_canon_cc_name(wid_ccname, sizeof(wid_ccname));\r
-\r
-    if (!_wcsicmp(w_ccname, wid_ccname))\r
-        kcdb_identity_set_default_int(ident);\r
-\r
- _iu_cleanup:\r
-    if (cc && k5_identpro_ctx)\r
-        pkrb5_cc_close(k5_identpro_ctx, cc);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-static khm_boolean\r
-k5_refresh_default_identity(krb5_context ctx) {\r
-    /* just like notify_create, except now we set the default identity\r
-       based on what we find in the configuration */\r
-    krb5_ccache cc = NULL;\r
-    krb5_error_code code;\r
-    krb5_principal princ = NULL;\r
-    char * princ_nameA = NULL;\r
-    wchar_t princ_nameW[KCDB_IDENT_MAXCCH_NAME];\r
-    char * ccname = NULL;\r
-    khm_handle ident = NULL;\r
-    khm_boolean found_default = FALSE;\r
-\r
-    assert(ctx != NULL);\r
-\r
-    _begin_task(0);\r
-    _report_cs0(KHERR_DEBUG_1, L"Refreshing default identity");\r
-    _describe();\r
-\r
-    code = pkrb5_cc_default(ctx, &cc);\r
-    if (code) {\r
-        _reportf(L"Can't open default ccache. code=%d", code);\r
-        goto _nc_cleanup;\r
-    }\r
-    \r
-    code = pkrb5_cc_get_principal(ctx, cc, &princ);\r
-    if (code) {\r
-        /* try to determine the identity from the ccache name */\r
-        ccname = pkrb5_cc_get_name(ctx, cc);\r
-\r
-        if (ccname) {\r
-            char * namepart = strchr(ccname, ':');\r
-\r
-            _reportf(L"CC name is [%S]", ccname);\r
-\r
-            if (namepart == NULL)\r
-                namepart = ccname;\r
-            else\r
-                namepart++;\r
-\r
-            _reportf(L"Checking if [%S] is a valid identity name", namepart);\r
-\r
-            AnsiStrToUnicode(princ_nameW, sizeof(princ_nameW), namepart);\r
-            if (kcdb_identity_is_valid_name(princ_nameW)) {\r
-                kcdb_identity_create(princ_nameW, KCDB_IDENT_FLAG_CREATE, &ident);\r
-                if (ident) {\r
-                    _reportf(L"Setting [%S] as the default identity", namepart);\r
-                    kcdb_identity_set_default_int(ident);\r
-                    found_default = TRUE;\r
-                }\r
-            }\r
-        } else {\r
-            _reportf(L"Can't determine ccache name");\r
-        }\r
-\r
-        goto _nc_cleanup;\r
-    }\r
-\r
-    code = pkrb5_unparse_name(ctx, princ, &princ_nameA);\r
-    if (code)\r
-        goto _nc_cleanup;\r
-\r
-    AnsiStrToUnicode(princ_nameW, sizeof(princ_nameW), princ_nameA);\r
-\r
-    _reportf(L"Found principal [%s]", princ_nameW);\r
-\r
-    if (KHM_FAILED(kcdb_identity_create(princ_nameW, KCDB_IDENT_FLAG_CREATE, &ident))) {\r
-        _reportf(L"Failed to create identity");\r
-        goto _nc_cleanup;\r
-    }\r
-\r
-    _reportf(L"Setting default identity to [%s]", princ_nameW);\r
-    kcdb_identity_set_default_int(ident);\r
-\r
-    found_default = TRUE;\r
-\r
- _nc_cleanup:\r
-\r
-    _end_task();\r
-\r
-    if (princ_nameA)\r
-        pkrb5_free_unparsed_name(ctx, princ_nameA);\r
-\r
-    if (princ)\r
-        pkrb5_free_principal(ctx, princ);\r
-\r
-    if (cc)\r
-        pkrb5_cc_close(ctx, cc);\r
-\r
-    if (ident)\r
-        kcdb_identity_release(ident);\r
-\r
-    return found_default;\r
-}\r
-\r
-static khm_int32\r
-k5_ident_init(khm_int32 msg_type,\r
-              khm_int32 msg_subtype,\r
-              khm_ui_4 uparam,\r
-              void * vparam) {\r
-\r
-    khm_boolean found_default;\r
-    khm_handle ident;\r
-\r
-    found_default = k5_refresh_default_identity(k5_identpro_ctx);\r
-\r
-    if (!found_default) {\r
-        wchar_t widname[KCDB_IDENT_MAXCCH_NAME];\r
-        khm_size cb;\r
-\r
-        cb = sizeof(widname);\r
-\r
-        assert(csp_params);\r
-\r
-        if (KHM_SUCCEEDED(khc_read_string(csp_params, L"LastDefaultIdent",\r
-                                          widname, &cb))) {\r
-            ident = NULL;\r
-            kcdb_identity_create(widname, KCDB_IDENT_FLAG_CREATE, &ident);\r
-            if (ident) {\r
-                kcdb_identity_set_default_int(ident);\r
-                kcdb_identity_release(ident);\r
-\r
-                found_default = TRUE;\r
-            }\r
-        }\r
-    }\r
-\r
-    if (!found_default) {\r
-\r
-        /* There was no default ccache and we don't have a\r
-           "LastDefaultIdent" value. Next we see if there are any\r
-           identities that have credentials which have a Krb5CCName\r
-           property (i.e. an identity that has a Kerberos 5 TGT), and\r
-           make it the default.\r
-\r
-           Note that since the Krb5Ident plug-in has a dependency on\r
-           Krb5Cred, by the time this code runs, we already have a\r
-           listing of Kerberos 5 tickets and identities. */\r
-\r
-        wchar_t * idlist = NULL;\r
-        wchar_t * thisid;\r
-        khm_size cb = 0;\r
-        khm_size n_idents = 0;\r
-        khm_int32 rv;\r
-        wchar_t ccname[KRB5_MAXCCH_CCNAME];\r
-        FILETIME ft_expire;\r
-        FILETIME ft_now;\r
-        FILETIME ft_threshold;\r
-        BOOL match_all = FALSE;\r
-\r
-        rv = kcdb_identity_enum(0, 0, NULL, &cb, &n_idents);\r
-\r
-        TimetToFileTimeInterval(5 * 60, &ft_threshold);\r
-        GetSystemTimeAsFileTime(&ft_now);\r
-        ft_now = FtAdd(&ft_now, &ft_threshold);\r
-\r
-        while (rv == KHM_ERROR_TOO_LONG && n_idents > 0) {\r
-            if (idlist) {\r
-                PFREE(idlist);\r
-                idlist = NULL;\r
-            }\r
-\r
-            idlist = PMALLOC(cb);\r
-\r
-            if (idlist == NULL)\r
-                break;\r
-\r
-            rv = kcdb_identity_enum(0, 0, idlist, &cb, &n_idents);\r
-        }\r
-\r
-        if (KHM_SUCCEEDED(rv)) {\r
-\r
-            /* first we try to find an identity that has a valid TGT.\r
-               If that fails, then we try to find an identity with\r
-               *any* TGT. */\r
-\r
-        try_again:\r
-\r
-            for (thisid = idlist;\r
-                 thisid && *thisid && !found_default;\r
-                 thisid = multi_string_next(thisid)) {\r
-\r
-                if (KHM_SUCCEEDED(kcdb_identity_create(thisid, 0, &ident))) {\r
-                    khm_size cb_ft = sizeof(FILETIME);\r
-                    cb = sizeof(ccname);\r
-\r
-                    if (KHM_SUCCEEDED(kcdb_identity_get_attr(ident, attr_id_krb5_ccname,\r
-                                                             NULL, ccname, &cb)) &&\r
-                        (match_all ||\r
-                         (KHM_SUCCEEDED(kcdb_identity_get_attr(ident, KCDB_ATTR_EXPIRE,\r
-                                                               NULL, &ft_expire, &cb_ft)) &&\r
-                          CompareFileTime(&ft_expire, &ft_now) > 0))) {\r
-\r
-                        /* found one */\r
-                        k5_ident_set_default_int(ident);\r
-                        kcdb_identity_set_default_int(ident);\r
-                        found_default = TRUE;\r
-\r
-                    }\r
-\r
-                    kcdb_identity_release(ident);\r
-                    ident = NULL;\r
-                }\r
-            }\r
-\r
-            if (!found_default && !match_all) {\r
-                match_all = TRUE;\r
-                goto try_again;\r
-            }\r
-        }\r
-\r
-        if (idlist) {\r
-            PFREE(idlist);\r
-            idlist = NULL;\r
-        }\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-static khm_int32\r
-k5_ident_exit(khm_int32 msg_type,\r
-              khm_int32 msg_subtype,\r
-              khm_ui_4 uparam,\r
-              void * vparam) {\r
-    /* don't really do anything */\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* forward dcl */\r
-khm_int32 KHMAPI\r
-k5_ident_name_comp_func(const void * dl, khm_size cb_dl,\r
-                        const void * dr, khm_size cb_dr);\r
-\r
-static khm_int32\r
-k5_ident_compare_name(khm_int32 msg_type,\r
-                      khm_int32 msg_subtype,\r
-                      khm_ui_4 uparam,\r
-                      void * vparam) {\r
-    kcdb_ident_name_xfer *px;\r
-\r
-    px = (kcdb_ident_name_xfer *) vparam;\r
-\r
-    /* note that k5_ident_name_comp_func() ignores the size\r
-       specifiers.  So we can just pass in 0's. */\r
-    px->result = k5_ident_name_comp_func(px->name_src, 0,\r
-                                         px->name_alt, 0);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-#if 0\r
-/* copy and paste template for ident provider messages */\r
-static khm_int32\r
-k5_ident_(khm_int32 msg_type,\r
-          khm_int32 msg_subtype,\r
-          khm_ui_4 uparam,\r
-          void * vparam) {\r
-}\r
-#endif\r
-\r
-khm_int32 KHMAPI \r
-k5_msg_ident(khm_int32 msg_type, \r
-               khm_int32 msg_subtype, \r
-               khm_ui_4 uparam, \r
-               void * vparam)\r
-{\r
-    switch(msg_subtype) {\r
-    case KMSG_IDENT_INIT:\r
-        return k5_ident_init(msg_type,\r
-                             msg_subtype,\r
-                             uparam,\r
-                             vparam);\r
-\r
-    case KMSG_IDENT_EXIT:\r
-        return k5_ident_exit(msg_type,\r
-                             msg_subtype,\r
-                             uparam,\r
-                             vparam);\r
-\r
-    case KMSG_IDENT_VALIDATE_NAME:\r
-        return k5_ident_validate_name(msg_type,\r
-                                      msg_subtype,\r
-                                      uparam,\r
-                                      vparam);\r
-\r
-    case KMSG_IDENT_VALIDATE_IDENTITY:\r
-        /* TODO: handle KMSG_IDENT_VALIDATE_IDENTITY */\r
-        break;\r
-\r
-    case KMSG_IDENT_CANON_NAME:\r
-        /* TODO: handle KMSG_IDENT_CANON_NAME */\r
-        break;\r
-\r
-    case KMSG_IDENT_COMPARE_NAME:\r
-        return k5_ident_compare_name(msg_type,\r
-                                     msg_subtype,\r
-                                     uparam,\r
-                                     vparam);\r
-\r
-    case KMSG_IDENT_SET_DEFAULT:\r
-        return k5_ident_set_default(msg_type,\r
-                                    msg_subtype,\r
-                                    uparam,\r
-                                    vparam);\r
-\r
-    case KMSG_IDENT_SET_SEARCHABLE:\r
-        /* TODO: handle KMSG_IDENT_SET_SEARCHABLE */\r
-        break;\r
-\r
-    case KMSG_IDENT_GET_INFO:\r
-        /* TODO: handle KMSG_IDENT_GET_INFO */\r
-        break;\r
-\r
-    case KMSG_IDENT_UPDATE:\r
-        return k5_ident_update(msg_type,\r
-                               msg_subtype,\r
-                               uparam,\r
-                               vparam);\r
-\r
-    case KMSG_IDENT_ENUM_KNOWN:\r
-        /* TODO: handle KMSG_IDENT_ENUM_KNOWN */\r
-        break;\r
-\r
-    case KMSG_IDENT_GET_UI_CALLBACK:\r
-        return k5_ident_get_ui_cb(msg_type,\r
-                                  msg_subtype,\r
-                                  uparam,\r
-                                  vparam);\r
-\r
-    case KMSG_IDENT_NOTIFY_CREATE:\r
-        return k5_ident_notify_create(msg_type,\r
-                                      msg_subtype,\r
-                                      uparam,\r
-                                      vparam);\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* note that we are ignoring the size specifiers.  We can do that\r
-   because we are guaranteed that dl and dr point to NULL terminated\r
-   unicode strings when used with credential data buffers.  We also\r
-   use the fact that we are ignoring the size specifiers when we call\r
-   this function from k5_ident_compare_name() to avoid calculating the\r
-   length of the string. */\r
-khm_int32 KHMAPI\r
-k5_ident_name_comp_func(const void * dl, khm_size cb_dl,\r
-                        const void * dr, khm_size cb_dr) {\r
-    wchar_t * idl = (wchar_t *) dl;\r
-    wchar_t * idr = (wchar_t *) dr;\r
-    wchar_t * rl;\r
-    wchar_t * rr;\r
-    khm_int32 r;\r
-\r
-    rl = khm_get_realm_from_princ(idl);\r
-    rr = khm_get_realm_from_princ(idr);\r
-\r
-    if (rl == NULL && rr == NULL)\r
-        return wcscmp(idl, idr);\r
-    else if (rl == NULL)\r
-        return 1;\r
-    else if (rr == NULL)\r
-        return -1;\r
-\r
-    r = wcscmp(rl, rr);\r
-    if (r == 0)\r
-        return wcscmp(idl, idr);\r
-    else\r
-        return r;\r
-}\r
-\r
-\r
-/* Identity change notification thread */\r
-\r
-HANDLE h_ccname_exit_event;\r
-HANDLE h_ccname_thread;\r
-\r
-DWORD WINAPI k5_ccname_monitor_thread(LPVOID lpParameter) {\r
-    krb5_context ctx = 0;\r
-\r
-    HKEY hk_ccname;\r
-    HANDLE h_notify;\r
-    HANDLE h_waits[2];\r
-\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    DWORD dwType;\r
-    DWORD dwSize;\r
-    DWORD dwDisp;\r
-    wchar_t reg_ccname[KRB5_MAXCCH_CCNAME];\r
-    LONG l;\r
-\r
-    PDESCTHREAD(L"Krb5 CCName Monitor", L"Krb5");\r
-\r
-    l = RegOpenKeyEx(HKEY_CURRENT_USER,\r
-                     L"Software\\MIT\\kerberos5",\r
-                     0,\r
-                     KEY_READ | KEY_WRITE,\r
-                     &hk_ccname);\r
-\r
-    if (l != ERROR_SUCCESS)\r
-        l = RegCreateKeyEx(HKEY_CURRENT_USER,\r
-                           L"Software\\MIT\\kerberos5",\r
-                           0,\r
-                           NULL,\r
-                           REG_OPTION_NON_VOLATILE,\r
-                           KEY_READ | KEY_WRITE,\r
-                           NULL,\r
-                           &hk_ccname,\r
-                           &dwDisp);\r
-\r
-    if (l != ERROR_SUCCESS) {\r
-        rv = KHM_ERROR_UNKNOWN;\r
-        goto _exit;\r
-    }\r
-\r
-    dwSize = sizeof(reg_ccname);\r
-    \r
-    l = RegQueryValueEx(hk_ccname,\r
-                        L"ccname",\r
-                        NULL,\r
-                        &dwType,\r
-                        (LPBYTE) reg_ccname,\r
-                        &dwSize);\r
-\r
-    if (l != ERROR_SUCCESS ||\r
-        dwType != REG_SZ) {\r
-\r
-        reg_ccname[0] = L'\0';\r
-    }\r
-\r
-    l = pkrb5_init_context(&ctx);\r
-\r
-    if (l)\r
-        goto _exit_0;\r
-\r
-    h_notify = CreateEvent(NULL, FALSE, FALSE, L"Local\\Krb5CCNameChangeNotifier");\r
-\r
-    if (h_notify == NULL)\r
-        goto _exit_0;\r
-\r
-    /* begin wait loop */\r
-\r
-    h_waits[0] = h_ccname_exit_event;\r
-    h_waits[1] = h_notify;\r
-\r
-    do {\r
-        DWORD dwrv;\r
-\r
-        l = RegNotifyChangeKeyValue(hk_ccname, FALSE,\r
-                                    REG_NOTIFY_CHANGE_LAST_SET,\r
-                                    h_notify, TRUE);\r
-\r
-        if (l != ERROR_SUCCESS) {\r
-            rv = KHM_ERROR_UNKNOWN;\r
-            break;\r
-        }\r
-\r
-        dwrv = WaitForMultipleObjects(2, h_waits, FALSE, INFINITE);\r
-\r
-        if (dwrv == WAIT_OBJECT_0) {\r
-            /* exit! */\r
-            break;\r
-\r
-        } else if (dwrv == WAIT_OBJECT_0 + 1) {\r
-            /* change notify! */\r
-            wchar_t new_ccname[KRB5_MAXCCH_CCNAME];\r
-\r
-            dwSize = sizeof(new_ccname);\r
-    \r
-            l = RegQueryValueEx(hk_ccname,\r
-                                L"ccname",\r
-                                NULL,\r
-                                &dwType,\r
-                                (LPBYTE) new_ccname,\r
-                                &dwSize);\r
-\r
-            if (l != ERROR_SUCCESS ||\r
-                dwType != REG_SZ) {\r
-                new_ccname[0] = L'\0';\r
-            }\r
-\r
-            if (_wcsicmp(new_ccname, reg_ccname)) {\r
-                k5_refresh_default_identity(ctx);\r
-                StringCbCopy(reg_ccname, sizeof(reg_ccname), new_ccname);\r
-            }\r
-\r
-        } else {\r
-            /* something went wrong */\r
-            rv = KHM_ERROR_UNKNOWN;\r
-            break;\r
-        }\r
-\r
-    } while (TRUE);\r
-\r
-    CloseHandle(h_notify);\r
-\r
- _exit_0:\r
-\r
-    RegCloseKey(hk_ccname);\r
-\r
-    if (ctx)\r
-        pkrb5_free_context(ctx);\r
-\r
- _exit:\r
-    ExitThread(rv);\r
-\r
-    /* not reached */\r
-    return rv;\r
-}\r
-\r
-khm_int32\r
-k5_msg_system_idpro(khm_int32 msg_type, khm_int32 msg_subtype,\r
-                    khm_ui_4 uparam, void * vparam) {\r
-\r
-    switch(msg_subtype) {\r
-    case KMSG_SYSTEM_INIT:\r
-        {\r
-\r
-            pkrb5_init_context(&k5_identpro_ctx);\r
-            kcdb_identity_set_type(credtype_id_krb5);\r
-\r
-            if (KHM_FAILED(kcdb_type_get_id(TYPENAME_KRB5_PRINC, \r
-                                            &type_id_krb5_princ))) {\r
-                kcdb_type dt;\r
-                kcdb_type * pstr;\r
-\r
-                kcdb_type_get_info(KCDB_TYPE_STRING, &pstr);\r
-\r
-                ZeroMemory(&dt, sizeof(dt));\r
-                dt.name = TYPENAME_KRB5_PRINC;\r
-                dt.id = KCDB_TYPE_INVALID;\r
-                dt.flags = KCDB_TYPE_FLAG_CB_AUTO;\r
-                dt.cb_min = pstr->cb_min;\r
-                dt.cb_max = pstr->cb_max;\r
-                dt.toString = pstr->toString;\r
-                dt.isValid = pstr->isValid;\r
-                dt.comp = k5_ident_name_comp_func;\r
-                dt.dup = pstr->dup;\r
-\r
-                kcdb_type_register(&dt, &type_id_krb5_princ);\r
-\r
-                type_regd_krb5_princ = TRUE;\r
-\r
-                kcdb_type_release_info(pstr);\r
-            }\r
-\r
-            if (type_id_krb5_princ != -1) {\r
-                kcdb_attrib * attr;\r
-\r
-                kcdb_attrib_get_info(KCDB_ATTR_ID_NAME, &attr);\r
-\r
-                attr->type = type_id_krb5_princ;\r
-\r
-                kcdb_attrib_release_info(attr);\r
-            }\r
-\r
-            h_ccname_exit_event = CreateEvent(NULL, FALSE, FALSE, NULL);\r
-            if (h_ccname_exit_event) {\r
-                h_ccname_thread = CreateThread(NULL,\r
-                                               200 * 1024,\r
-                                               k5_ccname_monitor_thread,\r
-                                               NULL,\r
-                                               0,\r
-                                               NULL);\r
-            } else {\r
-                h_ccname_thread = NULL;\r
-            }\r
-        }\r
-        break;\r
-\r
-    case KMSG_SYSTEM_EXIT:\r
-        {\r
-\r
-            if (h_ccname_thread) {\r
-                SetEvent(h_ccname_exit_event);\r
-                WaitForSingleObject(h_ccname_thread, INFINITE);\r
-                CloseHandle(h_ccname_thread);\r
-                CloseHandle(h_ccname_exit_event);\r
-\r
-                h_ccname_exit_event = NULL;\r
-                h_ccname_thread = NULL;\r
-            }\r
-\r
-            if (k5_identpro_ctx) {\r
-                pkrb5_free_context(k5_identpro_ctx);\r
-                k5_identpro_ctx = NULL;\r
-            }\r
-\r
-            if (type_id_krb5_princ != -1) {\r
-                kcdb_attrib * attr;\r
-\r
-                kcdb_attrib_get_info(KCDB_ATTR_ID_NAME, &attr);\r
-\r
-                attr->type = KCDB_TYPE_STRING;\r
-\r
-                kcdb_attrib_release_info(attr);\r
-            }\r
-\r
-            /* allow a brief moment for any stale references to die */\r
-            Sleep(100);\r
-\r
-            if (type_regd_krb5_princ) {\r
-                kcdb_type_unregister(type_id_krb5_princ);\r
-            }\r
-        }\r
-        break;\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-khm_int32 KHMAPI\r
-k5_ident_callback(khm_int32 msg_type, khm_int32 msg_subtype,\r
-                  khm_ui_4 uparam, void * vparam) {\r
-    switch(msg_type) {\r
-    case KMSG_SYSTEM:\r
-        return k5_msg_system_idpro(msg_type, msg_subtype, uparam, vparam);\r
-\r
-    case KMSG_IDENT:\r
-        return k5_msg_ident(msg_type, msg_subtype, uparam, vparam);\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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;
+
+static
+void
+trim_str(wchar_t * s, khm_size cch) {
+    wchar_t * c, * last_ws;
+
+    for (c = s; *c && iswspace(*c) && ((khm_size)(c - s)) < cch; c++);
+
+    if (((khm_size)(c - s)) >= cch)
+        return;
+
+    if (c != s && ((khm_size)(c - s)) < cch) {
+#if _MSC_VER >= 1400
+        wmemmove_s(s, cch, c, cch - ((khm_size)(c - s)));
+#else
+        memmove(s, c, (cch - ((khm_size)(c - s))) * sizeof(wchar_t));
+#endif
+    }
+
+    last_ws = NULL;
+    for (c = s; *c && ((khm_size)(c - s)) < cch; c++) {
+        if (!iswspace(*c))
+            last_ws = NULL;
+        else if (last_ws == NULL)
+            last_ws = c;
+    }
+
+    if (last_ws)
+        *last_ws = L'\0';
+}
+
+/* Runs in the UI thread */
+int 
+k5_get_realm_from_nc(khui_new_creds * nc, 
+                     wchar_t * buf, 
+                     khm_size cch_buf) {
+    k5_new_cred_data * d;
+    khm_size s;
+
+    d = (k5_new_cred_data *) nc->ident_aux;
+    buf[0] = L'\0';
+    GetWindowText(d->hw_realm, buf, (int) cch_buf);
+    trim_str(buf, cch_buf);
+
+    StringCchLength(buf, cch_buf, &s);
+
+    return (int) s;
+}
+
+/* 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;
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+
+    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));
+    trim_str(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);
+
+    if (cch >= KCDB_IDENT_MAXCCH_NAME - 3) {
+        /* has to allow space for the '@' and at least a single
+           character realm, and the NULL terminator. */
+        rv = KHM_ERROR_TOO_LONG;
+        goto _set_null_ident;
+    }
+
+    realm = un + cch;   /* now points at terminating NULL */
+    cch_left = KCDB_IDENT_MAXCCH_NAME - cch;
+
+    *realm++ = L'@';
+    *realm = L'\0';
+    cch_left--;
+
+    cch = GetWindowTextLength(d->hw_realm);
+    if (cch == 0 || cch >= cch_left) {
+        rv = KHM_ERROR_INVALID_NAME;
+        goto _set_null_ident;
+    }
+
+    GetWindowText(d->hw_realm, realm, (int) cch_left);
+    trim_str(realm, cch_left);
+
+ _set_ident:
+    if (KHM_FAILED(rv = 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_new_creds_by_type * nct = NULL;
+        wchar_t cmsg[256];
+
+        khui_cw_find_type(nc, credtype_id_krb5, &nct);
+        if (nct && nct->hwnd_panel) {
+
+            switch(rv) {
+            case KHM_ERROR_TOO_LONG:
+                LoadString(hResModule, IDS_NCERR_IDENT_TOO_LONG,
+                           cmsg, ARRAYLENGTH(cmsg));
+                break;
+
+            case KHM_ERROR_INVALID_NAME:
+                LoadString(hResModule, IDS_NCERR_IDENT_INVALID,
+                           cmsg, ARRAYLENGTH(cmsg));
+                break;
+
+            default:
+                LoadString(hResModule, IDS_NCERR_IDENT_UNKNOWN,
+                           cmsg, ARRAYLENGTH(cmsg));
+            }
+
+            SendMessage(nct->hwnd_panel,
+                        KHUI_WM_NC_NOTIFY,
+                        MAKEWPARAM(0, K5_SET_CRED_MSG),
+                        (LPARAM) cmsg);
+        }
+
+        khui_cw_set_primary_id(nc, NULL);
+    }
+    return;
+}
+
+/* runs in the UI thread */
+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;
+    int idx;
+
+    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));
+    trim_str(un, ARRAYLENGTH(un));
+
+    un_realm = khm_get_realm_from_princ(un);
+
+    if (un_realm == NULL) {
+        EnableWindow(d->hw_realm, TRUE);
+        return FALSE;
+    }
+
+    if (ctrl_id_src == K5_NCID_UN) {
+
+        idx = (int)SendMessage(d->hw_realm,
+                               CB_FINDSTRINGEXACT,
+                               (WPARAM) -1,
+                               (LPARAM) un_realm);
+
+        if (idx != CB_ERR) {
+            wchar_t srealm[KCDB_IDENT_MAXCCH_NAME];
+
+            cch = SendMessage(d->hw_realm,
+                              CB_GETLBTEXTLEN,
+                              (WPARAM) idx,
+                              0);
+
+#ifdef DEBUG
+            assert(cch < ARRAYLENGTH(srealm) - 1);
+#endif
+            SendMessage(d->hw_realm,
+                        CB_GETLBTEXT,
+                        (WPARAM) idx,
+                        (LPARAM) srealm);
+
+            if (!_wcsicmp(srealm, un_realm) && wcscmp(srealm, un_realm)) {
+                /* differ only by case */
+
+                StringCchCopy(un_realm, ARRAYLENGTH(un) - (un_realm - un),
+                              srealm);
+
+                SetWindowText(d->hw_username, un);
+            }
+        }
+
+        SendMessage(d->hw_realm,
+                    CB_SELECTSTRING,
+                    (WPARAM) -1,
+                    (LPARAM) un_realm);
+
+        SetWindowText(d->hw_realm,
+                      un_realm);
+
+        if (GetFocus() == d->hw_realm) {
+            HWND hw_next = GetNextDlgTabItem(nc->hwnd, d->hw_realm,
+                                             FALSE);
+            if (hw_next)
+                SetFocus(hw_next);
+        }
+
+        EnableWindow(d->hw_realm, FALSE);
+
+        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));
+    trim_str(realm, ARRAYLENGTH(realm));
+
+    idx = (int)SendMessage(d->hw_realm,
+                           CB_FINDSTRINGEXACT,
+                           (WPARAM) -1,
+                           (LPARAM) realm);
+
+    if (idx != CB_ERR) {
+        wchar_t srealm[KCDB_IDENT_MAXCCH_NAME];
+
+        SendMessage(d->hw_realm,
+                    CB_GETLBTEXT,
+                    (WPARAM) idx,
+                    (LPARAM) srealm);
+
+        if (!_wcsicmp(srealm, realm) && wcscmp(srealm, realm)) {
+            StringCbCopy(realm, sizeof(realm), srealm);
+
+            SetWindowText(d->hw_realm, srealm);
+        }
+    }
+
+    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 = PMALLOC(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 | WS_VSCROLL,
+                 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 | WS_VSCROLL,
+                 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 || cb_ms <= sizeof(wchar_t) * 2)
+                goto _add_lru_realms;
+
+            ms = PMALLOC(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) {
+                    PFREE(ms);
+                    ms = PMALLOC(cb);
+                    assert(ms);
+                    cb_ms = cb;
+                }
+            } else {
+                ms = PMALLOC(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:
+
+            {
+                khm_int32 inc_realms = 0;
+
+                if (KHM_FAILED(khc_read_int32(csp_params,
+                                              L"UseFullRealmList",
+                                              &inc_realms)) ||
+                    !inc_realms)
+                    goto _done_adding_all_realms;
+            }
+
+           if(ms)
+               PFREE(ms);
+
+           ms = khm_krb5_get_realm_list();
+           if(ms) {
+               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_all_realms:
+
+            /* 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)
+                PFREE(defrealm);
+
+            if (ms)
+                PFREE(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. */
+            PFREE(d);
+        }
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static khm_int32
+k5_ident_validate_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;
+    wchar_t * atsign;
+
+    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);
+
+    /* krb5_parse_name() accepts principal names with no realm or an
+       empty realm.  We don't. */
+    atsign = wcschr(nx->name_src, L'@');
+    if (atsign == NULL || atsign[1] == L'\0') {
+        nx->result = KHM_ERROR_INVALID_NAME;
+    } else {
+        nx->result = KHM_ERROR_SUCCESS;
+    }
+
+    return KHM_ERROR_SUCCESS;
+}
+
+static void
+k5_update_last_default_identity(khm_handle ident) {
+    wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
+    khm_size cb;
+
+    cb = sizeof(idname);
+    if (KHM_FAILED(kcdb_identity_get_name(ident, idname, &cb)))
+        return;
+
+    assert(csp_params);
+
+    khc_write_string(csp_params, L"LastDefaultIdent", idname);
+}
+
+static khm_int32
+k5_ident_set_default_int(khm_handle def_ident) {
+    wchar_t id_ccname[KRB5_MAXCCH_CCNAME];
+    khm_size cb;
+    DWORD dw;
+    LONG l;
+    HKEY hk_ccname;
+    DWORD dwType;
+    DWORD dwSize;
+    wchar_t reg_ccname[KRB5_MAXCCH_CCNAME];
+
+#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))) {
+        khm_handle csp_ident = NULL;
+        khm_handle csp_k5 = NULL;
+
+        _reportf(L"The specified identity does not have the Krb5CCName property");
+
+        cb = sizeof(id_ccname);
+        if (KHM_SUCCEEDED(kcdb_identity_get_config(def_ident, 0, &csp_ident)) &&
+            KHM_SUCCEEDED(khc_open_space(csp_ident, CSNAME_KRB5CRED, 0, &csp_k5)) &&
+            KHM_SUCCEEDED(khc_read_string(csp_k5, L"DefaultCCName",
+                                          id_ccname, &cb))) {
+
+            _reportf(L"Found CC name in configuration [%s]", id_ccname);
+        } else {
+            /* last resort, use the name of the identity as the cc
+               name */
+            cb = sizeof(id_ccname);
+            if (KHM_FAILED(kcdb_identity_get_name(def_ident, id_ccname, &cb))) {
+                _reportf(L"Can't use name of identity as CCName");
+                _end_task();
+
+                id_ccname[0] = L'\0';
+            }
+        }
+
+        if (csp_k5)
+            khc_close_space(csp_k5);
+        if (csp_ident)
+            khc_close_space(csp_ident);
+
+        if (id_ccname[0] == L'\0')
+            return KHM_ERROR_INVALID_PARAM;
+    }
+
+    khm_krb5_canon_cc_name(id_ccname, sizeof(id_ccname));
+
+    _reportf(L"Found Krb5CCName property : %s", id_ccname);
+
+    StringCbLength(id_ccname, sizeof(id_ccname), &cb);
+    cb += sizeof(wchar_t);
+
+    _reportf(L"Setting default CC name in the registry");
+
+    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) {
+        _reportf(L"Can't create registry key : %d", l);
+        _end_task();
+        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) {
+        _reportf(L"Successfully set the default ccache");
+        k5_update_last_default_identity(def_ident);
+        return KHM_ERROR_SUCCESS;
+    } else {
+        _reportf(L"Can't set the registry value : %d", l);
+        return KHM_ERROR_UNKNOWN;
+    }
+}
+
+static khm_int32
+k5_ident_set_default(khm_int32 msg_type,
+                     khm_int32 msg_subtype,
+                     khm_ui_4 uparam,
+                     void * vparam) {
+
+    /* 
+       Currently, setting the default identity simply sets the
+       "ccname" registry value at "Software\MIT\kerberos5".
+    */
+
+    if (uparam) {
+        /* an identity is being made default */
+        khm_handle def_ident = (khm_handle) vparam;
+        khm_int32 rv;
+
+#ifdef DEBUG
+        assert(def_ident != NULL);
+#endif
+
+        {
+            wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
+            khm_size cb;
+
+            cb = sizeof(idname);
+            kcdb_identity_get_name(def_ident, idname, &cb);
+
+            _begin_task(0);
+            _report_cs1(KHERR_DEBUG_1, L"Setting default identity [%1!s!]", _cstr(idname));
+            _describe();
+        }
+
+        rv = k5_ident_set_default_int(def_ident);
+
+        _end_task();
+
+        return rv;
+
+    } 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;
+
+    /* if there is a default identity already, we assume we don't need
+       to check this one. */
+
+    khm_handle def_ident;
+
+    if (KHM_SUCCEEDED(kcdb_identity_get_default(&def_ident))) {
+        kcdb_identity_release(def_ident);
+
+        return KHM_ERROR_SUCCESS;
+    }
+
+    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;
+}
+
+struct k5_ident_update_data {
+    khm_handle identity;
+
+    FILETIME   ft_expire;      /* expiration */
+    FILETIME   ft_issue;       /* issue */
+    FILETIME   ft_rexpire;     /* renew expiration */
+    wchar_t    ccname[KRB5_MAXCCH_CCNAME];
+    khm_int32  k5_flags;
+};
+
+/* The logic here has to reflect the logic in khm_krb5_list_tickets().
+   We use this to handle an identity update request because some other
+   plug-in or maybe NetIDMgr itself is about to do something
+   important(tm) with the identity and needs to make sure that the
+   properties of the identity are up-to-date. */
+static khm_int32 KHMAPI
+k5_ident_update_apply_proc(khm_handle cred,
+                           void * rock) {
+    struct k5_ident_update_data * d = (struct k5_ident_update_data *) rock;
+    khm_handle ident = NULL;
+    khm_int32 t;
+    khm_int32 flags;
+    FILETIME t_cexpire;
+    FILETIME 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 (!kcdb_identity_is_equal(ident,d->identity))
+
+        goto _cleanup;
+
+    if (KHM_FAILED(kcdb_cred_get_flags(cred, &flags)))
+
+        flags = 0;
+
+    if (flags & KCDB_CRED_FLAG_INITIAL) {
+        cb = sizeof(t_cexpire);
+        if (KHM_SUCCEEDED(kcdb_cred_get_attr(cred,
+                                             KCDB_ATTR_EXPIRE,
+                                             NULL,
+                                             &t_cexpire,
+                                             &cb))) {
+            if ((d->ft_expire.dwLowDateTime == 0 &&
+                 d->ft_expire.dwHighDateTime == 0) ||
+                CompareFileTime(&t_cexpire, &d->ft_expire) > 0) {
+                goto update_identity;
+            }
+        }
+    }
+
+    goto _cleanup;
+
+ update_identity:
+
+    d->ft_expire = t_cexpire;
+
+    cb = sizeof(d->ccname);
+    if (KHM_FAILED(kcdb_cred_get_attr(cred, KCDB_ATTR_LOCATION, NULL, d->ccname, &cb))) {
+        d->ccname[0] = L'\0';
+    }
+
+    cb = sizeof(d->k5_flags);
+    if (KHM_FAILED(kcdb_cred_get_attr(cred, attr_id_krb5_flags, NULL,
+                                         &d->k5_flags, &cb))) {
+        d->k5_flags = 0;
+    }
+
+    cb = sizeof(d->ft_issue);
+    if (KHM_FAILED(kcdb_cred_get_attr(cred, KCDB_ATTR_ISSUE, NULL, &d->ft_issue, &cb))) {
+        ZeroMemory(&d->ft_issue, sizeof(d->ft_issue));
+    }
+
+    cb = sizeof(t_rexpire);
+    if ((d->k5_flags & TKT_FLG_RENEWABLE) &&
+        KHM_SUCCEEDED(kcdb_cred_get_attr(cred,
+                                         KCDB_ATTR_RENEW_EXPIRE,
+                                         NULL,
+                                         &t_rexpire,
+                                         &cb))) {
+        d->ft_rexpire = t_rexpire;
+    } else {
+        ZeroMemory(&d->ft_rexpire, sizeof(d->ft_rexpire));
+    }
+
+ _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) {
+
+#if 0
+    struct k5_ident_update_data d;
+#endif
+    khm_handle ident;
+    khm_handle tident;
+    krb5_ccache cc = NULL;
+    char * ccname;
+    krb5_error_code code;
+    khm_size cb;
+    wchar_t wid_ccname[MAX_PATH];
+    wchar_t w_ccname[MAX_PATH];
+
+    ident = (khm_handle) vparam;
+    if (ident == NULL)
+        return KHM_ERROR_SUCCESS;
+
+#if 0
+    /* we are going to skip doing this here since
+       khm_krb5_list_tickets() performs this function for us each time
+       we enumerate tickets.  Since it also gets run each time our
+       list of tickets changes and since we are basing this operation
+       on existing tickets, we are unlikely to find anything new
+       here.  */
+    ZeroMemory(&d, sizeof(d));
+    d.identity = ident;
+
+    kcdb_credset_apply(NULL,
+                       k5_ident_update_apply_proc,
+                       (void *) &d);
+
+    if (d.ft_expire.dwLowDateTime != 0 ||
+        d.ft_expire.dwHighDateTime != 0) {
+
+        /* we found a TGT */
+
+        kcdb_identity_set_attr(ident, KCDB_ATTR_EXPIRE,
+                               &d.ft_expire, sizeof(d.ft_expire));
+        if (d.ft_issue.dwLowDateTime != 0 ||
+            d.ft_issue.dwHighDateTime != 0)
+            kcdb_identity_set_attr(ident, KCDB_ATTR_ISSUE,
+                                   &d.ft_issue, sizeof(d.ft_issue));
+        else
+            kcdb_identity_set_attr(ident, KCDB_ATTR_ISSUE, NULL, 0);
+
+        if (d.ft_rexpire.dwLowDateTime != 0 ||
+            d.ft_rexpire.dwHighDateTime != 0)
+            kcdb_identity_set_attr(ident, KCDB_ATTR_RENEW_EXPIRE,
+                                   &d.ft_rexpire, sizeof(d.ft_rexpire));
+        else
+            kcdb_identity_set_attr(ident, KCDB_ATTR_RENEW_EXPIRE, NULL, 0);
+
+        kcdb_identity_set_attr(ident, attr_id_krb5_flags,
+                               &d.k5_flags, sizeof(d.k5_flags));
+
+        if (d.ccname[0])
+            kcdb_identity_set_attr(ident, attr_id_krb5_ccname,
+                                   d.ccname, KCDB_CBSIZE_AUTO);
+        else
+            kcdb_identity_set_attr(ident, attr_id_krb5_ccname, NULL, 0);
+
+    } else {
+        /* Clear out the attributes.  We don't have any information
+           about this identity */
+        kcdb_identity_set_attr(ident, KCDB_ATTR_EXPIRE, NULL, 0);
+        kcdb_identity_set_attr(ident, KCDB_ATTR_ISSUE, NULL, 0);
+        kcdb_identity_set_attr(ident, KCDB_ATTR_RENEW_EXPIRE, NULL, 0);
+        kcdb_identity_set_attr(ident, attr_id_krb5_flags, NULL, 0);
+        kcdb_identity_set_attr(ident, attr_id_krb5_ccname, NULL, 0);
+    }
+#endif
+
+    if (KHM_SUCCEEDED(kcdb_identity_get_default(&tident))) {
+        kcdb_identity_release(tident);
+        goto _iu_cleanup;
+    }
+
+    cb = sizeof(wid_ccname);
+    if (KHM_FAILED(kcdb_identity_get_attr(ident,
+                                          attr_id_krb5_ccname,
+                                          NULL,
+                                          wid_ccname,
+                                          &cb)))
+        goto _iu_cleanup;
+
+    if(k5_identpro_ctx == NULL)
+        goto _iu_cleanup;
+
+    code = pkrb5_cc_default(k5_identpro_ctx, &cc);
+    if (code)
+        goto _iu_cleanup;
+
+    ccname = pkrb5_cc_get_name(k5_identpro_ctx, cc);
+    if (ccname == NULL)
+        goto _iu_cleanup;
+
+    AnsiStrToUnicode(w_ccname, sizeof(w_ccname), ccname);
+
+    khm_krb5_canon_cc_name(w_ccname, sizeof(w_ccname));
+    khm_krb5_canon_cc_name(wid_ccname, sizeof(wid_ccname));
+
+    if (!_wcsicmp(w_ccname, wid_ccname))
+        kcdb_identity_set_default_int(ident);
+
+ _iu_cleanup:
+    if (cc && k5_identpro_ctx)
+        pkrb5_cc_close(k5_identpro_ctx, cc);
+
+    return KHM_ERROR_SUCCESS;
+}
+
+static khm_boolean
+k5_refresh_default_identity(krb5_context ctx) {
+    /* 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];
+    char * ccname = NULL;
+    khm_handle ident = NULL;
+    khm_boolean found_default = FALSE;
+
+    assert(ctx != NULL);
+
+    _begin_task(0);
+    _report_cs0(KHERR_DEBUG_1, L"Refreshing default identity");
+    _describe();
+
+    code = pkrb5_cc_default(ctx, &cc);
+    if (code) {
+        _reportf(L"Can't open default ccache. code=%d", code);
+        goto _nc_cleanup;
+    }
+    
+    code = pkrb5_cc_get_principal(ctx, cc, &princ);
+    if (code) {
+        /* try to determine the identity from the ccache name */
+        ccname = pkrb5_cc_get_name(ctx, cc);
+
+        if (ccname) {
+            char * namepart = strchr(ccname, ':');
+
+            _reportf(L"CC name is [%S]", ccname);
+
+            if (namepart == NULL)
+                namepart = ccname;
+            else
+                namepart++;
+
+            _reportf(L"Checking if [%S] is a valid identity name", namepart);
+
+            AnsiStrToUnicode(princ_nameW, sizeof(princ_nameW), namepart);
+            if (kcdb_identity_is_valid_name(princ_nameW)) {
+                kcdb_identity_create(princ_nameW, KCDB_IDENT_FLAG_CREATE, &ident);
+                if (ident) {
+                    _reportf(L"Setting [%S] as the default identity", namepart);
+                    kcdb_identity_set_default_int(ident);
+                    found_default = TRUE;
+                }
+            }
+        } else {
+            _reportf(L"Can't determine ccache name");
+        }
+
+        goto _nc_cleanup;
+    }
+
+    code = pkrb5_unparse_name(ctx, princ, &princ_nameA);
+    if (code)
+        goto _nc_cleanup;
+
+    AnsiStrToUnicode(princ_nameW, sizeof(princ_nameW), princ_nameA);
+
+    _reportf(L"Found principal [%s]", princ_nameW);
+
+    if (KHM_FAILED(kcdb_identity_create(princ_nameW, KCDB_IDENT_FLAG_CREATE, &ident))) {
+        _reportf(L"Failed to create identity");
+        goto _nc_cleanup;
+    }
+
+    _reportf(L"Setting default identity to [%s]", princ_nameW);
+    kcdb_identity_set_default_int(ident);
+
+    found_default = TRUE;
+
+ _nc_cleanup:
+
+    _end_task();
+
+    if (princ_nameA)
+        pkrb5_free_unparsed_name(ctx, princ_nameA);
+
+    if (princ)
+        pkrb5_free_principal(ctx, princ);
+
+    if (cc)
+        pkrb5_cc_close(ctx, cc);
+
+    if (ident)
+        kcdb_identity_release(ident);
+
+    return found_default;
+}
+
+static khm_int32
+k5_ident_init(khm_int32 msg_type,
+              khm_int32 msg_subtype,
+              khm_ui_4 uparam,
+              void * vparam) {
+
+    khm_boolean found_default;
+    khm_handle ident;
+
+    found_default = k5_refresh_default_identity(k5_identpro_ctx);
+
+    if (!found_default) {
+        wchar_t widname[KCDB_IDENT_MAXCCH_NAME];
+        khm_size cb;
+
+        cb = sizeof(widname);
+
+        assert(csp_params);
+
+        if (KHM_SUCCEEDED(khc_read_string(csp_params, L"LastDefaultIdent",
+                                          widname, &cb))) {
+            ident = NULL;
+            kcdb_identity_create(widname, KCDB_IDENT_FLAG_CREATE, &ident);
+            if (ident) {
+                kcdb_identity_set_default_int(ident);
+                kcdb_identity_release(ident);
+
+                found_default = TRUE;
+            }
+        }
+    }
+
+    if (!found_default) {
+
+        /* There was no default ccache and we don't have a
+           "LastDefaultIdent" value. Next we see if there are any
+           identities that have credentials which have a Krb5CCName
+           property (i.e. an identity that has a Kerberos 5 TGT), and
+           make it the default.
+
+           Note that since the Krb5Ident plug-in has a dependency on
+           Krb5Cred, by the time this code runs, we already have a
+           listing of Kerberos 5 tickets and identities. */
+
+        wchar_t * idlist = NULL;
+        wchar_t * thisid;
+        khm_size cb = 0;
+        khm_size n_idents = 0;
+        khm_int32 rv;
+        wchar_t ccname[KRB5_MAXCCH_CCNAME];
+        FILETIME ft_expire;
+        FILETIME ft_now;
+        FILETIME ft_threshold;
+        BOOL match_all = FALSE;
+
+        rv = kcdb_identity_enum(0, 0, NULL, &cb, &n_idents);
+
+        TimetToFileTimeInterval(5 * 60, &ft_threshold);
+        GetSystemTimeAsFileTime(&ft_now);
+        ft_now = FtAdd(&ft_now, &ft_threshold);
+
+        while (rv == KHM_ERROR_TOO_LONG && n_idents > 0) {
+            if (idlist) {
+                PFREE(idlist);
+                idlist = NULL;
+            }
+
+            idlist = PMALLOC(cb);
+
+            if (idlist == NULL)
+                break;
+
+            rv = kcdb_identity_enum(0, 0, idlist, &cb, &n_idents);
+        }
+
+        if (KHM_SUCCEEDED(rv)) {
+
+            /* first we try to find an identity that has a valid TGT.
+               If that fails, then we try to find an identity with
+               *any* TGT. */
+
+        try_again:
+
+            for (thisid = idlist;
+                 thisid && *thisid && !found_default;
+                 thisid = multi_string_next(thisid)) {
+
+                if (KHM_SUCCEEDED(kcdb_identity_create(thisid, 0, &ident))) {
+                    khm_size cb_ft = sizeof(FILETIME);
+                    cb = sizeof(ccname);
+
+                    if (KHM_SUCCEEDED(kcdb_identity_get_attr(ident, attr_id_krb5_ccname,
+                                                             NULL, ccname, &cb)) &&
+                        (match_all ||
+                         (KHM_SUCCEEDED(kcdb_identity_get_attr(ident, KCDB_ATTR_EXPIRE,
+                                                               NULL, &ft_expire, &cb_ft)) &&
+                          CompareFileTime(&ft_expire, &ft_now) > 0))) {
+
+                        /* found one */
+                        k5_ident_set_default_int(ident);
+                        kcdb_identity_set_default_int(ident);
+                        found_default = TRUE;
+
+                    }
+
+                    kcdb_identity_release(ident);
+                    ident = NULL;
+                }
+            }
+
+            if (!found_default && !match_all) {
+                match_all = TRUE;
+                goto try_again;
+            }
+        }
+
+        if (idlist) {
+            PFREE(idlist);
+            idlist = NULL;
+        }
+    }
+
+    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;
+}
+
+/* forward dcl */
+khm_int32 KHMAPI
+k5_ident_name_comp_func(const void * dl, khm_size cb_dl,
+                        const void * dr, khm_size cb_dr);
+
+static khm_int32
+k5_ident_compare_name(khm_int32 msg_type,
+                      khm_int32 msg_subtype,
+                      khm_ui_4 uparam,
+                      void * vparam) {
+    kcdb_ident_name_xfer *px;
+
+    px = (kcdb_ident_name_xfer *) vparam;
+
+    /* note that k5_ident_name_comp_func() ignores the size
+       specifiers.  So we can just pass in 0's. */
+    px->result = k5_ident_name_comp_func(px->name_src, 0,
+                                         px->name_alt, 0);
+
+    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_validate_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:
+        return k5_ident_compare_name(msg_type,
+                                     msg_subtype,
+                                     uparam,
+                                     vparam);
+
+    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;
+}
+
+/* note that we are ignoring the size specifiers.  We can do that
+   because we are guaranteed that dl and dr point to NULL terminated
+   unicode strings when used with credential data buffers.  We also
+   use the fact that we are ignoring the size specifiers when we call
+   this function from k5_ident_compare_name() to avoid calculating the
+   length of the string. */
+khm_int32 KHMAPI
+k5_ident_name_comp_func(const void * dl, khm_size cb_dl,
+                        const void * dr, khm_size cb_dr) {
+    wchar_t * idl = (wchar_t *) dl;
+    wchar_t * idr = (wchar_t *) dr;
+    wchar_t * rl;
+    wchar_t * rr;
+    khm_int32 r;
+
+    rl = khm_get_realm_from_princ(idl);
+    rr = khm_get_realm_from_princ(idr);
+
+    if (rl == NULL && rr == NULL)
+        return wcscmp(idl, idr);
+    else if (rl == NULL)
+        return 1;
+    else if (rr == NULL)
+        return -1;
+
+    r = wcscmp(rl, rr);
+    if (r == 0)
+        return wcscmp(idl, idr);
+    else
+        return r;
+}
+
+
+/* Identity change notification thread */
+
+HANDLE h_ccname_exit_event;
+HANDLE h_ccname_thread;
+
+DWORD WINAPI k5_ccname_monitor_thread(LPVOID lpParameter) {
+    krb5_context ctx = 0;
+
+    HKEY hk_ccname;
+    HANDLE h_notify;
+    HANDLE h_waits[2];
+
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+    DWORD dwType;
+    DWORD dwSize;
+    DWORD dwDisp;
+    wchar_t reg_ccname[KRB5_MAXCCH_CCNAME];
+    LONG l;
+
+    PDESCTHREAD(L"Krb5 CCName Monitor", L"Krb5");
+
+    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,
+                           &dwDisp);
+
+    if (l != ERROR_SUCCESS) {
+        rv = KHM_ERROR_UNKNOWN;
+        goto _exit;
+    }
+
+    dwSize = sizeof(reg_ccname);
+    
+    l = RegQueryValueEx(hk_ccname,
+                        L"ccname",
+                        NULL,
+                        &dwType,
+                        (LPBYTE) reg_ccname,
+                        &dwSize);
+
+    if (l != ERROR_SUCCESS ||
+        dwType != REG_SZ) {
+
+        reg_ccname[0] = L'\0';
+    }
+
+    l = pkrb5_init_context(&ctx);
+
+    if (l)
+        goto _exit_0;
+
+    h_notify = CreateEvent(NULL, FALSE, FALSE, L"Local\\Krb5CCNameChangeNotifier");
+
+    if (h_notify == NULL)
+        goto _exit_0;
+
+    /* begin wait loop */
+
+    h_waits[0] = h_ccname_exit_event;
+    h_waits[1] = h_notify;
+
+    do {
+        DWORD dwrv;
+
+        l = RegNotifyChangeKeyValue(hk_ccname, FALSE,
+                                    REG_NOTIFY_CHANGE_LAST_SET,
+                                    h_notify, TRUE);
+
+        if (l != ERROR_SUCCESS) {
+            rv = KHM_ERROR_UNKNOWN;
+            break;
+        }
+
+        dwrv = WaitForMultipleObjects(2, h_waits, FALSE, INFINITE);
+
+        if (dwrv == WAIT_OBJECT_0) {
+            /* exit! */
+            break;
+
+        } else if (dwrv == WAIT_OBJECT_0 + 1) {
+            /* change notify! */
+            wchar_t new_ccname[KRB5_MAXCCH_CCNAME];
+
+            dwSize = sizeof(new_ccname);
+    
+            l = RegQueryValueEx(hk_ccname,
+                                L"ccname",
+                                NULL,
+                                &dwType,
+                                (LPBYTE) new_ccname,
+                                &dwSize);
+
+            if (l != ERROR_SUCCESS ||
+                dwType != REG_SZ) {
+                new_ccname[0] = L'\0';
+            }
+
+            if (_wcsicmp(new_ccname, reg_ccname)) {
+                k5_refresh_default_identity(ctx);
+                StringCbCopy(reg_ccname, sizeof(reg_ccname), new_ccname);
+            }
+
+        } else {
+            /* something went wrong */
+            rv = KHM_ERROR_UNKNOWN;
+            break;
+        }
+
+    } while (TRUE);
+
+    CloseHandle(h_notify);
+
+ _exit_0:
+
+    RegCloseKey(hk_ccname);
+
+    if (ctx)
+        pkrb5_free_context(ctx);
+
+ _exit:
+    ExitThread(rv);
+
+    /* not reached */
+    return rv;
+}
+
+khm_int32
+k5_msg_system_idpro(khm_int32 msg_type, khm_int32 msg_subtype,
+                    khm_ui_4 uparam, void * vparam) {
+
+    switch(msg_subtype) {
+    case KMSG_SYSTEM_INIT:
+        {
+
+            pkrb5_init_context(&k5_identpro_ctx);
+            kcdb_identity_set_type(credtype_id_krb5);
+
+            if (KHM_FAILED(kcdb_type_get_id(TYPENAME_KRB5_PRINC, 
+                                            &type_id_krb5_princ))) {
+                kcdb_type dt;
+                kcdb_type * pstr;
+
+                kcdb_type_get_info(KCDB_TYPE_STRING, &pstr);
+
+                ZeroMemory(&dt, sizeof(dt));
+                dt.name = TYPENAME_KRB5_PRINC;
+                dt.id = KCDB_TYPE_INVALID;
+                dt.flags = KCDB_TYPE_FLAG_CB_AUTO;
+                dt.cb_min = pstr->cb_min;
+                dt.cb_max = pstr->cb_max;
+                dt.toString = pstr->toString;
+                dt.isValid = pstr->isValid;
+                dt.comp = k5_ident_name_comp_func;
+                dt.dup = pstr->dup;
+
+                kcdb_type_register(&dt, &type_id_krb5_princ);
+
+                type_regd_krb5_princ = TRUE;
+
+                kcdb_type_release_info(pstr);
+            }
+
+            if (type_id_krb5_princ != -1) {
+                kcdb_attrib * attr;
+
+                kcdb_attrib_get_info(KCDB_ATTR_ID_NAME, &attr);
+
+                attr->type = type_id_krb5_princ;
+
+                kcdb_attrib_release_info(attr);
+            }
+
+            h_ccname_exit_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+            if (h_ccname_exit_event) {
+                h_ccname_thread = CreateThread(NULL,
+                                               200 * 1024,
+                                               k5_ccname_monitor_thread,
+                                               NULL,
+                                               0,
+                                               NULL);
+            } else {
+                h_ccname_thread = NULL;
+            }
+        }
+        break;
+
+    case KMSG_SYSTEM_EXIT:
+        {
+
+            if (h_ccname_thread) {
+                SetEvent(h_ccname_exit_event);
+                WaitForSingleObject(h_ccname_thread, INFINITE);
+                CloseHandle(h_ccname_thread);
+                CloseHandle(h_ccname_exit_event);
+
+                h_ccname_exit_event = NULL;
+                h_ccname_thread = NULL;
+            }
+
+            if (k5_identpro_ctx) {
+                pkrb5_free_context(k5_identpro_ctx);
+                k5_identpro_ctx = NULL;
+            }
+
+            if (type_id_krb5_princ != -1) {
+                kcdb_attrib * attr;
+
+                kcdb_attrib_get_info(KCDB_ATTR_ID_NAME, &attr);
+
+                attr->type = KCDB_TYPE_STRING;
+
+                kcdb_attrib_release_info(attr);
+            }
+
+            /* allow a brief moment for any stale references to die */
+            Sleep(100);
+
+            if (type_regd_krb5_princ) {
+                kcdb_type_unregister(type_id_krb5_princ);
+            }
+        }
+        break;
+    }
+
+    return KHM_ERROR_SUCCESS;
+}
+
+khm_int32 KHMAPI
+k5_ident_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_idpro(msg_type, msg_subtype, uparam, vparam);
+
+    case KMSG_IDENT:
+        return k5_msg_ident(msg_type, msg_subtype, uparam, vparam);
+    }
+
+    return KHM_ERROR_SUCCESS;
+}
index 3d2f2c0e95da2110eee3a9ec5d33c17dde222514..befa7a9808e95632728312d31f7253c8a93ddcf2 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<krbcred.h>\r
-#include<kherror.h>\r
-\r
-kmm_module h_khModule; /* KMM's handle to this module */\r
-HINSTANCE hInstance;\r
-HMODULE hResModule;    /* HMODULE to the resource library */\r
-const wchar_t * k5_facility = L"Krb5Cred";\r
-\r
-khm_int32 type_id_enctype       = -1;\r
-khm_int32 type_id_addr_list     = -1;\r
-khm_int32 type_id_krb5_flags    = -1;\r
-khm_int32 type_id_krb5_princ    = -1;\r
-khm_int32 type_id_kvno          = -1;\r
-\r
-BOOL type_regd_enctype      = FALSE;\r
-BOOL type_regd_addr_list    = FALSE;\r
-BOOL type_regd_krb5_flags   = FALSE;\r
-BOOL type_regd_krb5_princ   = FALSE;\r
-BOOL type_regd_kvno         = FALSE;\r
-\r
-khm_int32 attr_id_key_enctype   = -1;\r
-khm_int32 attr_id_tkt_enctype   = -1;\r
-khm_int32 attr_id_addr_list     = -1;\r
-khm_int32 attr_id_krb5_flags    = -1;\r
-khm_int32 attr_id_krb5_ccname   = -1;\r
-khm_int32 attr_id_kvno          = -1;\r
-khm_int32 attr_id_krb5_idflags  = -1;\r
-\r
-BOOL attr_regd_key_enctype  = FALSE;\r
-BOOL attr_regd_tkt_enctype  = FALSE;\r
-BOOL attr_regd_addr_list    = FALSE;\r
-BOOL attr_regd_krb5_flags   = FALSE;\r
-BOOL attr_regd_krb5_ccname  = FALSE;\r
-BOOL attr_regd_kvno         = FALSE;\r
-BOOL attr_regd_krb5_idflags = FALSE;\r
-\r
-khm_handle csp_plugins      = NULL;\r
-khm_handle csp_krbcred   = NULL;\r
-khm_handle csp_params       = NULL;\r
-\r
-BOOL is_k5_identpro = TRUE;\r
-\r
-khm_ui_4  k5_commctl_version;\r
-\r
-kmm_module_locale locales[] = {\r
-    LOCALE_DEF(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US), L"krb5cred_en_us.dll", KMM_MLOC_FLAG_DEFAULT)\r
-};\r
-int n_locales = ARRAYLENGTH(locales);\r
-\r
-/* These two should not do anything */\r
-void init_krb() {\r
-}\r
-\r
-void exit_krb() {\r
-}\r
-\r
-/* called by the NetIDMgr module manager */\r
-KHMEXP khm_int32 KHMAPI init_module(kmm_module h_module) {\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    kmm_plugin_reg pi;\r
-    wchar_t buf[256];\r
-\r
-    h_khModule = h_module;\r
-\r
-    rv = kmm_set_locale_info(h_module, locales, n_locales);\r
-    if(KHM_SUCCEEDED(rv)) {\r
-        hResModule = kmm_get_resource_hmodule(h_module);\r
-    } else\r
-        goto _exit;\r
-\r
-    k5_commctl_version = khm_get_commctl_version(NULL);\r
-\r
-    /* register the plugin */\r
-    ZeroMemory(&pi, sizeof(pi));\r
-    pi.name = KRB5_PLUGIN_NAME;\r
-    pi.type = KHM_PITYPE_CRED;\r
-    pi.icon = LoadImage(hResModule, MAKEINTRESOURCE(IDI_PLUGIN),\r
-                        IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE);\r
-    pi.flags = 0;\r
-    pi.msg_proc = k5_msg_callback;\r
-    pi.description = buf;\r
-    pi.dependencies = NULL;\r
-    LoadString(hResModule, IDS_PLUGIN_DESC,\r
-               buf, ARRAYLENGTH(buf));\r
-    kmm_provide_plugin(h_module, &pi);\r
-\r
-    ZeroMemory(&pi, sizeof(pi));\r
-    pi.name = KRB5_IDENTPRO_NAME;\r
-    pi.type = KHM_PITYPE_IDENT;\r
-    pi.icon = LoadImage(hResModule, MAKEINTRESOURCE(IDI_PLUGIN),\r
-                        IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE);\r
-    pi.flags = 0;\r
-    pi.msg_proc = k5_ident_callback;\r
-    pi.description = buf;\r
-    pi.dependencies = KRB5_PLUGIN_NAME L"\0";\r
-    LoadString(hResModule, IDS_IDENTPRO_DESC,\r
-               buf, ARRAYLENGTH(buf));\r
-    kmm_provide_plugin(h_module, &pi);\r
-\r
-    if(KHM_FAILED(rv = init_imports()))\r
-        goto _exit;\r
-\r
-    if(KHM_FAILED(rv = init_error_funcs()))\r
-        goto _exit;\r
-\r
-    /* Register common data types */\r
-    if(KHM_FAILED(kcdb_type_get_id(TYPENAME_ENCTYPE, &type_id_enctype))) {\r
-        kcdb_type type;\r
-        kcdb_type *t32;\r
-\r
-        kcdb_type_get_info(KCDB_TYPE_INT32, &t32);\r
-\r
-        type.id = KCDB_TYPE_INVALID;\r
-        type.name = TYPENAME_ENCTYPE;\r
-        type.flags = KCDB_TYPE_FLAG_CB_FIXED;\r
-        type.cb_max = t32->cb_max;\r
-        type.cb_min = t32->cb_min;\r
-        type.isValid = t32->isValid;\r
-        type.comp = t32->comp;\r
-        type.dup = t32->dup;\r
-        type.toString = enctype_toString;\r
-\r
-        rv = kcdb_type_register(&type, &type_id_enctype);\r
-        kcdb_type_release_info(t32);\r
-\r
-        if(KHM_FAILED(rv))\r
-            goto _exit;\r
-        type_regd_enctype = TRUE;\r
-    }\r
-\r
-    if(KHM_FAILED(kcdb_type_get_id(TYPENAME_ADDR_LIST, &type_id_addr_list))) {\r
-        kcdb_type type;\r
-        kcdb_type *tdata;\r
-\r
-        kcdb_type_get_info(KCDB_TYPE_DATA, &tdata);\r
-\r
-        type.id = KCDB_TYPE_INVALID;\r
-        type.name = TYPENAME_ADDR_LIST;\r
-        type.flags = KCDB_TYPE_FLAG_CB_MIN;\r
-        type.cb_min = 0;\r
-        type.cb_max = 0;\r
-        type.isValid = tdata->isValid;\r
-        type.comp = addr_list_comp;\r
-        type.dup = tdata->dup;\r
-        type.toString = addr_list_toString;\r
-\r
-        rv = kcdb_type_register(&type, &type_id_addr_list);\r
-        kcdb_type_release_info(tdata);\r
-\r
-        if(KHM_FAILED(rv))\r
-            goto _exit;\r
-        type_regd_addr_list = TRUE;\r
-    }\r
-\r
-    if(KHM_FAILED(kcdb_type_get_id(TYPENAME_KRB5_FLAGS, &type_id_krb5_flags))) {\r
-        kcdb_type type;\r
-        kcdb_type *t32;\r
-\r
-        kcdb_type_get_info(KCDB_TYPE_INT32, &t32);\r
-\r
-        type.id = KCDB_TYPE_INVALID;\r
-        type.name = TYPENAME_KRB5_FLAGS;\r
-        type.flags = KCDB_TYPE_FLAG_CB_FIXED;\r
-        type.cb_max = t32->cb_max;\r
-        type.cb_min = t32->cb_min;\r
-        type.isValid = t32->isValid;\r
-        type.comp = t32->comp;\r
-        type.dup = t32->dup;\r
-        type.toString = krb5flags_toString;\r
-\r
-        rv = kcdb_type_register(&type, &type_id_krb5_flags);\r
-        kcdb_type_release_info(t32);\r
-\r
-        if(KHM_FAILED(rv))\r
-            goto _exit;\r
-        type_regd_krb5_flags = TRUE;\r
-    }\r
-\r
-    if (KHM_FAILED(kcdb_type_get_id(TYPENAME_KVNO, &type_id_kvno))) {\r
-        kcdb_type type;\r
-        kcdb_type *t32;\r
-\r
-        kcdb_type_get_info(KCDB_TYPE_INT32, &t32);\r
-\r
-        type.id = KCDB_TYPE_INVALID;\r
-        type.name = TYPENAME_KVNO;\r
-        type.flags = KCDB_TYPE_FLAG_CB_FIXED;\r
-        type.cb_max = t32->cb_max;\r
-        type.cb_min = t32->cb_min;\r
-        type.isValid = t32->isValid;\r
-        type.comp = t32->comp;\r
-        type.dup = t32->dup;\r
-        type.toString = kvno_toString;\r
-\r
-        rv = kcdb_type_register(&type, &type_id_kvno);\r
-        kcdb_type_release_info(t32);\r
-\r
-        if (KHM_FAILED(rv))\r
-            goto _exit;\r
-\r
-        type_regd_kvno = TRUE;\r
-    }\r
-\r
-    /* Register common attributes */\r
-    if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_KEY_ENCTYPE, &attr_id_key_enctype))) {\r
-        kcdb_attrib attrib;\r
-        wchar_t sbuf[KCDB_MAXCCH_SHORT_DESC];\r
-        wchar_t lbuf[KCDB_MAXCCH_SHORT_DESC];\r
-        /* although we are loading a long descriptoin, it still fits\r
-        in the short descriptoin buffer */\r
-\r
-        ZeroMemory(&attrib, sizeof(attrib));\r
-\r
-        attrib.name = ATTRNAME_KEY_ENCTYPE;\r
-        attrib.id = KCDB_ATTR_INVALID;\r
-        attrib.type = type_id_enctype;\r
-        attrib.flags = KCDB_ATTR_FLAG_TRANSIENT;\r
-        LoadString(hResModule, IDS_KEY_ENCTYPE_SHORT_DESC, sbuf, ARRAYLENGTH(sbuf));\r
-        LoadString(hResModule, IDS_KEY_ENCTYPE_LONG_DESC, lbuf, ARRAYLENGTH(lbuf));\r
-        attrib.short_desc = sbuf;\r
-        attrib.long_desc = lbuf;\r
-        \r
-        rv = kcdb_attrib_register(&attrib, &attr_id_key_enctype);\r
-\r
-        if(KHM_FAILED(rv))\r
-            goto _exit;\r
-\r
-        attr_regd_key_enctype = TRUE;\r
-    }\r
-\r
-    if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_TKT_ENCTYPE, &attr_id_tkt_enctype))) {\r
-        kcdb_attrib attrib;\r
-        wchar_t sbuf[KCDB_MAXCCH_SHORT_DESC];\r
-        wchar_t lbuf[KCDB_MAXCCH_SHORT_DESC];\r
-        /* although we are loading a long descriptoin, it still fits\r
-        in the short descriptoin buffer */\r
-\r
-        ZeroMemory(&attrib, sizeof(attrib));\r
-\r
-        attrib.name = ATTRNAME_TKT_ENCTYPE;\r
-        attrib.id = KCDB_ATTR_INVALID;\r
-        attrib.type = type_id_enctype;\r
-        attrib.flags = KCDB_ATTR_FLAG_TRANSIENT;\r
-        LoadString(hResModule, IDS_TKT_ENCTYPE_SHORT_DESC, sbuf, ARRAYLENGTH(sbuf));\r
-        LoadString(hResModule, IDS_TKT_ENCTYPE_LONG_DESC, lbuf, ARRAYLENGTH(lbuf));\r
-        attrib.short_desc = sbuf;\r
-        attrib.long_desc = lbuf;\r
-        \r
-        rv = kcdb_attrib_register(&attrib, &attr_id_tkt_enctype);\r
-\r
-        if(KHM_FAILED(rv))\r
-            goto _exit;\r
-\r
-        attr_regd_tkt_enctype = TRUE;\r
-    }\r
-\r
-    if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_ADDR_LIST, &attr_id_addr_list))) {\r
-        kcdb_attrib attrib;\r
-        wchar_t sbuf[KCDB_MAXCCH_SHORT_DESC];\r
-        wchar_t lbuf[KCDB_MAXCCH_SHORT_DESC];\r
-        /* although we are loading a long descriptoin, it still fits\r
-        in the short descriptoin buffer */\r
-\r
-        ZeroMemory(&attrib, sizeof(attrib));\r
-\r
-        attrib.name = ATTRNAME_ADDR_LIST;\r
-        attrib.id = KCDB_ATTR_INVALID;\r
-        attrib.type = type_id_addr_list;\r
-        attrib.flags = KCDB_ATTR_FLAG_TRANSIENT;\r
-        LoadString(hResModule, IDS_ADDR_LIST_SHORT_DESC, sbuf, ARRAYLENGTH(sbuf));\r
-        LoadString(hResModule, IDS_ADDR_LIST_LONG_DESC, lbuf, ARRAYLENGTH(lbuf));\r
-        attrib.short_desc = sbuf;\r
-        attrib.long_desc = lbuf;\r
-        \r
-        rv = kcdb_attrib_register(&attrib, &attr_id_addr_list);\r
-\r
-        if(KHM_FAILED(rv))\r
-            goto _exit;\r
-\r
-        attr_regd_addr_list = TRUE;\r
-    }\r
-\r
-    if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_KRB5_FLAGS, &attr_id_krb5_flags))) {\r
-        kcdb_attrib attrib;\r
-        wchar_t sbuf[KCDB_MAXCCH_SHORT_DESC];\r
-\r
-        /* although we are loading a long descriptoin, it still fits\r
-        in the short descriptoin buffer */\r
-\r
-        ZeroMemory(&attrib, sizeof(attrib));\r
-\r
-        attrib.name = ATTRNAME_KRB5_FLAGS;\r
-        attrib.id = KCDB_ATTR_INVALID;\r
-        attrib.type = type_id_krb5_flags;\r
-        attrib.flags = KCDB_ATTR_FLAG_TRANSIENT;\r
-        LoadString(hResModule, IDS_KRB5_FLAGS_SHORT_DESC, sbuf, ARRAYLENGTH(sbuf));\r
-        attrib.short_desc = sbuf;\r
-        attrib.long_desc = NULL;\r
-        \r
-        rv = kcdb_attrib_register(&attrib, &attr_id_krb5_flags);\r
-\r
-        if(KHM_FAILED(rv))\r
-            goto _exit;\r
-\r
-        attr_regd_krb5_flags = TRUE;\r
-    }\r
-\r
-    if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_KRB5_CCNAME, &attr_id_krb5_ccname))) {\r
-        kcdb_attrib attrib;\r
-        wchar_t sbuf[KCDB_MAXCCH_SHORT_DESC];\r
-        wchar_t lbuf[KCDB_MAXCCH_SHORT_DESC];\r
-        /* although we are loading a long descriptoin, it still fits\r
-        in the short descriptoin buffer */\r
-\r
-        ZeroMemory(&attrib, sizeof(attrib));\r
-\r
-        attrib.name = ATTRNAME_KRB5_CCNAME;\r
-        attrib.id = KCDB_ATTR_INVALID;\r
-        attrib.type = KCDB_TYPE_STRING;\r
-        attrib.flags =\r
-         KCDB_ATTR_FLAG_PROPERTY |\r
-         KCDB_ATTR_FLAG_TRANSIENT;\r
-        LoadString(hResModule, IDS_KRB5_CCNAME_SHORT_DESC, sbuf, ARRAYLENGTH(sbuf));\r
-        LoadString(hResModule, IDS_KRB5_CCNAME_LONG_DESC, lbuf, ARRAYLENGTH(lbuf));\r
-        attrib.short_desc = sbuf;\r
-        attrib.long_desc = lbuf;\r
-        \r
-        rv = kcdb_attrib_register(&attrib, &attr_id_krb5_ccname);\r
-\r
-        if(KHM_FAILED(rv))\r
-            goto _exit;\r
-\r
-        attr_regd_krb5_ccname = TRUE;\r
-    }\r
-\r
-    if (KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_KVNO, &attr_id_kvno))) {\r
-        kcdb_attrib attrib;\r
-        wchar_t sbuf[KCDB_MAXCCH_SHORT_DESC];\r
-        wchar_t lbuf[KCDB_MAXCCH_LONG_DESC];\r
-        /* although we are loading a long description, it still fits\r
-           in the short description buffer */\r
-\r
-        ZeroMemory(&attrib, sizeof(attrib));\r
-\r
-        attrib.name = ATTRNAME_KVNO;\r
-        attrib.id = KCDB_ATTR_INVALID;\r
-        attrib.type = type_id_kvno;\r
-        attrib.flags = KCDB_ATTR_FLAG_TRANSIENT;\r
-        LoadString(hResModule, IDS_KVNO_SHORT_DESC, sbuf, ARRAYLENGTH(sbuf));\r
-        LoadString(hResModule, IDS_KVNO_LONG_DESC, lbuf, ARRAYLENGTH(lbuf));\r
-        attrib.short_desc = sbuf;\r
-        attrib.long_desc = lbuf;\r
-\r
-        rv = kcdb_attrib_register(&attrib, &attr_id_kvno);\r
-\r
-        if (KHM_FAILED(rv))\r
-            goto _exit;\r
-\r
-        attr_regd_kvno = TRUE;\r
-    }\r
-\r
-    if (KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_KRB5_IDFLAGS, &attr_id_krb5_idflags))) {\r
-        kcdb_attrib attrib;\r
-\r
-        ZeroMemory(&attrib, sizeof(attrib));\r
-\r
-        attrib.name = ATTRNAME_KRB5_IDFLAGS;\r
-        attrib.id = KCDB_ATTR_INVALID;\r
-        attrib.type = KCDB_TYPE_INT32;\r
-        attrib.flags = KCDB_ATTR_FLAG_PROPERTY |\r
-            KCDB_ATTR_FLAG_HIDDEN;\r
-        /* we don't bother localizing these strings since the\r
-           attribute is hidden.  The user will not see these\r
-           descriptions anyway. */\r
-        attrib.short_desc = L"Krb5 ID flags";\r
-        attrib.long_desc = L"Kerberos 5 Identity Flags";\r
-\r
-        rv = kcdb_attrib_register(&attrib, &attr_id_krb5_idflags);\r
-\r
-        if (KHM_FAILED(rv))\r
-            goto _exit;\r
-\r
-        attr_regd_krb5_idflags = TRUE;\r
-    }\r
-\r
-    rv = kmm_get_plugins_config(0, &csp_plugins);\r
-    if(KHM_FAILED(rv)) goto _exit;\r
-\r
-    rv = khc_load_schema(csp_plugins, schema_krbconfig);\r
-    if(KHM_FAILED(rv)) goto _exit;\r
-\r
-    rv = khc_open_space(csp_plugins, CSNAME_KRB5CRED, 0, &csp_krbcred);\r
-    if(KHM_FAILED(rv)) goto _exit;\r
-\r
-    rv = khc_open_space(csp_krbcred, CSNAME_PARAMS, 0, &csp_params);\r
-    if(KHM_FAILED(rv)) goto _exit;\r
-\r
-_exit:\r
-    return rv;\r
-}\r
-\r
-/* called by the NetIDMgr module manager */\r
-KHMEXP khm_int32 KHMAPI exit_module(kmm_module h_module) {\r
-    exit_imports();\r
-    exit_error_funcs();\r
-\r
-    if(attr_regd_key_enctype)\r
-        kcdb_attrib_unregister(attr_id_key_enctype);\r
-    if(attr_regd_tkt_enctype)\r
-        kcdb_attrib_unregister(attr_id_tkt_enctype);\r
-    if(attr_regd_addr_list)\r
-        kcdb_attrib_unregister(attr_id_addr_list);\r
-    if(attr_regd_krb5_flags)\r
-        kcdb_attrib_unregister(attr_id_krb5_flags);\r
-    if(attr_regd_krb5_ccname)\r
-        kcdb_attrib_unregister(attr_id_krb5_ccname);\r
-    if(attr_regd_kvno)\r
-        kcdb_attrib_unregister(attr_id_kvno);\r
-    if(attr_regd_krb5_idflags)\r
-        kcdb_attrib_unregister(attr_id_krb5_idflags);\r
-\r
-    if(type_regd_enctype)\r
-        kcdb_type_unregister(type_id_enctype);\r
-    if(type_regd_addr_list)\r
-        kcdb_type_unregister(type_id_addr_list);\r
-    if(type_regd_krb5_flags)\r
-        kcdb_type_unregister(type_id_krb5_flags);\r
-    if(type_regd_kvno)\r
-        kcdb_type_unregister(type_id_kvno);\r
-\r
-    if(csp_params) {\r
-        khc_close_space(csp_params);\r
-        csp_params = NULL;\r
-    }\r
-\r
-    if(csp_krbcred) {\r
-        khc_close_space(csp_krbcred);\r
-        csp_krbcred = NULL;\r
-    }\r
-\r
-    if(csp_plugins) {\r
-        khc_unload_schema(csp_plugins, schema_krbconfig);\r
-        khc_close_space(csp_plugins);\r
-        csp_plugins = NULL;\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS; /* the return code is ignored */\r
-}\r
-\r
-BOOL WINAPI DllMain(\r
-  HINSTANCE hinstDLL,\r
-  DWORD fdwReason,\r
-  LPVOID lpvReserved\r
-)\r
-{\r
-    switch(fdwReason) {\r
-        case DLL_PROCESS_ATTACH:\r
-            hInstance = hinstDLL;\r
-            init_krb();\r
-            break;\r
-        case DLL_PROCESS_DETACH:\r
-            exit_krb();\r
-            break;\r
-        case DLL_THREAD_ATTACH:\r
-            break;\r
-        case DLL_THREAD_DETACH:\r
-            break;\r
-    }\r
-\r
-    return TRUE;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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"Krb5Cred";
+
+khm_int32 type_id_enctype       = -1;
+khm_int32 type_id_addr_list     = -1;
+khm_int32 type_id_krb5_flags    = -1;
+khm_int32 type_id_krb5_princ    = -1;
+khm_int32 type_id_kvno          = -1;
+
+BOOL type_regd_enctype      = FALSE;
+BOOL type_regd_addr_list    = FALSE;
+BOOL type_regd_krb5_flags   = FALSE;
+BOOL type_regd_krb5_princ   = FALSE;
+BOOL type_regd_kvno         = 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;
+khm_int32 attr_id_kvno          = -1;
+khm_int32 attr_id_krb5_idflags  = -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;
+BOOL attr_regd_kvno         = FALSE;
+BOOL attr_regd_krb5_idflags = FALSE;
+
+khm_handle csp_plugins      = NULL;
+khm_handle csp_krbcred   = NULL;
+khm_handle csp_params       = NULL;
+
+BOOL is_k5_identpro = TRUE;
+
+khm_ui_4  k5_commctl_version;
+
+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;
+
+    k5_commctl_version = khm_get_commctl_version(NULL);
+
+    /* register the plugin */
+    ZeroMemory(&pi, sizeof(pi));
+    pi.name = KRB5_PLUGIN_NAME;
+    pi.type = KHM_PITYPE_CRED;
+    pi.icon = LoadImage(hResModule, MAKEINTRESOURCE(IDI_PLUGIN),
+                        IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE);
+    pi.flags = 0;
+    pi.msg_proc = k5_msg_callback;
+    pi.description = buf;
+    pi.dependencies = NULL;
+    LoadString(hResModule, IDS_PLUGIN_DESC,
+               buf, ARRAYLENGTH(buf));
+    kmm_provide_plugin(h_module, &pi);
+
+    ZeroMemory(&pi, sizeof(pi));
+    pi.name = KRB5_IDENTPRO_NAME;
+    pi.type = KHM_PITYPE_IDENT;
+    pi.icon = LoadImage(hResModule, MAKEINTRESOURCE(IDI_PLUGIN),
+                        IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE);
+    pi.flags = 0;
+    pi.msg_proc = k5_ident_callback;
+    pi.description = buf;
+    pi.dependencies = KRB5_PLUGIN_NAME L"\0";
+    LoadString(hResModule, IDS_IDENTPRO_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 = addr_list_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;
+    }
+
+    if (KHM_FAILED(kcdb_type_get_id(TYPENAME_KVNO, &type_id_kvno))) {
+        kcdb_type type;
+        kcdb_type *t32;
+
+        kcdb_type_get_info(KCDB_TYPE_INT32, &t32);
+
+        type.id = KCDB_TYPE_INVALID;
+        type.name = TYPENAME_KVNO;
+        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 = kvno_toString;
+
+        rv = kcdb_type_register(&type, &type_id_kvno);
+        kcdb_type_release_info(t32);
+
+        if (KHM_FAILED(rv))
+            goto _exit;
+
+        type_regd_kvno = 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 = KCDB_ATTR_FLAG_TRANSIENT;
+        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 = KCDB_ATTR_FLAG_TRANSIENT;
+        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 = KCDB_ATTR_FLAG_TRANSIENT;
+        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 = KCDB_ATTR_FLAG_TRANSIENT;
+        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 |
+         KCDB_ATTR_FLAG_TRANSIENT;
+        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;
+    }
+
+    if (KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_KVNO, &attr_id_kvno))) {
+        kcdb_attrib attrib;
+        wchar_t sbuf[KCDB_MAXCCH_SHORT_DESC];
+        wchar_t lbuf[KCDB_MAXCCH_LONG_DESC];
+        /* although we are loading a long description, it still fits
+           in the short description buffer */
+
+        ZeroMemory(&attrib, sizeof(attrib));
+
+        attrib.name = ATTRNAME_KVNO;
+        attrib.id = KCDB_ATTR_INVALID;
+        attrib.type = type_id_kvno;
+        attrib.flags = KCDB_ATTR_FLAG_TRANSIENT;
+        LoadString(hResModule, IDS_KVNO_SHORT_DESC, sbuf, ARRAYLENGTH(sbuf));
+        LoadString(hResModule, IDS_KVNO_LONG_DESC, lbuf, ARRAYLENGTH(lbuf));
+        attrib.short_desc = sbuf;
+        attrib.long_desc = lbuf;
+
+        rv = kcdb_attrib_register(&attrib, &attr_id_kvno);
+
+        if (KHM_FAILED(rv))
+            goto _exit;
+
+        attr_regd_kvno = TRUE;
+    }
+
+    if (KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_KRB5_IDFLAGS, &attr_id_krb5_idflags))) {
+        kcdb_attrib attrib;
+
+        ZeroMemory(&attrib, sizeof(attrib));
+
+        attrib.name = ATTRNAME_KRB5_IDFLAGS;
+        attrib.id = KCDB_ATTR_INVALID;
+        attrib.type = KCDB_TYPE_INT32;
+        attrib.flags = KCDB_ATTR_FLAG_PROPERTY |
+            KCDB_ATTR_FLAG_HIDDEN;
+        /* we don't bother localizing these strings since the
+           attribute is hidden.  The user will not see these
+           descriptions anyway. */
+        attrib.short_desc = L"Krb5 ID flags";
+        attrib.long_desc = L"Kerberos 5 Identity Flags";
+
+        rv = kcdb_attrib_register(&attrib, &attr_id_krb5_idflags);
+
+        if (KHM_FAILED(rv))
+            goto _exit;
+
+        attr_regd_krb5_idflags = 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(attr_regd_kvno)
+        kcdb_attrib_unregister(attr_id_kvno);
+    if(attr_regd_krb5_idflags)
+        kcdb_attrib_unregister(attr_id_krb5_idflags);
+
+    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(type_regd_kvno)
+        kcdb_type_unregister(type_id_kvno);
+
+    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;
+}
index edd64725da98dafab19fe01c540b35df65fe1f7e..d2458fe50e29de3c54ef57fe5db551df70921cdf 100644 (file)
-/*\r
- * Copyright (c) 2006 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<krbcred.h>\r
-#include<kherror.h>\r
-#include<khmsgtypes.h>\r
-#include<strsafe.h>\r
-#include<krb5.h>\r
-\r
-#include<commctrl.h>\r
-\r
-#include<assert.h>\r
-\r
-extern LPVOID k5_main_fiber;\r
-extern LPVOID k5_kinit_fiber;\r
-\r
-typedef struct k5_dlg_data_t {\r
-    khui_new_creds * nc;\r
-\r
-    khui_tracker tc_lifetime;\r
-    khui_tracker tc_renew;\r
-\r
-    BOOL dirty;                 /* is the data in sync with the\r
-                                   configuration store? */\r
-    BOOL sync;                  /* is the data in sync with the kinit\r
-                                   request? */\r
-    DWORD   renewable;\r
-    DWORD   forwardable;\r
-    DWORD   proxiable;\r
-    DWORD   addressless;\r
-    DWORD   publicIP;\r
-\r
-    wchar_t * cred_message;     /* overrides the credential text, if\r
-                                   non-NULL */\r
-    BOOL    pwd_change;         /* force a password change */\r
-} k5_dlg_data;\r
-\r
-\r
-INT_PTR\r
-k5_handle_wm_initdialog(HWND hwnd,\r
-                        WPARAM wParam,\r
-                        LPARAM lParam)\r
-{\r
-    HWND hw;\r
-    k5_dlg_data * d;\r
-    khui_new_creds_by_type * nct;\r
-    \r
-    d = PMALLOC(sizeof(*d));\r
-    ZeroMemory(d, sizeof(*d));\r
-    /* lParam is a pointer to a khui_new_creds structure */\r
-    d->nc = (khui_new_creds *) lParam;\r
-    khui_cw_find_type(d->nc, credtype_id_krb5, &nct);\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-    SetWindowLongPtr(hwnd, DWLP_USER, (LPARAM) d);\r
-#pragma warning(pop)\r
-\r
-    nct->aux = (LPARAM) d;\r
-\r
-    if (d->nc->subtype == KMSG_CRED_NEW_CREDS) {\r
-        khui_tracker_initialize(&d->tc_lifetime);\r
-        khui_tracker_initialize(&d->tc_renew);\r
-\r
-        hw = GetDlgItem(hwnd, IDC_NCK5_LIFETIME_EDIT);\r
-        khui_tracker_install(hw, &d->tc_lifetime);\r
-\r
-        hw = GetDlgItem(hwnd, IDC_NCK5_RENEW_EDIT);\r
-        khui_tracker_install(hw, &d->tc_renew);\r
-    }\r
-    return TRUE;\r
-}\r
-\r
-INT_PTR\r
-k5_handle_wm_destroy(HWND hwnd,\r
-                     WPARAM wParam,\r
-                     LPARAM lParam)\r
-{\r
-    k5_dlg_data * d;\r
-    khui_new_creds_by_type * nct = NULL;\r
-\r
-    d = (k5_dlg_data *) (LONG_PTR)\r
-        GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-    if (!d)\r
-        return TRUE;\r
-\r
-    khui_cw_find_type(d->nc, credtype_id_krb5, &nct);\r
-\r
-#ifdef DEBUG\r
-    assert(nct);\r
-#endif\r
-\r
-    nct->aux = 0;\r
-\r
-    if (d->nc->subtype == KMSG_CRED_NEW_CREDS) {\r
-        khui_tracker_kill_controls(&d->tc_renew);\r
-        khui_tracker_kill_controls(&d->tc_lifetime);\r
-    }\r
-\r
-    PFREE(d);\r
-\r
-    return TRUE;\r
-}\r
-\r
-LRESULT\r
-k5_force_password_change(k5_dlg_data * d) {\r
-    /* we are turning this dialog into a change password dialog... */\r
-    wchar_t wbuf[KHUI_MAXCCH_BANNER];\r
-\r
-    khui_cw_clear_prompts(d->nc);\r
-\r
-    LoadString(hResModule, IDS_NC_PWD_BANNER,\r
-               wbuf, ARRAYLENGTH(wbuf));\r
-    khui_cw_begin_custom_prompts(d->nc, 3, NULL, wbuf);\r
-\r
-    LoadString(hResModule, IDS_NC_PWD_PWD,\r
-               wbuf, ARRAYLENGTH(wbuf));\r
-    khui_cw_add_prompt(d->nc, KHUI_NCPROMPT_TYPE_PASSWORD,\r
-                       wbuf, NULL, KHUI_NCPROMPT_FLAG_HIDDEN);\r
-\r
-    LoadString(hResModule, IDS_NC_PWD_NPWD,\r
-               wbuf, ARRAYLENGTH(wbuf));\r
-    khui_cw_add_prompt(d->nc, KHUI_NCPROMPT_TYPE_NEW_PASSWORD,\r
-                       wbuf, NULL, KHUI_NCPROMPT_FLAG_HIDDEN);\r
-\r
-    LoadString(hResModule, IDS_NC_PWD_NPWD_AGAIN,\r
-               wbuf, ARRAYLENGTH(wbuf));\r
-    khui_cw_add_prompt(d->nc, KHUI_NCPROMPT_TYPE_NEW_PASSWORD_AGAIN,\r
-                       wbuf, NULL, KHUI_NCPROMPT_FLAG_HIDDEN);\r
-\r
-    d->pwd_change = TRUE;\r
-\r
-    if (is_k5_identpro &&\r
-        d->nc->n_identities > 0 &&\r
-        d->nc->identities[0]) {\r
-\r
-        kcdb_identity_set_flags(d->nc->identities[0],\r
-                                KCDB_IDENT_FLAG_VALID,\r
-                                KCDB_IDENT_FLAG_VALID);\r
-\r
-    }\r
-\r
-    PostMessage(d->nc->hwnd, KHUI_WM_NC_NOTIFY,\r
-                MAKEWPARAM(0, WMNC_UPDATE_CREDTEXT),\r
-                (LPARAM) d->nc);\r
-\r
-    return TRUE;\r
-}\r
-\r
-INT_PTR\r
-k5_handle_wmnc_notify(HWND hwnd,\r
-                      WPARAM wParam, \r
-                      LPARAM lParam)\r
-{\r
-    switch(HIWORD(wParam)) {\r
-    case WMNC_DIALOG_MOVE:\r
-        {\r
-            k5_dlg_data * d;\r
-                \r
-            d = (k5_dlg_data *)(LONG_PTR) \r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-            if (d->nc->subtype == KMSG_CRED_NEW_CREDS) {\r
-                khui_tracker_reposition(&d->tc_lifetime);\r
-                khui_tracker_reposition(&d->tc_renew);\r
-            }\r
-\r
-            return TRUE;\r
-        }\r
-        break;\r
-\r
-    case WMNC_DIALOG_SETUP:\r
-        {\r
-            k5_dlg_data * d;\r
-            BOOL old_sync;\r
-\r
-            d = (k5_dlg_data *)(LONG_PTR) \r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-            if (d->nc->subtype == KMSG_CRED_PASSWORD)\r
-                return TRUE;\r
-\r
-            /* we save the value of the 'sync' field here because some\r
-               of the notifications that are generated while setting\r
-               the controls overwrite the field. */\r
-            old_sync = d->sync;\r
-\r
-            /* need to update the controls with d->* */\r
-            SendDlgItemMessage(hwnd, IDC_NCK5_RENEWABLE, \r
-                               BM_SETCHECK,\r
-                              (d->renewable? BST_CHECKED : BST_UNCHECKED), \r
-                               0);\r
-            EnableWindow(GetDlgItem(hwnd, IDC_NCK5_RENEW_EDIT), \r
-                         !!d->renewable);\r
-\r
-            khui_tracker_refresh(&d->tc_lifetime);\r
-            khui_tracker_refresh(&d->tc_renew);\r
-\r
-            SendDlgItemMessage(hwnd, IDC_NCK5_FORWARDABLE, \r
-                               BM_SETCHECK,\r
-                               (d->forwardable ? BST_CHECKED : BST_UNCHECKED),\r
-                               0);\r
-\r
-            SendDlgItemMessage(hwnd, IDC_NCK5_ADDRESS,\r
-                               BM_SETCHECK,\r
-                               (d->addressless ? BST_CHECKED : BST_UNCHECKED),\r
-                               0);\r
-\r
-            SendDlgItemMessage(hwnd, IDC_NCK5_PUBLICIP,\r
-                               IPM_SETADDRESS,\r
-                               0, d->publicIP);\r
-\r
-            EnableWindow(GetDlgItem(hwnd, IDC_NCK5_PUBLICIP), !d->addressless);\r
-\r
-            d->sync = old_sync;\r
-        }\r
-        break;\r
-\r
-    case WMNC_CREDTEXT_LINK:\r
-        {\r
-            k5_dlg_data * d;\r
-            khui_htwnd_link * l;\r
-            khui_new_creds * nc;\r
-            wchar_t linktext[128];\r
-\r
-            d = (k5_dlg_data *)(LONG_PTR)\r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-            nc = d->nc;\r
-            l = (khui_htwnd_link *) lParam;\r
-\r
-            if (!l)\r
-                break;\r
-\r
-            StringCchCopyN(linktext, ARRAYLENGTH(linktext),\r
-                           l->id, l->id_len);\r
-\r
-            if (!wcscmp(linktext, L"Krb5Cred:!Passwd")) {\r
-                return k5_force_password_change(d);\r
-            }\r
-        }\r
-        break;\r
-\r
-    case WMNC_UPDATE_CREDTEXT:\r
-        {\r
-            k5_dlg_data * d;\r
-            khui_new_creds * nc;\r
-            khui_new_creds_by_type * nct;\r
-            wchar_t sbuf[1024];\r
-            wchar_t fbuf[256];\r
-            wchar_t tbuf[256];\r
-            size_t cbsize;\r
-            khm_int32 flags;\r
-\r
-            d = (k5_dlg_data *)(LONG_PTR) \r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-            nc = d->nc;\r
-            khui_cw_find_type(nc, credtype_id_krb5, &nct);\r
-\r
-            if(nct == NULL)\r
-                break;\r
-\r
-            if(nct->credtext)\r
-                PFREE(nct->credtext);\r
-            nct->credtext = NULL;\r
-\r
-            tbuf[0] = L'\0';\r
-\r
-            if (nc->n_identities > 0 &&\r
-                KHM_SUCCEEDED(kcdb_identity_get_flags(nc->identities[0], \r
-                                                      &flags)) &&\r
-                (flags & KCDB_IDENT_FLAG_VALID) &&\r
-                nc->subtype == KMSG_CRED_NEW_CREDS &&\r
-                !d->pwd_change) {\r
-\r
-                if (is_k5_identpro)\r
-                    k5_get_realm_from_nc(nc, tbuf, ARRAYLENGTH(tbuf));\r
-                else\r
-                    GetDlgItemText(hwnd, IDC_NCK5_REALM, tbuf, \r
-                                   ARRAYLENGTH(tbuf));\r
-\r
-                /*TODO: if additional realms were specified, then those\r
-                  must be listed as well */\r
-                LoadString(hResModule, IDS_KRB5_CREDTEXT_0, \r
-                           fbuf, ARRAYLENGTH(fbuf));\r
-                StringCbPrintf(sbuf, sizeof(sbuf), fbuf, \r
-                               tbuf);\r
-\r
-                StringCbLength(sbuf, sizeof(sbuf), &cbsize);\r
-                cbsize += sizeof(wchar_t);\r
-\r
-                nct->credtext = PMALLOC(cbsize);\r
-\r
-                StringCbCopy(nct->credtext, cbsize, sbuf);\r
-            } else if (nc->n_identities > 0 &&\r
-                       (nc->subtype == KMSG_CRED_PASSWORD ||\r
-                        (nc->subtype == KMSG_CRED_NEW_CREDS && d->pwd_change))) {\r
-                cbsize = sizeof(tbuf);\r
-                kcdb_identity_get_name(nc->identities[0], tbuf, &cbsize);\r
-\r
-                LoadString(hResModule, IDS_KRB5_CREDTEXT_P0,\r
-                           fbuf, ARRAYLENGTH(fbuf));\r
-                StringCbPrintf(sbuf, sizeof(sbuf), fbuf, tbuf);\r
-\r
-                StringCbLength(sbuf, sizeof(sbuf), &cbsize);\r
-                cbsize += sizeof(wchar_t);\r
-\r
-                nct->credtext = PMALLOC(cbsize);\r
-\r
-                StringCbCopy(nct->credtext, cbsize, sbuf);\r
-            } else {\r
-                if (d->cred_message) {\r
-                    StringCbLength(d->cred_message, KHUI_MAXCB_BANNER,\r
-                                   &cbsize);\r
-                    cbsize += sizeof(wchar_t);\r
-\r
-                    nct->credtext = PMALLOC(cbsize);\r
-\r
-                    StringCbCopy(nct->credtext, cbsize, d->cred_message);\r
-                }\r
-            }\r
-        }\r
-        break;\r
-\r
-    case WMNC_IDENTITY_CHANGE:\r
-        {\r
-            /* There has been a change of identity */\r
-            k5_dlg_data * d;\r
-\r
-            d = (k5_dlg_data *)(LONG_PTR) \r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-            kmq_post_sub_msg(k5_sub, KMSG_CRED, \r
-                             KMSG_CRED_DIALOG_NEW_IDENTITY, \r
-                             0, (void *) d->nc);\r
-        }\r
-        break;\r
-\r
-    case WMNC_DIALOG_PREPROCESS:\r
-        {\r
-            k5_dlg_data * d;\r
-\r
-            d = (k5_dlg_data *)(LONG_PTR) \r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-            if(!d->sync && d->nc->result == KHUI_NC_RESULT_PROCESS) {\r
-                kmq_post_sub_msg(k5_sub, KMSG_CRED, \r
-                                 KMSG_CRED_DIALOG_NEW_OPTIONS, \r
-                                 0, (void *) d->nc);\r
-            }\r
-        }\r
-        break;\r
-\r
-    case K5_SET_CRED_MSG:\r
-        {\r
-            k5_dlg_data * d;\r
-            khm_size cb;\r
-            wchar_t * msg;\r
-\r
-            d = (k5_dlg_data *) (LONG_PTR)\r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-            msg = (wchar_t *) lParam;\r
-\r
-            if (d->cred_message) {\r
-                PFREE(d->cred_message);\r
-                d->cred_message = NULL;\r
-            }\r
-\r
-            if (msg &&\r
-                SUCCEEDED(StringCbLength(msg,\r
-                                         KHUI_MAXCB_MESSAGE,\r
-                                         &cb))) {\r
-                cb += sizeof(wchar_t);\r
-                d->cred_message = PMALLOC(cb);\r
-#ifdef DEBUG\r
-                assert(d->cred_message);\r
-#endif\r
-                StringCbCopy(d->cred_message, cb, msg);\r
-            }\r
-        }\r
-        break;\r
-    }\r
-\r
-    return 0;\r
-}\r
-\r
-INT_PTR\r
-k5_handle_wm_notify(HWND hwnd,\r
-                    WPARAM wParam,\r
-                    LPARAM lParam) {\r
-    LPNMHDR pnmh;\r
-    k5_dlg_data * d;\r
-\r
-    pnmh = (LPNMHDR) lParam;\r
-    if (pnmh->idFrom == IDC_NCK5_PUBLICIP &&\r
-        pnmh->code == IPN_FIELDCHANGED) {\r
-\r
-        d = (k5_dlg_data *) (LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        SendDlgItemMessage(hwnd, IDC_NCK5_PUBLICIP,\r
-                           IPM_GETADDRESS,\r
-                           0, (LPARAM) &d->publicIP);\r
-\r
-        d->dirty = TRUE;\r
-        d->sync = FALSE;\r
-\r
-        return TRUE;\r
-    }\r
-\r
-    return 0;\r
-}\r
-\r
-INT_PTR\r
-k5_handle_wm_command(HWND hwnd,\r
-                     WPARAM wParam,\r
-                     LPARAM lParam)\r
-{\r
-    int cid;\r
-    int notif;\r
-    k5_dlg_data * d;\r
-\r
-    d = (k5_dlg_data *)(LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-    cid = LOWORD(wParam);\r
-    notif = HIWORD(wParam);\r
-\r
-    if(notif == BN_CLICKED && cid == IDC_NCK5_RENEWABLE) {\r
-        int c;\r
-        c = (int) SendDlgItemMessage(hwnd, IDC_NCK5_RENEWABLE, \r
-                                     BM_GETCHECK, 0, 0);\r
-        if(c==BST_CHECKED) {\r
-            EnableWindow(GetDlgItem(hwnd, IDC_NCK5_RENEW_EDIT), TRUE);\r
-            d->renewable = TRUE;\r
-        } else {\r
-            EnableWindow(GetDlgItem(hwnd, IDC_NCK5_RENEW_EDIT), FALSE);\r
-            d->renewable = FALSE;\r
-        }\r
-        d->dirty = TRUE;\r
-        d->sync = FALSE;\r
-    } else if(notif == BN_CLICKED && cid == IDC_NCK5_FORWARDABLE) {\r
-        int c;\r
-        c = (int) SendDlgItemMessage(hwnd, IDC_NCK5_FORWARDABLE, \r
-                                     BM_GETCHECK, 0, 0);\r
-        if(c==BST_CHECKED) {\r
-            d->forwardable = TRUE;\r
-        } else {\r
-            d->forwardable = FALSE;\r
-        }\r
-        d->dirty = TRUE;\r
-        d->sync = FALSE;\r
-    } else if (notif == BN_CLICKED && cid == IDC_NCK5_ADDRESS) {\r
-        int c;\r
-\r
-        c = (int) SendDlgItemMessage(hwnd, IDC_NCK5_ADDRESS,\r
-                                     BM_GETCHECK, 0, 0);\r
-\r
-        if (c==BST_CHECKED) {\r
-            d->addressless = TRUE;\r
-        } else {\r
-            d->addressless = FALSE;\r
-        }\r
-        d->dirty = TRUE;\r
-        d->sync = FALSE;\r
-\r
-        EnableWindow(GetDlgItem(hwnd, IDC_NCK5_PUBLICIP), !d->addressless);\r
-    } else if (notif == EN_CHANGE && (cid == IDC_NCK5_RENEW_EDIT ||\r
-                                      cid == IDC_NCK5_LIFETIME_EDIT)) {\r
-        d->dirty = TRUE;\r
-        d->sync = FALSE;\r
-    } else if((notif == CBN_SELCHANGE || \r
-               notif == CBN_KILLFOCUS) && \r
-              cid == IDC_NCK5_REALM &&\r
-              !is_k5_identpro) {\r
-        /* find out what the realm of the current identity\r
-           is, and if they are the same, then we don't do\r
-           anything */\r
-        wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-        wchar_t realm[KCDB_IDENT_MAXCCH_NAME];\r
-        wchar_t *r;\r
-        khm_size cbsize;\r
-        khm_handle ident;\r
-        int idx;\r
-\r
-        if(d->nc->n_identities > 0) {\r
-            if(notif == CBN_SELCHANGE) {\r
-                idx = (int) SendDlgItemMessage(hwnd, IDC_NCK5_REALM, \r
-                                               CB_GETCURSEL, 0, 0);\r
-                SendDlgItemMessage(hwnd, IDC_NCK5_REALM, \r
-                                   CB_GETLBTEXT, idx, (LPARAM) realm);\r
-            } else {\r
-                GetDlgItemText(hwnd, IDC_NCK5_REALM, \r
-                               realm, ARRAYLENGTH(realm));\r
-            }\r
-            cbsize = sizeof(idname);\r
-            if(KHM_SUCCEEDED(kcdb_identity_get_name(d->nc->identities[0], \r
-                                                  idname, &cbsize))) {\r
-                r = wcschr(idname, L'@');\r
-                if(r && !wcscmp(realm, r+1))\r
-                    return 0; /* nothing to do */\r
-\r
-                if(!r) {\r
-                    r = idname + wcslen(idname);\r
-                    *r++ = L'@';\r
-                    *r++ = 0;\r
-                }\r
-\r
-                /* if we get here, we have a new user */\r
-                StringCchCopy(r+1, \r
-                              ARRAYLENGTH(idname) - ((r+1) - idname), \r
-                              realm);\r
-                if(KHM_SUCCEEDED(kcdb_identity_create(idname, \r
-                                                    KCDB_IDENT_FLAG_CREATE, \r
-                                                    &ident))) {\r
-                    khui_cw_set_primary_id(d->nc, ident);\r
-                    kcdb_identity_release(ident);\r
-                }\r
-                return 0;\r
-            }\r
-        }\r
-\r
-        /* if we get here, we have a new realm, but there is no\r
-           identity */\r
-        PostMessage(d->nc->hwnd, KHUI_WM_NC_NOTIFY, \r
-                    MAKEWPARAM(0, WMNC_UPDATE_CREDTEXT), 0);\r
-    }\r
-\r
-    return 0;\r
-}\r
-                       \r
-\r
-/*  Dialog procedure for the Krb5 credentials type panel.\r
-\r
-    NOTE: Runs in the context of the UI thread\r
-*/\r
-INT_PTR CALLBACK \r
-k5_nc_dlg_proc(HWND hwnd,\r
-               UINT uMsg,\r
-               WPARAM wParam,\r
-               LPARAM lParam)\r
-{\r
-    switch(uMsg) {\r
-    case WM_INITDIALOG: \r
-        return k5_handle_wm_initdialog(hwnd, wParam, lParam);\r
-\r
-    case WM_COMMAND:\r
-        return k5_handle_wm_command(hwnd, wParam, lParam);\r
-\r
-    case KHUI_WM_NC_NOTIFY:\r
-        return k5_handle_wmnc_notify(hwnd, wParam, lParam);\r
-\r
-    case WM_NOTIFY:\r
-        return k5_handle_wm_notify(hwnd, wParam, lParam);\r
-\r
-    case WM_DESTROY:\r
-        return k5_handle_wm_destroy(hwnd, wParam, lParam);\r
-    }\r
-    return FALSE;\r
-}\r
-\r
-/* forward dcl */\r
-krb5_error_code KRB5_CALLCONV \r
-k5_kinit_prompter(krb5_context context,\r
-                  void *data,\r
-                  const char *name,\r
-                  const char *banner,\r
-                  int num_prompts,\r
-                  krb5_prompt prompts[]);\r
-\r
-\r
-\r
-fiber_job g_fjob;   /* global fiber job object */\r
-\r
-static BOOL \r
-k5_cached_kinit_prompter(void);\r
-\r
-static BOOL\r
-k5_cp_check_continue(void);\r
-\r
-/*\r
-    Runs in the context of the krb5 plugin's slave fiber\r
-*/\r
-VOID CALLBACK \r
-k5_kinit_fiber_proc(PVOID lpParameter)\r
-{\r
-    while(TRUE)\r
-    {\r
-        if(g_fjob.command == FIBER_CMD_KINIT) {\r
-            g_fjob.state = FIBER_STATE_KINIT;\r
-\r
-            g_fjob.prompt_set = 0;\r
-\r
-            if (k5_cached_kinit_prompter()) {\r
-                SwitchToFiber(k5_main_fiber);\r
-\r
-                if (g_fjob.command != FIBER_CMD_CONTINUE)\r
-                    goto _switch_to_main;\r
-\r
-                if (!k5_cp_check_continue()) {\r
-                    g_fjob.code = KRB5KRB_AP_ERR_BAD_INTEGRITY;\r
-                    goto _switch_to_main;\r
-                }\r
-            }\r
-\r
-#ifdef DEBUG\r
-            /* log the state of g_fjob.* */\r
-            _reportf(L"g_fjob state prior to calling khm_krb5_kinit() :");\r
-            _reportf(L"  g_fjob.principal = [%S]", g_fjob.principal);\r
-            _reportf(L"  g_fjob.code      = %d", g_fjob.code);\r
-            _reportf(L"  g_fjob.state     = %d", g_fjob.state);\r
-            _reportf(L"  g_fjob.prompt_set= %d", g_fjob.prompt_set);\r
-            _reportf(L"  g_fjob.valid_principal = %d", (int) g_fjob.valid_principal);\r
-#endif\r
-\r
-            /* If we don't know if we have a valid principal, we\r
-               restrict the options that are set when we call kinit.\r
-               This way we will be able to use the response from the\r
-               KDC to verify the principal. */\r
-\r
-            g_fjob.retry_if_valid_principal = (g_fjob.forwardable ||\r
-                                               g_fjob.proxiable ||\r
-                                               g_fjob.renewable);\r
-\r
-        retry_kinit:\r
-            g_fjob.code =\r
-                khm_krb5_kinit(0,\r
-                               g_fjob.principal,\r
-                               g_fjob.password,\r
-                               g_fjob.ccache,\r
-                               g_fjob.lifetime,\r
-                               g_fjob.valid_principal ? g_fjob.forwardable : 0,\r
-                               g_fjob.valid_principal ? g_fjob.proxiable : 0,\r
-                               (g_fjob.valid_principal && g_fjob.renewable ? g_fjob.renew_life : 0),\r
-                               g_fjob.addressless,\r
-                               g_fjob.publicIP,\r
-                               k5_kinit_prompter,\r
-                               &g_fjob);\r
-\r
-            /* If the principal was found to be valid, and if we\r
-               restricted the options that were being passed to kinit,\r
-               then we need to retry the kinit call.  This time we use\r
-               the real options. */\r
-            if (g_fjob.state == FIBER_STATE_RETRY_KINIT) {\r
-#ifdef DEBUG\r
-                assert(g_fjob.valid_principal);\r
-#endif\r
-                g_fjob.state = FIBER_STATE_KINIT;\r
-                goto retry_kinit;\r
-            }\r
-        }\r
-\r
-    _switch_to_main:\r
-        g_fjob.state = FIBER_STATE_NONE;\r
-\r
-        SwitchToFiber(k5_main_fiber);\r
-    }\r
-}\r
-\r
-/* return TRUE if we should go ahead with creds acquisition */\r
-static BOOL\r
-k5_cp_check_continue(void) {\r
-    khm_size i;\r
-    khm_size n_p;\r
-    khui_new_creds_prompt * p;\r
-    size_t cch;\r
-\r
-#ifdef DEBUG\r
-    assert(g_fjob.nc);\r
-#endif\r
-\r
-    if (KHM_FAILED(khui_cw_get_prompt_count(g_fjob.nc, &n_p))) {\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-        return TRUE;\r
-    }\r
-\r
-    khui_cw_sync_prompt_values(g_fjob.nc);\r
-\r
-    g_fjob.null_password = FALSE;\r
-\r
-    /* we are just checking whether there was a password field that\r
-       was left empty, in which case we can't continue with the\r
-       credentials acquisition. */\r
-    for (i=0; i < n_p; i++) {\r
-        if(KHM_FAILED(khui_cw_get_prompt(g_fjob.nc,\r
-                                         (int) i,\r
-                                         &p)))\r
-            continue;\r
-        if(p->type == KHUI_NCPROMPT_TYPE_PASSWORD) {\r
-            if (p->value == NULL ||\r
-                FAILED(StringCchLength(p->value, KHUI_MAXCCH_PROMPT,\r
-                                       &cch)) ||\r
-                cch == 0) {\r
-                g_fjob.null_password = TRUE;\r
-                return FALSE;\r
-            } else\r
-                break;\r
-        }\r
-    }\r
-\r
-    return TRUE;\r
-}\r
-\r
-/* returns true if we find cached prompts */\r
-static BOOL \r
-k5_cached_kinit_prompter(void) {\r
-    BOOL rv = FALSE;\r
-    khm_handle ident;\r
-    khm_handle csp_idconfig = NULL;\r
-    khm_handle csp_k5config = NULL;\r
-    khm_handle csp_prcache = NULL;\r
-    khm_size cb;\r
-    khm_size n_cur_prompts;\r
-    khm_int32 n_prompts;\r
-    khm_int32 i;\r
-    khm_int64 iexpiry;\r
-    FILETIME expiry;\r
-    FILETIME current;\r
-\r
-#ifdef DEBUG\r
-    assert(g_fjob.nc);\r
-#endif\r
-\r
-    ident = g_fjob.identity;\r
-    if (!ident)\r
-        return FALSE;\r
-\r
-    /* don't need to hold ident, since it is already held in g_fjob\r
-       and it doesn't change until we return */\r
-\r
-    if (KHM_FAILED(kcdb_identity_get_config(ident, 0, &csp_idconfig)) ||\r
-\r
-        KHM_FAILED(khc_open_space(csp_idconfig, CSNAME_KRB5CRED,\r
-                                  0, &csp_k5config)) ||\r
-\r
-        KHM_FAILED(khc_open_space(csp_k5config, CSNAME_PROMPTCACHE,\r
-                                  0, &csp_prcache)) ||\r
-\r
-        KHM_FAILED(khc_read_int32(csp_prcache, L"PromptCount",\r
-                                  &n_prompts)) ||\r
-        n_prompts == 0)\r
-\r
-        goto _cleanup;\r
-\r
-    if (KHM_SUCCEEDED(khc_read_int64(csp_prcache, L"ExpiresOn", &iexpiry))) {\r
-        /* has the cache expired? */\r
-        expiry = IntToFt(iexpiry);\r
-        GetSystemTimeAsFileTime(&current);\r
-\r
-        if (CompareFileTime(&expiry, &current) < 0)\r
-            /* already expired */\r
-            goto _cleanup;\r
-    } else {\r
-        /* if there is no value for ExpiresOn, we assume the prompts\r
-           have already expired. */\r
-        goto _cleanup;\r
-    }\r
-\r
-    /* we found a prompt cache.  We take this to imply that the\r
-       principal is valid. */\r
-    g_fjob.valid_principal = TRUE;\r
-\r
-    /* check if there are any prompts currently showing.  If there are\r
-       we check if they are the same as the ones we are going to show.\r
-       In which case we just reuse the exisitng prompts */\r
-    if (KHM_FAILED(khui_cw_get_prompt_count(g_fjob.nc, \r
-                                            &n_cur_prompts)) ||\r
-        n_prompts != (khm_int32) n_cur_prompts)\r
-        goto _show_new_prompts;\r
-\r
-    for(i = 0; i < n_prompts; i++) {\r
-        wchar_t wsname[8];\r
-        wchar_t wprompt[KHUI_MAXCCH_PROMPT];\r
-        khm_handle csp_p = NULL;\r
-        khm_int32 p_type;\r
-        khm_int32 p_flags;\r
-        khui_new_creds_prompt * p;\r
-\r
-        if (KHM_FAILED(khui_cw_get_prompt(g_fjob.nc, i, &p)))\r
-            break;\r
-\r
-        StringCbPrintf(wsname, sizeof(wsname), L"%d", i);\r
-\r
-        if (KHM_FAILED(khc_open_space(csp_prcache, wsname, 0, &csp_p)))\r
-            break;\r
-\r
-        cb = sizeof(wprompt);\r
-        if (KHM_FAILED(khc_read_string(csp_p, L"Prompt", \r
-                                       wprompt, &cb))) {\r
-            khc_close_space(csp_p);\r
-            break;\r
-        }\r
-\r
-        if (KHM_FAILED(khc_read_int32(csp_p, L"Type", &p_type)))\r
-            p_type = 0;\r
-\r
-        if (KHM_FAILED(khc_read_int32(csp_p, L"Flags", &p_flags)))\r
-            p_flags = 0;\r
-\r
-        if (                    /* if we received a prompt string,\r
-                                   then it should be the same as the\r
-                                   one that is displayed */\r
-            (wprompt[0] &&\r
-             (p->prompt == NULL ||\r
-              wcscmp(wprompt, p->prompt))) ||\r
-\r
-                                /* if we didn't receive one, then\r
-                                   there shouldn't be one displayed.\r
-                                   This case really shouldn't happen\r
-                                   in reality, but we check anyway. */\r
-            (!wprompt[0] &&\r
-             p->prompt != NULL) ||\r
-\r
-                                /* the type should match */\r
-            (p_type != p->type) ||\r
-\r
-                                /* if this prompt should be hidden,\r
-                                   then it must also be so */\r
-            (p_flags != p->flags)\r
-            ) {\r
-\r
-            khc_close_space(csp_p);\r
-            break;\r
-\r
-        }\r
-        \r
-\r
-        khc_close_space(csp_p);\r
-    }\r
-\r
-    if (i == n_prompts) {\r
-        rv = TRUE;\r
-        goto _cleanup;\r
-    }\r
-\r
- _show_new_prompts:\r
-\r
-    khui_cw_clear_prompts(g_fjob.nc);\r
-\r
-    {\r
-        wchar_t wbanner[KHUI_MAXCCH_BANNER];\r
-        wchar_t wpname[KHUI_MAXCCH_PNAME];\r
-\r
-        cb = sizeof(wbanner);\r
-        if (KHM_FAILED(khc_read_string(csp_prcache, L"Banner", \r
-                                      wbanner, &cb)))\r
-            wbanner[0] = 0;\r
-\r
-        cb = sizeof(wpname);\r
-        if (KHM_FAILED(khc_read_string(csp_prcache, L"Name",\r
-                                       wpname, &cb)))\r
-            wpname[0] = 0;\r
-\r
-        khui_cw_begin_custom_prompts(g_fjob.nc,\r
-                                     n_prompts,\r
-                                     (wbanner[0]? wbanner: NULL),\r
-                                     (wpname[0]? wpname: NULL));\r
-    }\r
-\r
-    for(i = 0; i < n_prompts; i++) {\r
-        wchar_t wsname[8];\r
-        wchar_t wprompt[KHUI_MAXCCH_PROMPT];\r
-        khm_handle csp_p = NULL;\r
-        khm_int32 p_type;\r
-        khm_int32 p_flags;\r
-\r
-        StringCbPrintf(wsname, sizeof(wsname), L"%d", i);\r
-\r
-        if (KHM_FAILED(khc_open_space(csp_prcache, wsname, 0, &csp_p)))\r
-            break;\r
-\r
-        cb = sizeof(wprompt);\r
-        if (KHM_FAILED(khc_read_string(csp_p, L"Prompt", \r
-                                       wprompt, &cb))) {\r
-            khc_close_space(csp_p);\r
-            break;\r
-        }\r
-\r
-        if (KHM_FAILED(khc_read_int32(csp_p, L"Type", &p_type)))\r
-            p_type = 0;\r
-\r
-        if (KHM_FAILED(khc_read_int32(csp_p, L"Flags", &p_flags)))\r
-            p_flags = 0;\r
-\r
-        khui_cw_add_prompt(g_fjob.nc, p_type, wprompt, NULL, p_flags);\r
-\r
-        khc_close_space(csp_p);\r
-    }\r
-\r
-    if (i < n_prompts) {\r
-        khui_cw_clear_prompts(g_fjob.nc);\r
-    } else {\r
-        rv = TRUE;\r
-    }\r
-     \r
- _cleanup:\r
-\r
-    if (csp_prcache)\r
-        khc_close_space(csp_prcache);\r
-\r
-    if (csp_k5config)\r
-        khc_close_space(csp_k5config);\r
-\r
-    if (csp_idconfig)\r
-        khc_close_space(csp_idconfig);\r
-\r
-    return rv;\r
-}\r
-\r
-/*  Runs in the context of the Krb5 plugin's slave fiber */\r
-krb5_error_code KRB5_CALLCONV \r
-k5_kinit_prompter(krb5_context context,\r
-                  void *data,\r
-                  const char *name,\r
-                  const char *banner,\r
-                  int num_prompts,\r
-                  krb5_prompt prompts[])\r
-{\r
-    int i;\r
-    khui_new_creds * nc;\r
-    krb5_prompt_type * ptypes;\r
-    khm_size ncp;\r
-    krb5_error_code code = 0;\r
-    BOOL new_prompts = TRUE;\r
-    khm_handle csp_prcache = NULL;\r
-\r
-#ifdef DEBUG\r
-    _reportf(L"k5_kinit_prompter() received %d prompts with name=[%S] banner=[%S]",\r
-             num_prompts,\r
-             name, banner);\r
-    for (i=0; i < num_prompts; i++) {\r
-        _reportf(L"Prompt[%d]: string[%S]", i, prompts[i].prompt);\r
-    }\r
-#endif\r
-\r
-    /* we got prompts?  Then we assume that the principal is valid */\r
-\r
-    if (!g_fjob.valid_principal) {\r
-        g_fjob.valid_principal = TRUE;\r
-\r
-        /* if the flags that were used to call kinit were restricted\r
-           because we didn't know the validity of the principal, then\r
-           we need to go back and retry the call with the correct\r
-           flags. */\r
-        if (g_fjob.retry_if_valid_principal) {\r
-            _reportf(L"Retrying kinit call due to restricted flags on first call.");\r
-            g_fjob.state = FIBER_STATE_RETRY_KINIT;\r
-            return KRB5_LIBOS_PWDINTR;\r
-        }\r
-    }\r
-\r
-    nc = g_fjob.nc;\r
-\r
-    if(pkrb5_get_prompt_types)\r
-        ptypes = pkrb5_get_prompt_types(context);\r
-    else\r
-        ptypes = NULL;\r
-\r
-    /* check if we are already showing the right prompts */\r
-    khui_cw_get_prompt_count(nc, &ncp);\r
-\r
-    if (num_prompts != (int) ncp)\r
-        goto _show_new_prompts;\r
-\r
-    for (i=0; i < num_prompts; i++) {\r
-        wchar_t wprompt[KHUI_MAXCCH_PROMPT];\r
-        khui_new_creds_prompt * p;\r
-\r
-        if(prompts[i].prompt) {\r
-            AnsiStrToUnicode(wprompt, sizeof(wprompt), \r
-                             prompts[i].prompt);\r
-        } else {\r
-            wprompt[0] = 0;\r
-        }\r
-\r
-        if (KHM_FAILED(khui_cw_get_prompt(nc, i, &p)))\r
-            break;\r
-\r
-        if (                    /* if we received a prompt string,\r
-                                   then it should be the same as the\r
-                                   one that is displayed */\r
-            (wprompt[0] &&\r
-             (p->prompt == NULL ||\r
-              wcscmp(wprompt, p->prompt))) ||\r
-                                /* if we didn't receive one, then\r
-                                   there shouldn't be one displayed.\r
-                                   This case really shouldn't happen\r
-                                   in reality, but we check anyway. */\r
-            (!wprompt[0] &&\r
-             p->prompt != NULL) ||\r
-                                /* the type should match */\r
-            (ptypes &&\r
-             ptypes[i] != p->type) ||\r
-            (!ptypes &&\r
-             p->type != 0) ||\r
-                                /* if this prompt should be hidden,\r
-                                   then it must also be so */\r
-            (prompts[i].hidden &&\r
-             !(p->flags & KHUI_NCPROMPT_FLAG_HIDDEN)) ||\r
-            (!prompts[i].hidden &&\r
-             (p->flags & KHUI_NCPROMPT_FLAG_HIDDEN))\r
-            )\r
-            break;\r
-    }\r
-\r
-    if (i < num_prompts)\r
-        goto _show_new_prompts;\r
-\r
-    new_prompts = FALSE;\r
-\r
-    /* ok. looks like we are already showing the same set of prompts\r
-       that we were supposed to show.  Sync up the values and go\r
-       ahead. */\r
-    khui_cw_sync_prompt_values(nc);\r
-    goto _process_prompts;\r
-\r
- _show_new_prompts:\r
-    /* special case.  if there are no actual input controls involved,\r
-       then we have to show an alerter window and pass through */\r
-    if (num_prompts == 0) {\r
-        wchar_t wbanner[KHUI_MAXCCH_BANNER];\r
-        wchar_t wname[KHUI_MAXCCH_PNAME];\r
-        wchar_t wident[KCDB_IDENT_MAXCCH_NAME];\r
-        wchar_t wmsg[KHUI_MAXCCH_MESSAGE];\r
-        wchar_t wfmt[KHUI_MAXCCH_BANNER];\r
-        khm_size cb;\r
-\r
-        if (!banner) {\r
-            code = 0;\r
-            g_fjob.null_password = FALSE;\r
-            goto _exit;\r
-        } else {\r
-            AnsiStrToUnicode(wbanner, sizeof(wbanner), banner);\r
-        }\r
-\r
-        if (name) {\r
-            AnsiStrToUnicode(wname, sizeof(wname), name);\r
-        } else {\r
-            LoadString(hResModule,\r
-                       IDS_KRB5_WARNING,\r
-                       wname,\r
-                       ARRAYLENGTH(wname));\r
-        }\r
-\r
-        cb = sizeof(wident);\r
-        if (KHM_FAILED(kcdb_identity_get_name(g_fjob.identity, wident, &cb)))\r
-            wident[0] = L'\0';\r
-\r
-        LoadString(hResModule,\r
-                   IDS_KRB5_WARN_FMT,\r
-                   wfmt,\r
-                   ARRAYLENGTH(wfmt));\r
-\r
-        StringCbPrintf(wmsg, sizeof(wmsg), wfmt, wident, wbanner);\r
-\r
-        khui_alert_show_simple(wname, wmsg, KHERR_WARNING);\r
-\r
-        code = 0;\r
-        g_fjob.null_password = FALSE;\r
-        goto _exit;\r
-    }\r
-\r
-    /* in addition to showing new prompts, we also cache the set of\r
-       prompts. */\r
-    if(g_fjob.prompt_set == 0) {\r
-        khm_handle csp_idconfig = NULL;\r
-        khm_handle csp_idk5 = NULL;\r
-\r
-        kcdb_identity_get_config(g_fjob.identity,\r
-                                 KHM_FLAG_CREATE,\r
-                                 &csp_idconfig);\r
-\r
-        if (csp_idconfig != NULL)\r
-            khc_open_space(csp_idconfig,\r
-                           CSNAME_KRB5CRED,\r
-                           KHM_FLAG_CREATE,\r
-                           &csp_idk5);\r
-\r
-        if (csp_idk5 != NULL)\r
-            khc_open_space(csp_idk5,\r
-                           CSNAME_PROMPTCACHE,\r
-                           KHM_FLAG_CREATE,\r
-                           &csp_prcache);\r
-\r
-        khc_close_space(csp_idconfig);\r
-        khc_close_space(csp_idk5);\r
-    }\r
-\r
-    {\r
-        wchar_t wbanner[KHUI_MAXCCH_BANNER];\r
-        wchar_t wname[KHUI_MAXCCH_PNAME];\r
-        FILETIME current;\r
-        FILETIME lifetime;\r
-        FILETIME expiry;\r
-        khm_int64 iexpiry;\r
-        khm_int32 t = 0;\r
-\r
-        if(banner)\r
-            AnsiStrToUnicode(wbanner, sizeof(wbanner), banner);\r
-        if(name)\r
-            AnsiStrToUnicode(wname, sizeof(wname), name);\r
-\r
-        khui_cw_clear_prompts(nc);\r
-\r
-        khui_cw_begin_custom_prompts(\r
-            nc, \r
-            num_prompts, \r
-            (banner)?wbanner:NULL,\r
-            (name)?wname:NULL);\r
-\r
-        if (csp_prcache) {\r
-\r
-            if (banner)\r
-                khc_write_string(csp_prcache,\r
-                                 L"Banner",\r
-                                 wbanner);\r
-            else\r
-                khc_write_string(csp_prcache,\r
-                                 L"Banner",\r
-                                 L"");\r
-\r
-            if (name)\r
-                khc_write_string(csp_prcache,\r
-                                 L"Name",\r
-                                 wname);\r
-            else if (csp_prcache)\r
-                khc_write_string(csp_prcache,\r
-                                 L"Name",\r
-                                 L"");\r
-\r
-            khc_write_int32(csp_prcache,\r
-                            L"PromptCount",\r
-                            (khm_int32) num_prompts);\r
-\r
-            GetSystemTimeAsFileTime(&current);\r
-#ifdef USE_PROMPT_CACHE_LIFETIME\r
-            khc_read_int32(csp_params, L"PromptCacheLifetime", &t);\r
-            if (t == 0)\r
-                t = 172800;         /* 48 hours */\r
-#else\r
-            khc_read_int32(csp_params, L"MaxRenewLifetime", &t);\r
-            if (t == 0)\r
-                t = 2592000;    /* 30 days */\r
-            t += 604800;        /* + 7 days */\r
-#endif\r
-            TimetToFileTimeInterval(t, &lifetime);\r
-            expiry = FtAdd(&current, &lifetime);\r
-            iexpiry = FtToInt(&expiry);\r
-\r
-            khc_write_int64(csp_prcache, L"ExpiresOn", iexpiry);\r
-        }\r
-    }\r
-\r
-    for(i=0; i < num_prompts; i++) {\r
-        wchar_t wprompt[KHUI_MAXCCH_PROMPT];\r
-\r
-        if(prompts[i].prompt) {\r
-            AnsiStrToUnicode(wprompt, sizeof(wprompt), \r
-                             prompts[i].prompt);\r
-        } else {\r
-            wprompt[0] = 0;\r
-        }\r
-\r
-        khui_cw_add_prompt(\r
-            nc,\r
-            (ptypes?ptypes[i]:0),\r
-            wprompt,\r
-            NULL,\r
-            (prompts[i].hidden?KHUI_NCPROMPT_FLAG_HIDDEN:0));\r
-\r
-        if (csp_prcache) {\r
-            khm_handle csp_p = NULL;\r
-            wchar_t wnum[8];    /* should be enough for 10\r
-                                   million prompts */\r
-\r
-            wnum[0] = 0;\r
-            StringCbPrintf(wnum, sizeof(wnum), L"%d", i);\r
-\r
-            khc_open_space(csp_prcache, wnum, \r
-                           KHM_FLAG_CREATE, &csp_p);\r
-\r
-            if (csp_p) {\r
-                khc_write_string(csp_p, L"Prompt", wprompt);\r
-                khc_write_int32(csp_p, L"Type", (ptypes?ptypes[i]:0));\r
-                khc_write_int32(csp_p, L"Flags",\r
-                                (prompts[i].hidden?\r
-                                 KHUI_NCPROMPT_FLAG_HIDDEN:0));\r
-\r
-                khc_close_space(csp_p);\r
-            }\r
-        }\r
-    }\r
-\r
-    if (csp_prcache) {\r
-        khc_close_space(csp_prcache);\r
-        csp_prcache = NULL;\r
-    }\r
-\r
- _process_prompts:\r
-    /* switch back to main thread if we showed new prompts */\r
-    if (new_prompts)\r
-        SwitchToFiber(k5_main_fiber);\r
-\r
-    /* we get here after the user selects an action that either\r
-       cancles the credentials acquisition operation or triggers the\r
-       actual acquisition of credentials. */\r
-    if(g_fjob.command != FIBER_CMD_CONTINUE &&\r
-       g_fjob.command != FIBER_CMD_KINIT) {\r
-        code = KRB5_LIBOS_PWDINTR;\r
-        goto _exit;\r
-    }\r
-\r
-    g_fjob.null_password = FALSE;\r
-\r
-    /* otherwise, we need to get the data back from the UI and\r
-       return 0 */\r
-    for(i=0; i<num_prompts; i++) {\r
-        krb5_data * d;\r
-        wchar_t wbuf[512];\r
-        khm_size cbbuf;\r
-        size_t cch;\r
-\r
-        d = prompts[i].reply;\r
-\r
-        cbbuf = sizeof(wbuf);\r
-        if(KHM_SUCCEEDED(khui_cw_get_prompt_value(nc, i, wbuf, &cbbuf))) {\r
-            UnicodeStrToAnsi(d->data, d->length, wbuf);\r
-            if(SUCCEEDED(StringCchLengthA(d->data, d->length, &cch)))\r
-                d->length = (unsigned int) cch;\r
-            else\r
-                d->length = 0;\r
-        } else {\r
-#ifdef DEBUG\r
-            assert(FALSE);\r
-#endif\r
-            d->length = 0;\r
-        }\r
-\r
-        if (ptypes && \r
-            ptypes[i] == KRB5_PROMPT_TYPE_PASSWORD &&\r
-            d->length == 0)\r
-\r
-            g_fjob.null_password = TRUE;\r
-    }\r
-\r
- _exit:\r
-\r
-    g_fjob.prompt_set++;\r
-\r
-    /* entering a NULL password is equivalent to cancelling out */\r
-    if (g_fjob.null_password)\r
-        return KRB5_LIBOS_PWDINTR;\r
-    else\r
-        return code;\r
-}\r
-\r
-\r
-void \r
-k5_read_dlg_params(k5_dlg_data * d, khm_handle identity)\r
-{\r
-    k5_params p;\r
-\r
-    khm_krb5_get_identity_params(identity, &p);\r
-\r
-    d->renewable = p.renewable;\r
-    d->forwardable = p.forwardable;\r
-    d->proxiable = p.proxiable;\r
-    d->addressless = p.addressless;\r
-    d->publicIP = p.publicIP;\r
-\r
-    d->tc_lifetime.current = p.lifetime;\r
-    d->tc_lifetime.max = p.lifetime_max;\r
-    d->tc_lifetime.min = p.lifetime_min;\r
-\r
-    d->tc_renew.current = p.renew_life;\r
-    d->tc_renew.max = p.renew_life_max;\r
-    d->tc_renew.min = p.renew_life_min;\r
-\r
-    /* however, if this has externally supplied defaults, we have to\r
-       use them too. */\r
-    if (d->nc && d->nc->ctx.vparam &&\r
-        d->nc->ctx.cb_vparam == sizeof(NETID_DLGINFO)) {\r
-        LPNETID_DLGINFO pdlginfo;\r
-\r
-        pdlginfo = (LPNETID_DLGINFO) d->nc->ctx.vparam;\r
-        if (pdlginfo->size == NETID_DLGINFO_V1_SZ &&\r
-            pdlginfo->in.use_defaults == 0) {\r
-            d->forwardable = pdlginfo->in.forwardable;\r
-            d->addressless = pdlginfo->in.noaddresses;\r
-            d->tc_lifetime.current = pdlginfo->in.lifetime;\r
-            d->tc_renew.current = pdlginfo->in.renew_till;\r
-\r
-            if (pdlginfo->in.renew_till == 0)\r
-                d->renewable = FALSE;\r
-            else\r
-                d->renewable = TRUE;\r
-\r
-            d->proxiable = pdlginfo->in.proxiable;\r
-            d->publicIP = pdlginfo->in.publicip;\r
-        }\r
-    }\r
-\r
-    /* once we read the new data, in, it is no longer considered\r
-       dirty */\r
-    d->dirty = FALSE;\r
-    d->sync = FALSE;\r
-}\r
-\r
-void \r
-k5_write_dlg_params(k5_dlg_data * d, khm_handle identity)\r
-{\r
-\r
-    k5_params p;\r
-\r
-    ZeroMemory(&p, sizeof(p));\r
-\r
-    p.source_reg = K5PARAM_FM_ALL; /* we want to write all the\r
-                                      settings to the registry, if\r
-                                      necessary. */\r
-\r
-    p.renewable = d->renewable;\r
-    p.forwardable = d->forwardable;\r
-    p.proxiable = d->proxiable;\r
-    p.addressless = d->addressless;\r
-    p.publicIP = d->publicIP;\r
-\r
-    p.lifetime = (krb5_deltat) d->tc_lifetime.current;\r
-    p.lifetime_max = (krb5_deltat) d->tc_lifetime.max;\r
-    p.lifetime_min = (krb5_deltat) d->tc_lifetime.min;\r
-\r
-    p.renew_life = (krb5_deltat) d->tc_renew.current;\r
-    p.renew_life_max = (krb5_deltat) d->tc_renew.max;\r
-    p.renew_life_min = (krb5_deltat) d->tc_renew.min;\r
-\r
-    khm_krb5_set_identity_params(identity, &p);\r
-\r
-    /* as in k5_read_dlg_params, once we write the data in, the local\r
-       data is no longer dirty */\r
-    d->dirty = FALSE;\r
-}\r
-\r
-void \r
-k5_free_kinit_job(void)\r
-{\r
-    if (g_fjob.principal)\r
-        PFREE(g_fjob.principal);\r
-\r
-    if (g_fjob.password)\r
-        PFREE(g_fjob.password);\r
-\r
-    if (g_fjob.identity)\r
-        kcdb_identity_release(g_fjob.identity);\r
-\r
-    if (g_fjob.ccache)\r
-        PFREE(g_fjob.ccache);\r
-\r
-    ZeroMemory(&g_fjob, sizeof(g_fjob));\r
-}\r
-\r
-void \r
-k5_prep_kinit_job(khui_new_creds * nc)\r
-{\r
-    khui_new_creds_by_type * nct;\r
-    k5_dlg_data * d;\r
-    wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-    khm_size cbbuf;\r
-    size_t size;\r
-    khm_handle ident;\r
-    LPNETID_DLGINFO pdlginfo;\r
-\r
-    khui_cw_find_type(nc, credtype_id_krb5, &nct);\r
-    if (!nct)\r
-        return;\r
-\r
-    d = (k5_dlg_data *)(LONG_PTR) \r
-        GetWindowLongPtr(nct->hwnd_panel, DWLP_USER);\r
-\r
-    if (!d)\r
-       return;\r
-\r
-    khui_cw_lock_nc(nc);\r
-    ident = nc->identities[0];\r
-    kcdb_identity_hold(ident);\r
-    khui_cw_unlock_nc(nc);\r
-\r
-    cbbuf = sizeof(idname);\r
-    kcdb_identity_get_name(ident, idname, &cbbuf);\r
-    StringCchLength(idname, ARRAYLENGTH(idname), &size);\r
-    size++;\r
-\r
-    k5_free_kinit_job();\r
-\r
-    g_fjob.command = FIBER_CMD_KINIT;\r
-    g_fjob.nc = nc;\r
-    g_fjob.nct = nct;\r
-    g_fjob.dialog = nct->hwnd_panel;\r
-    g_fjob.principal = PMALLOC(size);\r
-    UnicodeStrToAnsi(g_fjob.principal, size, idname);\r
-    g_fjob.password = NULL;\r
-    g_fjob.lifetime = (krb5_deltat) d->tc_lifetime.current;\r
-    g_fjob.forwardable = d->forwardable;\r
-    g_fjob.proxiable = d->proxiable;\r
-    g_fjob.renewable = d->renewable;\r
-    g_fjob.renew_life = (krb5_deltat) d->tc_renew.current;\r
-    g_fjob.addressless = d->addressless;\r
-    g_fjob.publicIP = d->publicIP;\r
-    g_fjob.code = 0;\r
-    g_fjob.identity = ident;\r
-    g_fjob.prompt_set = 0;\r
-    g_fjob.valid_principal = FALSE;\r
-    g_fjob.retry_if_valid_principal = FALSE;\r
-\r
-                                /* the value for\r
-                                   retry_if_valid_principal is not\r
-                                   necessarily the correct value here,\r
-                                   but the correct value will be\r
-                                   assigned k5_kinit_fiber_proc(). */\r
-\r
-    /* if we have external parameters, we should use them as well */\r
-    if (nc->ctx.cb_vparam == sizeof(NETID_DLGINFO) &&\r
-        (pdlginfo = nc->ctx.vparam) &&\r
-        pdlginfo->size == NETID_DLGINFO_V1_SZ) {\r
-        wchar_t * t;\r
-\r
-        if (pdlginfo->in.ccache[0] &&\r
-            SUCCEEDED(StringCchLength(pdlginfo->in.ccache,\r
-                                      NETID_CCACHE_NAME_SZ,\r
-                                      &size))) {\r
-            g_fjob.ccache = PMALLOC(sizeof(char) * (size + 1));\r
-#ifdef DEBUG\r
-            assert(g_fjob.ccache);\r
-#endif\r
-            UnicodeStrToAnsi(g_fjob.ccache, size + 1,\r
-                             pdlginfo->in.ccache);\r
-\r
-            /* this is the same as the output cache */\r
-\r
-            StringCbCopy(pdlginfo->out.ccache, sizeof(pdlginfo->out.ccache),\r
-                         pdlginfo->in.ccache);\r
-        } else {\r
-            g_fjob.ccache = NULL;\r
-\r
-            StringCbCopy(pdlginfo->out.ccache, sizeof(pdlginfo->out.ccache),\r
-                         idname);\r
-\r
-            khm_krb5_canon_cc_name(pdlginfo->out.ccache,\r
-                                   sizeof(pdlginfo->out.ccache));\r
-        }\r
-\r
-        t = khm_get_realm_from_princ(idname);\r
-\r
-        if (t) {\r
-            StringCbCopy(pdlginfo->out.realm,\r
-                         sizeof(pdlginfo->out.realm),\r
-                         t);\r
-\r
-            if ((t - idname) > 1) {\r
-                StringCchCopyN(pdlginfo->out.username,\r
-                               ARRAYLENGTH(pdlginfo->out.username),\r
-                               idname,\r
-                               (t - idname) - 1);\r
-            } else {\r
-                StringCbCopy(pdlginfo->out.username,\r
-                             sizeof(pdlginfo->out.username),\r
-                             L"");\r
-            }\r
-        } else {\r
-            StringCbCopy(pdlginfo->out.username,\r
-                         sizeof(pdlginfo->out.username),\r
-                         idname);\r
-            StringCbCopy(pdlginfo->out.realm,\r
-                         sizeof(pdlginfo->out.realm),\r
-                         L"");\r
-        }\r
-    }\r
-\r
-    /* leave identity held, since we added a reference above */\r
-}\r
-\r
-static khm_int32 KHMAPI \r
-k5_find_tgt_filter(khm_handle cred,\r
-                   khm_int32 flags,\r
-                   void * rock) {\r
-    khm_handle ident = (khm_handle) rock;\r
-    khm_handle cident = NULL;\r
-    khm_int32 f;\r
-    khm_int32 rv;\r
-\r
-    if (KHM_SUCCEEDED(kcdb_cred_get_identity(cred,\r
-                                           &cident)) &&\r
-        cident == ident &&\r
-        KHM_SUCCEEDED(kcdb_cred_get_flags(cred, &f)) &&\r
-        (f & KCDB_CRED_FLAG_INITIAL) &&\r
-        !(f & KCDB_CRED_FLAG_EXPIRED))\r
-        rv = 1;\r
-    else\r
-        rv = 0;\r
-\r
-    if (cident)\r
-        kcdb_identity_release(cident);\r
-\r
-    return rv;\r
-}\r
-\r
-khm_int32\r
-k5_remove_from_LRU(khm_handle identity)\r
-{\r
-    wchar_t * wbuf = NULL;\r
-    wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-    khm_size cb;\r
-    khm_size cb_ms;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    cb = sizeof(idname);\r
-    rv = kcdb_identity_get_name(identity, idname, &cb);\r
-    assert(rv == KHM_ERROR_SUCCESS);\r
-\r
-    rv = khc_read_multi_string(csp_params, L"LRUPrincipals", NULL, &cb_ms);\r
-    if (rv != KHM_ERROR_TOO_LONG)\r
-        cb_ms = sizeof(wchar_t) * 2;\r
-\r
-    wbuf = PMALLOC(cb_ms);\r
-    assert(wbuf);\r
-\r
-    cb = cb_ms;\r
-\r
-    if (rv == KHM_ERROR_TOO_LONG) {\r
-        rv = khc_read_multi_string(csp_params, L"LRUPrincipals", wbuf, &cb);\r
-        assert(KHM_SUCCEEDED(rv));\r
-\r
-        if (multi_string_find(wbuf, idname, KHM_CASE_SENSITIVE) != NULL) {\r
-            multi_string_delete(wbuf, idname, KHM_CASE_SENSITIVE);\r
-        }\r
-    } else {\r
-        multi_string_init(wbuf, cb_ms);\r
-    }\r
-\r
-    rv = khc_write_multi_string(csp_params, L"LRUPrincipals", wbuf);\r
-\r
-    if (wbuf)\r
-        PFREE(wbuf);\r
-\r
-    return rv;\r
-}\r
-\r
-khm_int32\r
-k5_update_LRU(khm_handle identity)\r
-{\r
-    wchar_t * wbuf = NULL;\r
-    wchar_t * idname = NULL;\r
-    wchar_t * realm = NULL;\r
-    khm_size cb;\r
-    khm_size cb_ms;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    rv = kcdb_identity_get_name(identity, NULL, &cb);\r
-    assert(rv == KHM_ERROR_TOO_LONG);\r
-\r
-    idname = PMALLOC(cb);\r
-    assert(idname);\r
-\r
-    rv = kcdb_identity_get_name(identity, idname, &cb);\r
-    assert(KHM_SUCCEEDED(rv));\r
-\r
-    rv = khc_read_multi_string(csp_params, L"LRUPrincipals", NULL, &cb_ms);\r
-    if (rv != KHM_ERROR_TOO_LONG)\r
-        cb_ms = cb + sizeof(wchar_t);\r
-    else\r
-        cb_ms += cb + sizeof(wchar_t);\r
-\r
-    wbuf = PMALLOC(cb_ms);\r
-    assert(wbuf);\r
-\r
-    cb = cb_ms;\r
-\r
-    if (rv == KHM_ERROR_TOO_LONG) {\r
-        rv = khc_read_multi_string(csp_params, L"LRUPrincipals", wbuf, &cb);\r
-        assert(KHM_SUCCEEDED(rv));\r
-\r
-        if (multi_string_find(wbuf, idname, KHM_CASE_SENSITIVE) != NULL) {\r
-            /* it's already there.  We remove it here and add it at\r
-               the top of the LRU list. */\r
-            multi_string_delete(wbuf, idname, KHM_CASE_SENSITIVE);\r
-        }\r
-    } else {\r
-        multi_string_init(wbuf, cb_ms);\r
-    }\r
-\r
-    cb = cb_ms;\r
-    rv = multi_string_prepend(wbuf, &cb, idname);\r
-    assert(KHM_SUCCEEDED(rv));\r
-\r
-    rv = khc_write_multi_string(csp_params, L"LRUPrincipals", wbuf);\r
-\r
-    realm = khm_get_realm_from_princ(idname);\r
-    if (realm == NULL || *realm == L'\0')\r
-        goto _done_with_LRU;\r
-\r
-    cb = cb_ms;\r
-    rv = khc_read_multi_string(csp_params, L"LRURealms", wbuf, &cb);\r
-\r
-    if (rv == KHM_ERROR_TOO_LONG) {\r
-        PFREE(wbuf);\r
-        wbuf = PMALLOC(cb);\r
-        assert(wbuf);\r
-\r
-        cb_ms = cb;\r
-\r
-        rv = khc_read_multi_string(csp_params, L"LRURealms", wbuf, &cb);\r
-\r
-        assert(KHM_SUCCEEDED(rv));\r
-    } else if (rv == KHM_ERROR_SUCCESS) {\r
-        if (multi_string_find(wbuf, realm, KHM_CASE_SENSITIVE) != NULL) {\r
-            /* remove the realm and add it at the top later. */\r
-            multi_string_delete(wbuf, realm, KHM_CASE_SENSITIVE); \r
-        }\r
-    } else {\r
-        multi_string_init(wbuf, cb_ms);\r
-    }\r
-\r
-    cb = cb_ms;\r
-    rv = multi_string_prepend(wbuf, &cb, realm);\r
-\r
-    if (rv == KHM_ERROR_TOO_LONG) {\r
-        wbuf = PREALLOC(wbuf, cb);\r
-\r
-        rv = multi_string_prepend(wbuf, &cb, realm);\r
-\r
-        assert(KHM_SUCCEEDED(rv));\r
-    }\r
-\r
-    rv = khc_write_multi_string(csp_params, L"LRURealms", wbuf);\r
-    \r
-    assert(KHM_SUCCEEDED(rv));\r
-\r
- _done_with_LRU:\r
-\r
-    if (wbuf)\r
-        PFREE(wbuf);\r
-    if (idname)\r
-        PFREE(idname);\r
-\r
-    return rv;\r
-}\r
-\r
-/* Handler for CRED type messages\r
-\r
-    Runs in the context of the Krb5 plugin\r
-*/\r
-khm_int32 KHMAPI \r
-k5_msg_cred_dialog(khm_int32 msg_type, \r
-                   khm_int32 msg_subtype, \r
-                   khm_ui_4 uparam, \r
-                   void * vparam)\r
-{\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    switch(msg_subtype) {\r
-\r
-    case KMSG_CRED_PASSWORD:\r
-    case KMSG_CRED_NEW_CREDS:\r
-        {\r
-            khui_new_creds * nc;\r
-            khui_new_creds_by_type * nct;\r
-            wchar_t wbuf[256];\r
-            size_t cbsize;\r
-\r
-            nc = (khui_new_creds *) vparam;\r
-\r
-            nct = PMALLOC(sizeof(*nct));\r
-            ZeroMemory(nct, sizeof(*nct));\r
-\r
-            nct->type = credtype_id_krb5;\r
-            nct->ordinal = 1;\r
-\r
-            LoadString(hResModule, IDS_KRB5_NC_NAME, \r
-                       wbuf, ARRAYLENGTH(wbuf));\r
-            StringCbLength(wbuf, sizeof(wbuf), &cbsize);\r
-            cbsize += sizeof(wchar_t);\r
-\r
-            nct->name = PMALLOC(cbsize);\r
-            StringCbCopy(nct->name, cbsize, wbuf);\r
-\r
-            nct->h_module = hResModule;\r
-            nct->dlg_proc = k5_nc_dlg_proc;\r
-            if (nc->subtype == KMSG_CRED_PASSWORD)\r
-                nct->dlg_template = MAKEINTRESOURCE(IDD_NC_KRB5_PASSWORD);\r
-            else\r
-                nct->dlg_template = MAKEINTRESOURCE(IDD_NC_KRB5);\r
-\r
-            khui_cw_add_type(nc, nct);\r
-        }\r
-        break;\r
-\r
-    case KMSG_CRED_RENEW_CREDS:\r
-        {\r
-            khui_new_creds * nc;\r
-            khui_new_creds_by_type * nct;\r
-\r
-            nc = (khui_new_creds *) vparam;\r
-\r
-            nct = PMALLOC(sizeof(*nct));\r
-            ZeroMemory(nct, sizeof(*nct));\r
-\r
-            nct->type = credtype_id_krb5;\r
-\r
-            khui_cw_add_type(nc, nct);\r
-        }\r
-        break;\r
-\r
-    case KMSG_CRED_DIALOG_PRESTART:\r
-        {\r
-            khui_new_creds * nc;\r
-            khui_new_creds_by_type * nct;\r
-            k5_dlg_data * d;\r
-            HWND hwnd;\r
-            wchar_t * realms;\r
-            wchar_t * t;\r
-            wchar_t * defrealm;\r
-\r
-            nc = (khui_new_creds *) vparam;\r
-\r
-            khui_cw_find_type(nc, credtype_id_krb5, &nct);\r
-\r
-            if(!nct)\r
-                break;\r
-\r
-            hwnd = nct->hwnd_panel;\r
-            d = (k5_dlg_data *)(LONG_PTR) \r
-                GetWindowLongPtr(nct->hwnd_panel, DWLP_USER);\r
-\r
-            /* this can be NULL if the dialog was closed while the\r
-               plug-in thread was processing. */\r
-            if (d == NULL)\r
-                break;\r
-\r
-            if (!is_k5_identpro) {\r
-\r
-                /* enumerate all realms and place in realms combo box */\r
-                SendDlgItemMessage(hwnd, IDC_NCK5_REALM, \r
-                                   CB_RESETCONTENT, \r
-                                   0, 0);\r
-\r
-                realms = khm_krb5_get_realm_list();\r
-                if(realms) {\r
-                    for (t = realms; t && *t; t = multi_string_next(t)) {\r
-                        SendDlgItemMessage(hwnd, IDC_NCK5_REALM, \r
-                                           CB_ADDSTRING,\r
-                                           0, (LPARAM) t);\r
-                    }\r
-                    PFREE(realms);\r
-                }\r
-\r
-                /* and set the default realm */\r
-                defrealm = khm_krb5_get_default_realm();\r
-                if(defrealm) {\r
-                    SendDlgItemMessage(hwnd, IDC_NCK5_REALM,\r
-                                       CB_SELECTSTRING,\r
-                                       (WPARAM) -1,\r
-                                       (LPARAM) defrealm);\r
-\r
-                    SendDlgItemMessage(hwnd, IDC_NCK5_REALM, \r
-                                       WM_SETTEXT, \r
-                                       0, (LPARAM) defrealm);\r
-                    PFREE(defrealm);\r
-                }\r
-            } else {            /* if krb5 is the identity provider */\r
-                HWND hw_realms;\r
-\r
-                /* in this case, the realm selection is done by the\r
-                   identity provider prompts. */\r
-\r
-                hw_realms = GetDlgItem(hwnd, IDC_NCK5_REALM);\r
-#ifdef DEBUG\r
-                assert(hw_realms);\r
-#endif\r
-                EnableWindow(hw_realms, FALSE);\r
-            }\r
-\r
-            if (nc->subtype == KMSG_CRED_NEW_CREDS) {\r
-                k5_read_dlg_params(d, NULL);\r
-            }\r
-\r
-            PostMessage(hwnd, KHUI_WM_NC_NOTIFY, \r
-                        MAKEWPARAM(0,WMNC_DIALOG_SETUP), 0);\r
-        }\r
-        break;\r
-            \r
-    case KMSG_CRED_DIALOG_NEW_IDENTITY:\r
-        {\r
-            khui_new_creds * nc;\r
-            khui_new_creds_by_type * nct;\r
-            k5_dlg_data * d;\r
-            \r
-            nc = (khui_new_creds *) vparam;\r
-\r
-            khui_cw_find_type(nc, credtype_id_krb5, &nct);\r
-            if (!nct)\r
-                break;\r
-\r
-            d = (k5_dlg_data *)(LONG_PTR) \r
-                GetWindowLongPtr(nct->hwnd_panel, DWLP_USER);\r
-\r
-            if (d == NULL)\r
-                break;\r
-\r
-            /* we only load the identity specific defaults if the user\r
-               hasn't changed the options */\r
-            khui_cw_lock_nc(nc);\r
-\r
-           /* ?: It might be better to not load identity defaults if\r
-              the user has already changed options in the dialog. */\r
-            if(/* !d->dirty && */ nc->n_identities > 0 &&\r
-               nc->subtype == KMSG_CRED_NEW_CREDS) {\r
-\r
-                k5_read_dlg_params(d, nc->identities[0]);\r
-\r
-                PostMessage(nct->hwnd_panel, KHUI_WM_NC_NOTIFY, \r
-                            MAKEWPARAM(0,WMNC_DIALOG_SETUP), 0);\r
-            }\r
-\r
-            khui_cw_unlock_nc(nc);\r
-\r
-            /* reset the force-password-change flag if this is a new\r
-               identity. */\r
-            d->pwd_change = FALSE;\r
-        }\r
-\r
-        /* fallthrough */\r
-    case KMSG_CRED_DIALOG_NEW_OPTIONS:\r
-        {\r
-            khui_new_creds * nc;\r
-            khui_new_creds_by_type * nct;\r
-            k5_dlg_data * d;\r
-\r
-            nc = (khui_new_creds *) vparam;\r
-\r
-            khui_cw_find_type(nc, credtype_id_krb5, &nct);\r
-            if (!nct)\r
-                break;\r
-\r
-            d = (k5_dlg_data *)(LONG_PTR) \r
-                GetWindowLongPtr(nct->hwnd_panel, DWLP_USER);\r
-            if (d == NULL)\r
-                break;\r
-\r
-            if (nc->subtype == KMSG_CRED_PASSWORD) {\r
-                khm_size n_prompts = 0;\r
-\r
-                khui_cw_get_prompt_count(nc, &n_prompts);\r
-\r
-                if (nc->n_identities == 0) {\r
-                    if (n_prompts)\r
-                        khui_cw_clear_prompts(nc);\r
-                } else if (n_prompts != 3) {\r
-                    wchar_t wbuf[KHUI_MAXCCH_BANNER];\r
-\r
-                    khui_cw_clear_prompts(nc);\r
-\r
-                    LoadString(hResModule, IDS_NC_PWD_BANNER,\r
-                               wbuf, ARRAYLENGTH(wbuf));\r
-                    khui_cw_begin_custom_prompts(nc, 3, NULL, wbuf);\r
-\r
-                    LoadString(hResModule, IDS_NC_PWD_PWD,\r
-                               wbuf, ARRAYLENGTH(wbuf));\r
-                    khui_cw_add_prompt(nc, KHUI_NCPROMPT_TYPE_PASSWORD, \r
-                                       wbuf, NULL, KHUI_NCPROMPT_FLAG_HIDDEN);\r
-\r
-                    LoadString(hResModule, IDS_NC_PWD_NPWD,\r
-                               wbuf, ARRAYLENGTH(wbuf));\r
-                    khui_cw_add_prompt(nc, KHUI_NCPROMPT_TYPE_NEW_PASSWORD,\r
-                                       wbuf, NULL, KHUI_NCPROMPT_FLAG_HIDDEN);\r
-\r
-                    LoadString(hResModule, IDS_NC_PWD_NPWD_AGAIN,\r
-                               wbuf, ARRAYLENGTH(wbuf));\r
-                    khui_cw_add_prompt(nc, KHUI_NCPROMPT_TYPE_NEW_PASSWORD_AGAIN,\r
-                                       wbuf, NULL, KHUI_NCPROMPT_FLAG_HIDDEN);\r
-                }\r
-\r
-                return KHM_ERROR_SUCCESS;\r
-            }\r
-            /* else; nc->subtype == KMSG_CRED_NEW_CREDS */\r
-\r
-            assert(nc->subtype == KMSG_CRED_NEW_CREDS);\r
-\r
-            /* If we are forcing a password change, then we don't do\r
-               anything here.  Note that if the identity changed, then\r
-               this field would have been reset, so we would proceed\r
-               as usual. */\r
-            if (d->pwd_change)\r
-                return KHM_ERROR_SUCCESS;\r
-\r
-#if 0\r
-            /* Clearing the prompts at this point is a bad idea since\r
-               the prompter depends on the prompts to know if this set\r
-               of prompts is the same as the new set and if so, use\r
-               the values entered in the old prompts as responses to\r
-               the new one. */\r
-            khui_cw_clear_prompts(nc);\r
-#endif\r
-\r
-            /* if the fiber is already in a kinit, cancel it */\r
-            if(g_fjob.state == FIBER_STATE_KINIT) {\r
-                g_fjob.command = FIBER_CMD_CANCEL;\r
-                SwitchToFiber(k5_kinit_fiber);\r
-                /* we get here when the cancel operation completes */\r
-                k5_free_kinit_job();\r
-            }\r
-\r
-            khui_cw_lock_nc(nc);\r
-\r
-            if(nc->n_identities > 0) {\r
-                khm_handle ident = nc->identities[0];\r
-\r
-                kcdb_identity_hold(ident);\r
-\r
-                k5_prep_kinit_job(nc);\r
-\r
-                /* after the switch to the fiber, the dialog will be\r
-                   back in sync with the kinit thread. */\r
-                d->sync = TRUE;\r
-\r
-                khui_cw_unlock_nc(nc);\r
-\r
-                SwitchToFiber(k5_kinit_fiber);\r
-                /* we get here when the fiber switches back */\r
-                if(g_fjob.state == FIBER_STATE_NONE) {\r
-                    wchar_t msg[KHUI_MAXCCH_BANNER];\r
-                    khm_size cb;\r
-\r
-                    /* Special case.  If the users' password has\r
-                       expired, we force a password change dialog on\r
-                       top of the new credentials dialog using a set\r
-                       of custom prompts, but only if we are the\r
-                       identity provider. */\r
-                    if (g_fjob.code == KRB5KDC_ERR_KEY_EXP &&\r
-                        is_k5_identpro) {\r
-\r
-                        k5_force_password_change(d);\r
-                        goto done_with_bad_princ;\r
-\r
-                    }\r
-\r
-                    /* we can't possibly have succeeded without a\r
-                       password */\r
-                    if(g_fjob.code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN && \r
-                      is_k5_identpro) {\r
-                        kcdb_identity_set_flags(ident,\r
-                                                KCDB_IDENT_FLAG_INVALID,\r
-                                                KCDB_IDENT_FLAG_INVALID);\r
-\r
-                        khui_cw_clear_prompts(nc);\r
-                    }\r
-\r
-                    if (d->cred_message) {\r
-                        PFREE(d->cred_message);\r
-                        d->cred_message = NULL;\r
-                    }\r
-\r
-                    msg[0] = L'\0';\r
-\r
-                    switch(g_fjob.code) {\r
-                    case KRB5KDC_ERR_NAME_EXP:\r
-                        /* principal expired */\r
-                        LoadString(hResModule, IDS_K5ERR_NAME_EXPIRED,\r
-                                   msg, ARRAYLENGTH(msg));\r
-                        break;\r
-\r
-                    case KRB5KDC_ERR_KEY_EXP:\r
-                        {\r
-                            /* password needs changing. */\r
-                            LoadString(hResModule, IDS_K5ERR_KEY_EXPIRED,\r
-                                       msg, ARRAYLENGTH(msg));\r
-                        }\r
-                        break;\r
-\r
-                    default:\r
-                        {\r
-                            DWORD dw_dummy;\r
-                            kherr_suggestion sug_dummy;\r
-                            wchar_t fmt[KHUI_MAXCCH_BANNER];\r
-                            wchar_t desc[KHUI_MAXCCH_BANNER];\r
-\r
-                            LoadString(hResModule, IDS_K5ERR_FMT,\r
-                                       fmt, ARRAYLENGTH(fmt));\r
-\r
-                            khm_err_describe(g_fjob.code,\r
-                                             desc,\r
-                                             sizeof(desc),\r
-                                             &dw_dummy,\r
-                                             &sug_dummy);\r
-\r
-                            StringCbPrintf(msg, sizeof(msg), fmt, desc);\r
-                        }\r
-                    }\r
-\r
-                    if (msg[0]) {\r
-                        StringCbLength(msg, sizeof(msg), &cb);\r
-                        cb += sizeof(wchar_t);\r
-\r
-                        d->cred_message = PMALLOC(cb);\r
-                        StringCbCopy(d->cred_message, cb, msg);\r
-                    }\r
-\r
-                done_with_bad_princ:\r
-\r
-                    k5_free_kinit_job();\r
-\r
-                    if (is_k5_identpro)\r
-                        kcdb_identity_set_flags(ident,\r
-                                                KCDB_IDENT_FLAG_UNKNOWN,\r
-                                                KCDB_IDENT_FLAG_UNKNOWN);\r
-\r
-\r
-                } else if(g_fjob.state == FIBER_STATE_KINIT) {\r
-                    /* this is what we want.  Leave the fiber there. */\r
-\r
-                    if(is_k5_identpro)\r
-                        kcdb_identity_set_flags(ident, \r
-                                                KCDB_IDENT_FLAG_VALID,\r
-                                                KCDB_IDENT_FLAG_VALID);\r
-                } else {\r
-                    /* huh?? */\r
-#ifdef DEBUG\r
-                    assert(FALSE);\r
-#endif\r
-                }\r
-\r
-                /* since the attributes of the identity have changed,\r
-                   we should update the cred text as well */\r
-                kcdb_identity_release(ident);\r
-                khui_cw_lock_nc(nc);\r
-                PostMessage(nc->hwnd, KHUI_WM_NC_NOTIFY, \r
-                            MAKEWPARAM(0, WMNC_UPDATE_CREDTEXT), 0);\r
-            } else {\r
-                khui_cw_unlock_nc(nc);\r
-                khui_cw_clear_prompts(nc);\r
-                khui_cw_lock_nc(nc);\r
-            }\r
-\r
-            khui_cw_unlock_nc(nc);\r
-        }\r
-        break;\r
-\r
-    case KMSG_CRED_PROCESS:\r
-        {\r
-            khui_new_creds * nc;\r
-            khui_new_creds_by_type * nct;\r
-            k5_dlg_data * d;\r
-\r
-            khm_int32 r = 0;\r
-\r
-            nc = (khui_new_creds *) vparam;\r
-\r
-            khui_cw_find_type(nc, credtype_id_krb5, &nct);\r
-\r
-            if(!nct)\r
-                break;\r
-\r
-            /* reset the null_password flag, just in case */\r
-            g_fjob.null_password = FALSE;\r
-\r
-            if (nc->subtype == KMSG_CRED_NEW_CREDS) {\r
-                d = (k5_dlg_data *) nct->aux;\r
-                if (d == NULL)\r
-                    break;\r
-\r
-                if (d->pwd_change) {\r
-                    /* we are forcing a password change */\r
-                    goto change_password;\r
-                }\r
-\r
-                _begin_task(0);\r
-                _report_mr0(KHERR_NONE, MSG_CTX_INITAL_CREDS);\r
-                _describe();\r
-\r
-                if (g_fjob.state == FIBER_STATE_KINIT) {\r
-                    if(nc->result == KHUI_NC_RESULT_CANCEL) {\r
-                        g_fjob.command = FIBER_CMD_CANCEL;\r
-                        SwitchToFiber(k5_kinit_fiber);\r
-\r
-                        /* if we cancelled out, then we shouldn't care\r
-                           about the return code. */\r
-#ifdef DEBUG\r
-                        assert(g_fjob.state == FIBER_STATE_NONE);\r
-#endif\r
-                        g_fjob.code = 0;\r
-\r
-                       _reportf(L"Cancelling");\r
-                    } else if (nc->result == KHUI_NC_RESULT_PROCESS) {\r
-                        khui_cw_sync_prompt_values(nc);\r
-                        g_fjob.command = FIBER_CMD_CONTINUE;\r
-                        SwitchToFiber(k5_kinit_fiber);\r
-\r
-                        /* We get back here once the fiber finishes\r
-                           processing */\r
-                    }\r
-#ifdef DEBUG\r
-                    else {\r
-                        assert(FALSE);\r
-                    }\r
-#endif\r
-                } else {\r
-                    /* we weren't in a KINIT state */\r
-                    if (nc->result == KHUI_NC_RESULT_CANCEL) {\r
-                        /* nothing to report */\r
-                        g_fjob.code = 0;\r
-                    } else if (nc->result == KHUI_NC_RESULT_PROCESS) {\r
-                        /* g_fjob.code should have the result of the\r
-                           last kinit attempt.  We should leave it\r
-                           as-is */\r
-                    }\r
-#ifdef DEBUG\r
-                    else {\r
-                        /* unknown result */\r
-                        assert(FALSE);\r
-                    }\r
-#endif\r
-                }\r
-\r
-                /* special case: if there was no password entered, and\r
-                   if there is a valid TGT we allow the credential\r
-                   acquisition to go through */\r
-                if (g_fjob.state == FIBER_STATE_NONE &&\r
-                    g_fjob.code &&\r
-                    g_fjob.null_password &&\r
-\r
-                    (nc->n_identities == 0 ||\r
-                     nc->identities[0] == NULL ||\r
-                     KHM_SUCCEEDED(kcdb_credset_find_filtered\r
-                                   (NULL,\r
-                                    -1,\r
-                                    k5_find_tgt_filter,\r
-                                    nc->identities[0],\r
-                                    NULL,\r
-                                    NULL)))) {\r
-                   _reportf(L"No password entered, but a valid TGT exists. Continuing");\r
-                    g_fjob.code = 0;\r
-               } else if (g_fjob.state == FIBER_STATE_NONE &&\r
-                           g_fjob.code == 0 &&\r
-                           nc->n_identities > 0 &&\r
-                           nc->identities[0] != NULL) {\r
-\r
-                    /* we had a password and we used it to get\r
-                       tickets.  We should reset the IMPORTED flag now\r
-                       since the tickets are not imported. */\r
-\r
-                    khm_krb5_set_identity_flags(nc->identities[0],\r
-                                                K5IDFLAG_IMPORTED,\r
-                                                0);\r
-                }\r
-\r
-                if(g_fjob.code != 0) {\r
-                    wchar_t tbuf[1024];\r
-                    DWORD suggestion;\r
-                    kherr_suggestion suggest_code;\r
-\r
-                    khm_err_describe(g_fjob.code, tbuf, sizeof(tbuf),\r
-                                     &suggestion, &suggest_code);\r
-\r
-                    _report_cs0(KHERR_ERROR, tbuf);\r
-                    if (suggestion != 0)\r
-                        _suggest_mr(suggestion, suggest_code);\r
-\r
-                    _resolve();\r
-\r
-                    r = KHUI_NC_RESPONSE_FAILED;\r
-\r
-                    if (suggest_code == KHERR_SUGGEST_RETRY) {\r
-                        r |= KHUI_NC_RESPONSE_NOEXIT |\r
-                            KHUI_NC_RESPONSE_PENDING;\r
-                    }\r
-\r
-#ifdef DEBUG\r
-                    assert(g_fjob.state == FIBER_STATE_NONE);\r
-#endif\r
-\r
-                    if (g_fjob.valid_principal &&\r
-                        nc->n_identities > 0 &&\r
-                        nc->identities[0]) {\r
-                        /* the principal was valid, so we can go ahead\r
-                           and update the LRU */\r
-                        k5_update_LRU(nc->identities[0]);\r
-                    }\r
-\r
-                } else if (nc->result == KHUI_NC_RESULT_PROCESS &&\r
-                           g_fjob.state == FIBER_STATE_NONE) {\r
-                    krb5_context ctx = NULL;\r
-\r
-                   _reportf(L"Tickets successfully acquired");\r
-\r
-                    r = KHUI_NC_RESPONSE_SUCCESS |\r
-                        KHUI_NC_RESPONSE_EXIT;\r
-\r
-                    /* if we successfully obtained credentials, we\r
-                       should save the current settings in the\r
-                       identity config space */\r
-\r
-                    assert(nc->n_identities > 0);\r
-                    assert(nc->identities[0]);\r
-\r
-                    k5_write_dlg_params(d, nc->identities[0]);\r
-\r
-                    /* We should also quickly refresh the credentials\r
-                       so that the identity flags and ccache\r
-                       properties reflect the current state of\r
-                       affairs.  This has to be done here so that\r
-                       other credentials providers which depend on\r
-                       Krb5 can properly find the initial creds to\r
-                       obtain their respective creds. */\r
-\r
-                    khm_krb5_list_tickets(&ctx);\r
-\r
-                    if (nc->set_default) {\r
-                       _reportf(L"Setting default identity");\r
-                        kcdb_identity_set_default(nc->identities[0]);\r
-                    }\r
-\r
-                    /* If there is no default identity, then make this the default */\r
-                    kcdb_identity_refresh(nc->identities[0]);\r
-                    {\r
-                        khm_handle tdefault = NULL;\r
-\r
-                        if (KHM_SUCCEEDED(kcdb_identity_get_default(&tdefault))) {\r
-                            kcdb_identity_release(tdefault);\r
-                        } else {\r
-                           _reportf(L"There was no default identity.  Setting default");\r
-                            kcdb_identity_set_default(nc->identities[0]);\r
-                        }\r
-                    }\r
-\r
-                    /* and update the LRU */\r
-                    k5_update_LRU(nc->identities[0]);\r
-\r
-                    if (ctx != NULL)\r
-                        pkrb5_free_context(ctx);\r
-                } else if (g_fjob.state == FIBER_STATE_NONE) {\r
-                    /* the user cancelled the operation */\r
-                    r = KHUI_NC_RESPONSE_EXIT | \r
-                        KHUI_NC_RESPONSE_SUCCESS;\r
-                }\r
-\r
-                if(g_fjob.state == FIBER_STATE_NONE) {\r
-                    khui_cw_set_response(nc, credtype_id_krb5, r);\r
-\r
-                    if (r & KHUI_NC_RESPONSE_NOEXIT) {\r
-                        /* if we are retrying the call, we should\r
-                           restart the kinit fiber */\r
-#ifdef DEBUG\r
-                        assert(r & KHUI_NC_RESPONSE_PENDING);\r
-#endif\r
-\r
-                        k5_prep_kinit_job(nc);\r
-                        SwitchToFiber(k5_kinit_fiber);\r
-                    } else {\r
-                        /* free up the fiber data fields. */\r
-                        k5_free_kinit_job();\r
-                    }\r
-                } else {\r
-                    khui_cw_set_response(nc, credtype_id_krb5,\r
-                                         KHUI_NC_RESPONSE_NOEXIT | \r
-                                         KHUI_NC_RESPONSE_PENDING | r);\r
-                }\r
-\r
-                _end_task();\r
-            } else if (nc->subtype == KMSG_CRED_RENEW_CREDS) {\r
-\r
-                FILETIME ftidexp = {0,0};\r
-                FILETIME ftcurrent;\r
-                khm_size cb;\r
-\r
-                GetSystemTimeAsFileTime(&ftcurrent);\r
-\r
-                _begin_task(0);\r
-                _report_mr0(KHERR_NONE, MSG_CTX_RENEW_CREDS);\r
-                _describe();\r
-\r
-                if (nc->ctx.scope == KHUI_SCOPE_IDENT ||\r
-                    (nc->ctx.scope == KHUI_SCOPE_CREDTYPE &&\r
-                     nc->ctx.cred_type == credtype_id_krb5) ||\r
-                   (nc->ctx.scope == KHUI_SCOPE_CRED &&\r
-                    nc->ctx.cred_type == credtype_id_krb5)) {\r
-                    int code;\r
-\r
-                   if (nc->ctx.scope == KHUI_SCOPE_CRED &&\r
-                       nc->ctx.cred != NULL) {\r
-\r
-                       /* get the expiration time for the identity first. */\r
-                       cb = sizeof(ftidexp);\r
-#ifdef DEBUG\r
-                       assert(nc->ctx.identity != NULL);\r
-#endif\r
-                       kcdb_identity_get_attr(nc->ctx.identity,\r
-                                              KCDB_ATTR_EXPIRE,\r
-                                              NULL,\r
-                                              &ftidexp,\r
-                                              &cb);\r
-\r
-                       code = khm_krb5_renew_cred(nc->ctx.cred);\r
-\r
-                    } else if (nc->ctx.scope == KHUI_SCOPE_IDENT &&\r
-                              nc->ctx.identity != 0) {\r
-                        /* get the current identity expiration time */\r
-                        cb = sizeof(ftidexp);\r
-\r
-                        kcdb_identity_get_attr(nc->ctx.identity,\r
-                                               KCDB_ATTR_EXPIRE,\r
-                                               NULL,\r
-                                               &ftidexp,\r
-                                               &cb);\r
-\r
-                        code = khm_krb5_renew_ident(nc->ctx.identity);\r
-                    } else {\r
-\r
-                       _reportf(L"No identity specified.  Can't renew Kerberos tickets");\r
-\r
-                        code = 1; /* it just has to be non-zero */\r
-                    }\r
-\r
-                    if (code == 0) {\r
-                       _reportf(L"Tickets successfully renewed");\r
-\r
-                        khui_cw_set_response(nc, credtype_id_krb5, \r
-                                             KHUI_NC_RESPONSE_EXIT | \r
-                                             KHUI_NC_RESPONSE_SUCCESS);\r
-                    } else if (nc->ctx.identity == 0) {\r
-\r
-                        _report_mr0(KHERR_ERROR, MSG_ERR_NO_IDENTITY);\r
-\r
-                        khui_cw_set_response(nc, credtype_id_krb5, \r
-                                             KHUI_NC_RESPONSE_EXIT | \r
-                                             KHUI_NC_RESPONSE_FAILED);\r
-                    } else if (CompareFileTime(&ftcurrent, &ftidexp) < 0) {\r
-                        wchar_t tbuf[1024];\r
-                        DWORD suggestion;\r
-                        kherr_suggestion sug_id;\r
-\r
-                        /* if we failed to get new tickets, but the\r
-                           identity is still valid, then we assume that\r
-                           the current tickets are still good enough\r
-                           for other credential types to obtain their\r
-                           credentials. */\r
-\r
-                        khm_err_describe(code, tbuf, sizeof(tbuf),\r
-                                         &suggestion, &sug_id);\r
-\r
-                        _report_cs0(KHERR_WARNING, tbuf);\r
-                        if (suggestion)\r
-                            _suggest_mr(suggestion, sug_id);\r
-\r
-                        _resolve();\r
-\r
-                        khui_cw_set_response(nc, credtype_id_krb5, \r
-                                             KHUI_NC_RESPONSE_EXIT |\r
-                                             KHUI_NC_RESPONSE_SUCCESS);\r
-                    } else {\r
-                        wchar_t tbuf[1024];\r
-                        DWORD suggestion;\r
-                        kherr_suggestion sug_id;\r
-\r
-                        khm_err_describe(code, tbuf, sizeof(tbuf),\r
-                                         &suggestion, &sug_id);\r
-\r
-                        _report_cs0(KHERR_ERROR, tbuf);\r
-                        if (suggestion)\r
-                            _suggest_mr(suggestion, sug_id);\r
-\r
-                        _resolve();\r
-\r
-                        khui_cw_set_response(nc, credtype_id_krb5, \r
-                                             ((sug_id == KHERR_SUGGEST_RETRY)?KHUI_NC_RESPONSE_NOEXIT:KHUI_NC_RESPONSE_EXIT) |\r
-                                             KHUI_NC_RESPONSE_FAILED);\r
-                    }\r
-                } else {\r
-                    khui_cw_set_response(nc, credtype_id_krb5, \r
-                                         KHUI_NC_RESPONSE_EXIT | \r
-                                         KHUI_NC_RESPONSE_SUCCESS);\r
-                }\r
-\r
-                _end_task();\r
-            } else if (nc->subtype == KMSG_CRED_PASSWORD &&\r
-                       nc->result == KHUI_NC_RESULT_PROCESS) {\r
-\r
-            change_password:\r
-                /* we jump here if there was a password change forced */\r
-\r
-                _begin_task(0);\r
-                _report_mr0(KHERR_NONE, MSG_CTX_PASSWD);\r
-                _describe();\r
-\r
-                khui_cw_lock_nc(nc);\r
-\r
-                if (nc->result == KHUI_NC_RESULT_CANCEL) {\r
-\r
-                    khui_cw_set_response(nc, credtype_id_krb5,\r
-                                         KHUI_NC_RESPONSE_SUCCESS |\r
-                                         KHUI_NC_RESPONSE_EXIT);\r
-\r
-                } else if (nc->n_identities == 0 ||\r
-                    nc->identities[0] == NULL) {\r
-                    _report_mr0(KHERR_ERROR, MSG_PWD_NO_IDENTITY);\r
-                    _suggest_mr(MSG_PWD_S_NO_IDENTITY, KHERR_SUGGEST_RETRY);\r
-\r
-                    khui_cw_set_response(nc, credtype_id_krb5,\r
-                                         KHUI_NC_RESPONSE_FAILED |\r
-                                         KHUI_NC_RESPONSE_NOEXIT);\r
-\r
-                } else {\r
-                    wchar_t   widname[KCDB_IDENT_MAXCCH_NAME];\r
-                    char      idname[KCDB_IDENT_MAXCCH_NAME];\r
-                    wchar_t   wpwd[KHUI_MAXCCH_PASSWORD];\r
-                    char      pwd[KHUI_MAXCCH_PASSWORD];\r
-                    wchar_t   wnpwd[KHUI_MAXCCH_PASSWORD];\r
-                    char      npwd[KHUI_MAXCCH_PASSWORD];\r
-                    wchar_t   wnpwd2[KHUI_MAXCCH_PASSWORD];\r
-                    wchar_t * wresult;\r
-                    char    * result;\r
-                    khm_size n_prompts = 0;\r
-                    khm_size cb;\r
-                    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-                    long code = 0;\r
-                    khm_handle ident;\r
-\r
-                    khui_cw_get_prompt_count(nc, &n_prompts);\r
-                    assert(n_prompts == 3);\r
-\r
-                    ident = nc->identities[0];\r
-                    cb = sizeof(widname);\r
-                    rv = kcdb_identity_get_name(ident, widname, &cb);\r
-                    if (KHM_FAILED(rv)) {\r
-#ifdef DEBUG\r
-                        assert(FALSE);\r
-#endif\r
-                        _report_mr0(KHERR_ERROR, MSG_PWD_UNKNOWN);\r
-                        goto _pwd_exit;\r
-                    }\r
-\r
-                    cb = sizeof(wpwd);\r
-                    rv = khui_cw_get_prompt_value(nc, 0, wpwd, &cb);\r
-                    if (KHM_FAILED(rv)) {\r
-#ifdef DEBUG\r
-                        assert(FALSE);\r
-#endif\r
-                        _report_mr0(KHERR_ERROR, MSG_PWD_UNKNOWN);\r
-                        goto _pwd_exit;\r
-                    }\r
-\r
-                    cb = sizeof(wnpwd);\r
-                    rv = khui_cw_get_prompt_value(nc, 1, wnpwd, &cb);\r
-                    if (KHM_FAILED(rv)) {\r
-#ifdef DEBUG\r
-                        assert(FALSE);\r
-#endif\r
-                        _report_mr0(KHERR_ERROR, MSG_PWD_UNKNOWN);\r
-                        goto _pwd_exit;\r
-                    }\r
-\r
-                    cb = sizeof(wnpwd2);\r
-                    rv = khui_cw_get_prompt_value(nc, 2, wnpwd2, &cb);\r
-                    if (KHM_FAILED(rv)) {\r
-#ifdef DEBUG\r
-                        assert(FALSE);\r
-#endif\r
-                        _report_mr0(KHERR_ERROR, MSG_PWD_UNKNOWN);\r
-                        goto _pwd_exit;\r
-                    }\r
-\r
-                    if (wcscmp(wnpwd, wnpwd2)) {\r
-                        rv = KHM_ERROR_INVALID_PARAM;\r
-                        _report_mr0(KHERR_ERROR, MSG_PWD_NOT_SAME);\r
-                        _suggest_mr(MSG_PWD_S_NOT_SAME, KHERR_SUGGEST_INTERACT);\r
-                        goto _pwd_exit;\r
-                    }\r
-\r
-                    if (!wcscmp(wpwd, wnpwd)) {\r
-                        rv = KHM_ERROR_INVALID_PARAM;\r
-                        _report_mr0(KHERR_ERROR, MSG_PWD_SAME);\r
-                        _suggest_mr(MSG_PWD_S_SAME, KHERR_SUGGEST_INTERACT);\r
-                        goto _pwd_exit;\r
-                    }\r
-\r
-                    UnicodeStrToAnsi(idname, sizeof(idname), widname);\r
-                    UnicodeStrToAnsi(pwd, sizeof(pwd), wpwd);\r
-                    UnicodeStrToAnsi(npwd, sizeof(npwd), wnpwd);\r
-\r
-                    result = NULL;\r
-\r
-                    code = khm_krb5_changepwd(idname,\r
-                                              pwd,\r
-                                              npwd,\r
-                                              &result);\r
-\r
-                    if (code)\r
-                        rv = KHM_ERROR_UNKNOWN;\r
-                    else {\r
-                        khm_handle csp_idcfg = NULL;\r
-                        krb5_context ctx = NULL;\r
-\r
-                        /* we set a new password.  now we need to get\r
-                           initial credentials. */\r
-\r
-                        d = (k5_dlg_data *) nct->aux;\r
-\r
-                        if (d == NULL) {\r
-                            rv = KHM_ERROR_UNKNOWN;\r
-                            goto _pwd_exit;\r
-                        }\r
-\r
-                        if (nc->subtype == KMSG_CRED_PASSWORD) {\r
-                            /* since this was just a password change,\r
-                               we need to load new credentials options\r
-                               from the configuration store. */\r
-\r
-                            k5_read_dlg_params(d, nc->identities[0]);\r
-                        }\r
-\r
-                        /* the password change phase is now done */\r
-                        d->pwd_change = FALSE;\r
-\r
-#ifdef DEBUG\r
-                        _reportf(L"Calling khm_krb5_kinit()");\r
-#endif\r
-                        code = khm_krb5_kinit(NULL, /* context (create one) */\r
-                                              idname, /* principal_name */\r
-                                              npwd, /* new password */\r
-                                              NULL, /* ccache name (figure out the identity cc)*/\r
-                                              (krb5_deltat) d->tc_lifetime.current,\r
-                                              d->forwardable,\r
-                                              d->proxiable,\r
-                                              (krb5_deltat)((d->renewable)?d->tc_renew.current:0),\r
-                                              d->addressless, /* addressless */\r
-                                              d->publicIP, /* public IP */\r
-                                              NULL, /* prompter */\r
-                                              NULL /* prompter data */);\r
-\r
-                        if (code) {\r
-                            rv = KHM_ERROR_UNKNOWN;\r
-                            goto _pwd_exit;\r
-                        }\r
-\r
-                        /* save the settings that we used for\r
-                           obtaining the ticket. */\r
-                        if (nc->subtype == KMSG_CRED_NEW_CREDS) {\r
-\r
-                            k5_write_dlg_params(d, nc->identities[0]);\r
-\r
-                            /* and then update the LRU too */\r
-                            k5_update_LRU(nc->identities[0]);\r
-                        }\r
-\r
-                        /* and do a quick refresh of the krb5 tickets\r
-                           so that other plug-ins that depend on krb5\r
-                           can look up tickets inside NetIDMgr */\r
-                        khm_krb5_list_tickets(&ctx);\r
-\r
-                        /* if there was no default identity, we make\r
-                           this one the default. */\r
-                        kcdb_identity_refresh(nc->identities[0]);\r
-                        {\r
-                            khm_handle tdefault = NULL;\r
-\r
-                            if (KHM_SUCCEEDED(kcdb_identity_get_default(&tdefault))) {\r
-                                kcdb_identity_release(tdefault);\r
-                            } else {\r
-                                _reportf(L"There was no default identity.  Setting defualt");\r
-                                kcdb_identity_set_default(nc->identities[0]);\r
-                            }\r
-                        }\r
-\r
-                        if (ctx != NULL)\r
-                            pkrb5_free_context(ctx);\r
-\r
-                        if (nc->subtype == KMSG_CRED_PASSWORD) {\r
-                            /* if we obtained new credentials as a\r
-                               result of successfully changing the\r
-                               password, we also schedule an identity\r
-                               renewal for this identity.  This allows\r
-                               the other credential types to obtain\r
-                               credentials for this identity. */\r
-                            khui_action_context ctx;\r
-\r
-                            _reportf(L"Scheduling renewal of [%s] after password change",\r
-                                     widname);\r
-\r
-                            khui_context_create(&ctx,\r
-                                                KHUI_SCOPE_IDENT,\r
-                                                nc->identities[0],\r
-                                                KCDB_CREDTYPE_INVALID,\r
-                                                NULL);\r
-                            khui_action_trigger(KHUI_ACTION_RENEW_CRED,\r
-                                                &ctx);\r
-\r
-                            khui_context_release(&ctx);\r
-                        }\r
-                    }\r
-\r
-                    /* result is only set when code != 0 */\r
-                    if (code && result) {\r
-                        size_t len;\r
-\r
-                        StringCchLengthA(result, KHERR_MAXCCH_STRING,\r
-                                         &len);\r
-                        wresult = PMALLOC((len + 1) * sizeof(wchar_t));\r
-#ifdef DEBUG\r
-                        assert(wresult);\r
-#endif\r
-                        AnsiStrToUnicode(wresult, (len + 1) * sizeof(wchar_t),\r
-                                         result);\r
-\r
-                        _report_cs1(KHERR_ERROR, L"%1!s!", _cstr(wresult));\r
-                        _resolve();\r
-\r
-                        PFREE(result);\r
-                        PFREE(wresult);\r
-\r
-                        /* we don't need to report anything more */\r
-                        code = 0;\r
-                    }\r
-\r
-                _pwd_exit:\r
-                    if (KHM_FAILED(rv)) {\r
-                        if (code) {\r
-                            wchar_t tbuf[1024];\r
-                            DWORD suggestion;\r
-                            kherr_suggestion sug_id;\r
-\r
-                            khm_err_describe(code, tbuf, sizeof(tbuf),\r
-                                             &suggestion, &sug_id);\r
-                            _report_cs0(KHERR_ERROR, tbuf);\r
-\r
-                            if (suggestion)\r
-                                _suggest_mr(suggestion, sug_id);\r
-\r
-                            _resolve();\r
-                        }\r
-\r
-                        khui_cw_set_response(nc, credtype_id_krb5, \r
-                                             KHUI_NC_RESPONSE_NOEXIT|\r
-                                             KHUI_NC_RESPONSE_FAILED);\r
-                    } else {\r
-                        khui_cw_set_response(nc, credtype_id_krb5,\r
-                                             KHUI_NC_RESPONSE_SUCCESS |\r
-                                             KHUI_NC_RESPONSE_EXIT);\r
-                    }\r
-                }\r
-\r
-                khui_cw_unlock_nc(nc);\r
-\r
-                _end_task();\r
-            } /* KMSG_CRED_PASSWORD */\r
-        }\r
-        break;\r
-\r
-    case KMSG_CRED_END:\r
-        {\r
-            khui_new_creds * nc;\r
-            khui_new_creds_by_type * nct;\r
-\r
-            nc = (khui_new_creds *) vparam;\r
-            khui_cw_find_type(nc, credtype_id_krb5, &nct);\r
-\r
-            if(!nct)\r
-                break;\r
-\r
-            khui_cw_del_type(nc, credtype_id_krb5);\r
-    \r
-            if (nct->name)\r
-                PFREE(nct->name);\r
-            if (nct->credtext)\r
-                PFREE(nct->credtext);\r
-\r
-            PFREE(nct);\r
-\r
-            k5_free_kinit_job();\r
-        }\r
-        break;\r
-\r
-    case KMSG_CRED_IMPORT:\r
-        {\r
-            khm_int32 t = 0;\r
-\r
-#ifdef DEBUG\r
-            assert(csp_params);\r
-#endif\r
-            khc_read_int32(csp_params, L"MsLsaImport", &t);\r
-\r
-            if (t != K5_LSAIMPORT_NEVER) {\r
-                krb5_context ctx = NULL;\r
-                khm_handle id_default = NULL;\r
-                khm_handle id_imported = NULL;\r
-                BOOL imported;\r
-\r
-                imported = khm_krb5_ms2mit(NULL, (t == K5_LSAIMPORT_MATCH), TRUE,\r
-                                           &id_imported);\r
-                if (imported) {\r
-                    khm_krb5_list_tickets(&ctx);\r
-\r
-                    if (ctx)\r
-                        pkrb5_free_context(ctx);\r
-\r
-                    kcdb_identity_refresh(id_imported);\r
-\r
-                    if (KHM_SUCCEEDED(kcdb_identity_get_default(&id_default))) {\r
-                        kcdb_identity_release(id_default);\r
-                        id_default = NULL;\r
-                    } else {\r
-                        _reportf(L"There was no default identity.  Setting default");\r
-                        kcdb_identity_set_default(id_imported);\r
-                    }\r
-\r
-                    /* and update the LRU */\r
-                    k5_update_LRU(id_imported);\r
-                }\r
-\r
-                if (id_imported)\r
-                    kcdb_identity_release(id_imported);\r
-            }\r
-        }\r
-        break;\r
-    }\r
-\r
-    return rv;\r
-}\r
+/*
+ * Copyright (c) 2006 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<commctrl.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;                 /* is the data in sync with the
+                                   configuration store? */
+    BOOL sync;                  /* is the data in sync with the kinit
+                                   request? */
+    DWORD   renewable;
+    DWORD   forwardable;
+    DWORD   proxiable;
+    DWORD   addressless;
+    DWORD   publicIP;
+
+    wchar_t * cred_message;     /* overrides the credential text, if
+                                   non-NULL */
+    BOOL    pwd_change;         /* force a password change */
+} 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 = PMALLOC(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);
+
+    if (!d)
+        return TRUE;
+
+    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);
+    }
+
+    PFREE(d);
+
+    return TRUE;
+}
+
+LRESULT
+k5_force_password_change(k5_dlg_data * d) {
+    /* we are turning this dialog into a change password dialog... */
+    wchar_t wbuf[KHUI_MAXCCH_BANNER];
+
+    khui_cw_clear_prompts(d->nc);
+
+    LoadString(hResModule, IDS_NC_PWD_BANNER,
+               wbuf, ARRAYLENGTH(wbuf));
+    khui_cw_begin_custom_prompts(d->nc, 3, NULL, wbuf);
+
+    LoadString(hResModule, IDS_NC_PWD_PWD,
+               wbuf, ARRAYLENGTH(wbuf));
+    khui_cw_add_prompt(d->nc, KHUI_NCPROMPT_TYPE_PASSWORD,
+                       wbuf, NULL, KHUI_NCPROMPT_FLAG_HIDDEN);
+
+    LoadString(hResModule, IDS_NC_PWD_NPWD,
+               wbuf, ARRAYLENGTH(wbuf));
+    khui_cw_add_prompt(d->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(d->nc, KHUI_NCPROMPT_TYPE_NEW_PASSWORD_AGAIN,
+                       wbuf, NULL, KHUI_NCPROMPT_FLAG_HIDDEN);
+
+    d->pwd_change = TRUE;
+
+    if (is_k5_identpro &&
+        d->nc->n_identities > 0 &&
+        d->nc->identities[0]) {
+
+        kcdb_identity_set_flags(d->nc->identities[0],
+                                KCDB_IDENT_FLAG_VALID,
+                                KCDB_IDENT_FLAG_VALID);
+
+    }
+
+    PostMessage(d->nc->hwnd, KHUI_WM_NC_NOTIFY,
+                MAKEWPARAM(0, WMNC_UPDATE_CREDTEXT),
+                (LPARAM) d->nc);
+
+    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;
+            BOOL old_sync;
+
+            d = (k5_dlg_data *)(LONG_PTR) 
+                GetWindowLongPtr(hwnd, DWLP_USER);
+
+            if (d->nc->subtype == KMSG_CRED_PASSWORD)
+                return TRUE;
+
+            /* we save the value of the 'sync' field here because some
+               of the notifications that are generated while setting
+               the controls overwrite the field. */
+            old_sync = d->sync;
+
+            /* need to update the controls with d->* */
+            SendDlgItemMessage(hwnd, IDC_NCK5_RENEWABLE, 
+                               BM_SETCHECK,
+                              (d->renewable? BST_CHECKED : BST_UNCHECKED), 
+                               0);
+            EnableWindow(GetDlgItem(hwnd, IDC_NCK5_RENEW_EDIT), 
+                         !!d->renewable);
+
+            khui_tracker_refresh(&d->tc_lifetime);
+            khui_tracker_refresh(&d->tc_renew);
+
+            SendDlgItemMessage(hwnd, IDC_NCK5_FORWARDABLE, 
+                               BM_SETCHECK,
+                               (d->forwardable ? BST_CHECKED : BST_UNCHECKED),
+                               0);
+
+            SendDlgItemMessage(hwnd, IDC_NCK5_ADDRESS,
+                               BM_SETCHECK,
+                               (d->addressless ? BST_CHECKED : BST_UNCHECKED),
+                               0);
+
+            SendDlgItemMessage(hwnd, IDC_NCK5_PUBLICIP,
+                               IPM_SETADDRESS,
+                               0, d->publicIP);
+
+            EnableWindow(GetDlgItem(hwnd, IDC_NCK5_PUBLICIP), !d->addressless);
+
+            d->sync = old_sync;
+        }
+        break;
+
+    case WMNC_CREDTEXT_LINK:
+        {
+            k5_dlg_data * d;
+            khui_htwnd_link * l;
+            khui_new_creds * nc;
+            wchar_t linktext[128];
+
+            d = (k5_dlg_data *)(LONG_PTR)
+                GetWindowLongPtr(hwnd, DWLP_USER);
+            nc = d->nc;
+            l = (khui_htwnd_link *) lParam;
+
+            if (!l)
+                break;
+
+            StringCchCopyN(linktext, ARRAYLENGTH(linktext),
+                           l->id, l->id_len);
+
+            if (!wcscmp(linktext, L"Krb5Cred:!Passwd")) {
+                return k5_force_password_change(d);
+            }
+        }
+        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)
+                PFREE(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 &&
+                !d->pwd_change) {
+
+                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 = PMALLOC(cbsize);
+
+                StringCbCopy(nct->credtext, cbsize, sbuf);
+            } else if (nc->n_identities > 0 &&
+                       (nc->subtype == KMSG_CRED_PASSWORD ||
+                        (nc->subtype == KMSG_CRED_NEW_CREDS && d->pwd_change))) {
+                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 = PMALLOC(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 = PMALLOC(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->sync && d->nc->result == KHUI_NC_RESULT_PROCESS) {
+                kmq_post_sub_msg(k5_sub, KMSG_CRED, 
+                                 KMSG_CRED_DIALOG_NEW_OPTIONS, 
+                                 0, (void *) d->nc);
+            }
+        }
+        break;
+
+    case K5_SET_CRED_MSG:
+        {
+            k5_dlg_data * d;
+            khm_size cb;
+            wchar_t * msg;
+
+            d = (k5_dlg_data *) (LONG_PTR)
+                GetWindowLongPtr(hwnd, DWLP_USER);
+
+            msg = (wchar_t *) lParam;
+
+            if (d->cred_message) {
+                PFREE(d->cred_message);
+                d->cred_message = NULL;
+            }
+
+            if (msg &&
+                SUCCEEDED(StringCbLength(msg,
+                                         KHUI_MAXCB_MESSAGE,
+                                         &cb))) {
+                cb += sizeof(wchar_t);
+                d->cred_message = PMALLOC(cb);
+#ifdef DEBUG
+                assert(d->cred_message);
+#endif
+                StringCbCopy(d->cred_message, cb, msg);
+            }
+        }
+        break;
+    }
+
+    return 0;
+}
+
+INT_PTR
+k5_handle_wm_notify(HWND hwnd,
+                    WPARAM wParam,
+                    LPARAM lParam) {
+    LPNMHDR pnmh;
+    k5_dlg_data * d;
+
+    pnmh = (LPNMHDR) lParam;
+    if (pnmh->idFrom == IDC_NCK5_PUBLICIP &&
+        pnmh->code == IPN_FIELDCHANGED) {
+
+        d = (k5_dlg_data *) (LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER);
+
+        SendDlgItemMessage(hwnd, IDC_NCK5_PUBLICIP,
+                           IPM_GETADDRESS,
+                           0, (LPARAM) &d->publicIP);
+
+        d->dirty = TRUE;
+        d->sync = FALSE;
+
+        return TRUE;
+    }
+
+    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;
+        d->sync = FALSE;
+    } else if(notif == BN_CLICKED && cid == IDC_NCK5_FORWARDABLE) {
+        int c;
+        c = (int) SendDlgItemMessage(hwnd, IDC_NCK5_FORWARDABLE, 
+                                     BM_GETCHECK, 0, 0);
+        if(c==BST_CHECKED) {
+            d->forwardable = TRUE;
+        } else {
+            d->forwardable = FALSE;
+        }
+        d->dirty = TRUE;
+        d->sync = FALSE;
+    } else if (notif == BN_CLICKED && cid == IDC_NCK5_ADDRESS) {
+        int c;
+
+        c = (int) SendDlgItemMessage(hwnd, IDC_NCK5_ADDRESS,
+                                     BM_GETCHECK, 0, 0);
+
+        if (c==BST_CHECKED) {
+            d->addressless = TRUE;
+        } else {
+            d->addressless = FALSE;
+        }
+        d->dirty = TRUE;
+        d->sync = FALSE;
+
+        EnableWindow(GetDlgItem(hwnd, IDC_NCK5_PUBLICIP), !d->addressless);
+    } else if (notif == EN_CHANGE && (cid == IDC_NCK5_RENEW_EDIT ||
+                                      cid == IDC_NCK5_LIFETIME_EDIT)) {
+        d->dirty = TRUE;
+        d->sync = FALSE;
+    } 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_NOTIFY:
+        return k5_handle_wm_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()) {
+                    g_fjob.code = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+                    goto _switch_to_main;
+                }
+            }
+
+#ifdef DEBUG
+            /* log the state of g_fjob.* */
+            _reportf(L"g_fjob state prior to calling khm_krb5_kinit() :");
+            _reportf(L"  g_fjob.principal = [%S]", g_fjob.principal);
+            _reportf(L"  g_fjob.code      = %d", g_fjob.code);
+            _reportf(L"  g_fjob.state     = %d", g_fjob.state);
+            _reportf(L"  g_fjob.prompt_set= %d", g_fjob.prompt_set);
+            _reportf(L"  g_fjob.valid_principal = %d", (int) g_fjob.valid_principal);
+#endif
+
+            /* If we don't know if we have a valid principal, we
+               restrict the options that are set when we call kinit.
+               This way we will be able to use the response from the
+               KDC to verify the principal. */
+
+            g_fjob.retry_if_valid_principal = (g_fjob.forwardable ||
+                                               g_fjob.proxiable ||
+                                               g_fjob.renewable);
+
+        retry_kinit:
+            g_fjob.code =
+                khm_krb5_kinit(0,
+                               g_fjob.principal,
+                               g_fjob.password,
+                               g_fjob.ccache,
+                               g_fjob.lifetime,
+                               g_fjob.valid_principal ? g_fjob.forwardable : 0,
+                               g_fjob.valid_principal ? g_fjob.proxiable : 0,
+                               (g_fjob.valid_principal && g_fjob.renewable ? g_fjob.renew_life : 0),
+                               g_fjob.addressless,
+                               g_fjob.publicIP,
+                               k5_kinit_prompter,
+                               &g_fjob);
+
+            /* If the principal was found to be valid, and if we
+               restricted the options that were being passed to kinit,
+               then we need to retry the kinit call.  This time we use
+               the real options. */
+            if (g_fjob.state == FIBER_STATE_RETRY_KINIT) {
+#ifdef DEBUG
+                assert(g_fjob.valid_principal);
+#endif
+                g_fjob.state = FIBER_STATE_KINIT;
+                goto retry_kinit;
+            }
+        }
+
+    _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;
+    khm_int64 iexpiry;
+    FILETIME expiry;
+    FILETIME current;
+
+#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;
+
+    if (KHM_SUCCEEDED(khc_read_int64(csp_prcache, L"ExpiresOn", &iexpiry))) {
+        /* has the cache expired? */
+        expiry = IntToFt(iexpiry);
+        GetSystemTimeAsFileTime(&current);
+
+        if (CompareFileTime(&expiry, &current) < 0)
+            /* already expired */
+            goto _cleanup;
+    } else {
+        /* if there is no value for ExpiresOn, we assume the prompts
+           have already expired. */
+        goto _cleanup;
+    }
+
+    /* we found a prompt cache.  We take this to imply that the
+       principal is valid. */
+    g_fjob.valid_principal = TRUE;
+
+    /* 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;
+
+#ifdef DEBUG
+    _reportf(L"k5_kinit_prompter() received %d prompts with name=[%S] banner=[%S]",
+             num_prompts,
+             name, banner);
+    for (i=0; i < num_prompts; i++) {
+        _reportf(L"Prompt[%d]: string[%S]", i, prompts[i].prompt);
+    }
+#endif
+
+    /* we got prompts?  Then we assume that the principal is valid */
+
+    if (!g_fjob.valid_principal) {
+        g_fjob.valid_principal = TRUE;
+
+        /* if the flags that were used to call kinit were restricted
+           because we didn't know the validity of the principal, then
+           we need to go back and retry the call with the correct
+           flags. */
+        if (g_fjob.retry_if_valid_principal) {
+            _reportf(L"Retrying kinit call due to restricted flags on first call.");
+            g_fjob.state = FIBER_STATE_RETRY_KINIT;
+            return KRB5_LIBOS_PWDINTR;
+        }
+    }
+
+    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];
+        FILETIME current;
+        FILETIME lifetime;
+        FILETIME expiry;
+        khm_int64 iexpiry;
+        khm_int32 t = 0;
+
+        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 (csp_prcache) {
+
+            if (banner)
+                khc_write_string(csp_prcache,
+                                 L"Banner",
+                                 wbanner);
+            else
+                khc_write_string(csp_prcache,
+                                 L"Banner",
+                                 L"");
+
+            if (name)
+                khc_write_string(csp_prcache,
+                                 L"Name",
+                                 wname);
+            else if (csp_prcache)
+                khc_write_string(csp_prcache,
+                                 L"Name",
+                                 L"");
+
+            khc_write_int32(csp_prcache,
+                            L"PromptCount",
+                            (khm_int32) num_prompts);
+
+            GetSystemTimeAsFileTime(&current);
+#ifdef USE_PROMPT_CACHE_LIFETIME
+            khc_read_int32(csp_params, L"PromptCacheLifetime", &t);
+            if (t == 0)
+                t = 172800;         /* 48 hours */
+#else
+            khc_read_int32(csp_params, L"MaxRenewLifetime", &t);
+            if (t == 0)
+                t = 2592000;    /* 30 days */
+            t += 604800;        /* + 7 days */
+#endif
+            TimetToFileTimeInterval(t, &lifetime);
+            expiry = FtAdd(&current, &lifetime);
+            iexpiry = FtToInt(&expiry);
+
+            khc_write_int64(csp_prcache, L"ExpiresOn", iexpiry);
+        }
+    }
+
+    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 = KRB5_LIBOS_PWDINTR;
+        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 KRB5_LIBOS_PWDINTR;
+    else
+        return code;
+}
+
+
+void 
+k5_read_dlg_params(k5_dlg_data * d, khm_handle identity)
+{
+    k5_params p;
+
+    khm_krb5_get_identity_params(identity, &p);
+
+    d->renewable = p.renewable;
+    d->forwardable = p.forwardable;
+    d->proxiable = p.proxiable;
+    d->addressless = p.addressless;
+    d->publicIP = p.publicIP;
+
+    d->tc_lifetime.current = p.lifetime;
+    d->tc_lifetime.max = p.lifetime_max;
+    d->tc_lifetime.min = p.lifetime_min;
+
+    d->tc_renew.current = p.renew_life;
+    d->tc_renew.max = p.renew_life_max;
+    d->tc_renew.min = p.renew_life_min;
+
+    /* 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;
+    d->sync = FALSE;
+}
+
+void 
+k5_write_dlg_params(k5_dlg_data * d, khm_handle identity)
+{
+
+    k5_params p;
+
+    ZeroMemory(&p, sizeof(p));
+
+    p.source_reg = K5PARAM_FM_ALL; /* we want to write all the
+                                      settings to the registry, if
+                                      necessary. */
+
+    p.renewable = d->renewable;
+    p.forwardable = d->forwardable;
+    p.proxiable = d->proxiable;
+    p.addressless = d->addressless;
+    p.publicIP = d->publicIP;
+
+    p.lifetime = (krb5_deltat) d->tc_lifetime.current;
+    p.lifetime_max = (krb5_deltat) d->tc_lifetime.max;
+    p.lifetime_min = (krb5_deltat) d->tc_lifetime.min;
+
+    p.renew_life = (krb5_deltat) d->tc_renew.current;
+    p.renew_life_max = (krb5_deltat) d->tc_renew.max;
+    p.renew_life_min = (krb5_deltat) d->tc_renew.min;
+
+    khm_krb5_set_identity_params(identity, &p);
+
+    /* as in k5_read_dlg_params, once we write the data in, the local
+       data is no longer dirty */
+    d->dirty = FALSE;
+}
+
+void 
+k5_free_kinit_job(void)
+{
+    if (g_fjob.principal)
+        PFREE(g_fjob.principal);
+
+    if (g_fjob.password)
+        PFREE(g_fjob.password);
+
+    if (g_fjob.identity)
+        kcdb_identity_release(g_fjob.identity);
+
+    if (g_fjob.ccache)
+        PFREE(g_fjob.ccache);
+
+    ZeroMemory(&g_fjob, sizeof(g_fjob));
+}
+
+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);
+
+    if (!d)
+       return;
+
+    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++;
+
+    k5_free_kinit_job();
+
+    g_fjob.command = FIBER_CMD_KINIT;
+    g_fjob.nc = nc;
+    g_fjob.nct = nct;
+    g_fjob.dialog = nct->hwnd_panel;
+    g_fjob.principal = PMALLOC(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 = d->publicIP;
+    g_fjob.code = 0;
+    g_fjob.identity = ident;
+    g_fjob.prompt_set = 0;
+    g_fjob.valid_principal = FALSE;
+    g_fjob.retry_if_valid_principal = FALSE;
+
+                                /* the value for
+                                   retry_if_valid_principal is not
+                                   necessarily the correct value here,
+                                   but the correct value will be
+                                   assigned k5_kinit_fiber_proc(). */
+
+    /* 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 = PMALLOC(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 */
+}
+
+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) &&
+        !(f & KCDB_CRED_FLAG_EXPIRED))
+        rv = 1;
+    else
+        rv = 0;
+
+    if (cident)
+        kcdb_identity_release(cident);
+
+    return rv;
+}
+
+khm_int32
+k5_remove_from_LRU(khm_handle identity)
+{
+    wchar_t * wbuf = NULL;
+    wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
+    khm_size cb;
+    khm_size cb_ms;
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+
+    cb = sizeof(idname);
+    rv = kcdb_identity_get_name(identity, idname, &cb);
+    assert(rv == KHM_ERROR_SUCCESS);
+
+    rv = khc_read_multi_string(csp_params, L"LRUPrincipals", NULL, &cb_ms);
+    if (rv != KHM_ERROR_TOO_LONG)
+        cb_ms = sizeof(wchar_t) * 2;
+
+    wbuf = PMALLOC(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) {
+            multi_string_delete(wbuf, idname, KHM_CASE_SENSITIVE);
+        }
+    } else {
+        multi_string_init(wbuf, cb_ms);
+    }
+
+    rv = khc_write_multi_string(csp_params, L"LRUPrincipals", wbuf);
+
+    if (wbuf)
+        PFREE(wbuf);
+
+    return rv;
+}
+
+khm_int32
+k5_update_LRU(khm_handle identity)
+{
+    wchar_t * wbuf = NULL;
+    wchar_t * idname = NULL;
+    wchar_t * realm = NULL;
+    khm_size cb;
+    khm_size cb_ms;
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+
+    rv = kcdb_identity_get_name(identity, NULL, &cb);
+    assert(rv == KHM_ERROR_TOO_LONG);
+
+    idname = PMALLOC(cb);
+    assert(idname);
+
+    rv = kcdb_identity_get_name(identity, 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 = PMALLOC(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.  We remove it here and add it at
+               the top of the LRU list. */
+            multi_string_delete(wbuf, idname, KHM_CASE_SENSITIVE);
+        }
+    } 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);
+
+    realm = khm_get_realm_from_princ(idname);
+    if (realm == NULL || *realm == L'\0')
+        goto _done_with_LRU;
+
+    cb = cb_ms;
+    rv = khc_read_multi_string(csp_params, L"LRURealms", wbuf, &cb);
+
+    if (rv == KHM_ERROR_TOO_LONG) {
+        PFREE(wbuf);
+        wbuf = PMALLOC(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, realm, KHM_CASE_SENSITIVE) != NULL) {
+            /* remove the realm and add it at the top later. */
+            multi_string_delete(wbuf, realm, KHM_CASE_SENSITIVE); 
+        }
+    } else {
+        multi_string_init(wbuf, cb_ms);
+    }
+
+    cb = cb_ms;
+    rv = multi_string_prepend(wbuf, &cb, realm);
+
+    if (rv == KHM_ERROR_TOO_LONG) {
+        wbuf = PREALLOC(wbuf, cb);
+
+        rv = multi_string_prepend(wbuf, &cb, realm);
+
+        assert(KHM_SUCCEEDED(rv));
+    }
+
+    rv = khc_write_multi_string(csp_params, L"LRURealms", wbuf);
+    
+    assert(KHM_SUCCEEDED(rv));
+
+ _done_with_LRU:
+
+    if (wbuf)
+        PFREE(wbuf);
+    if (idname)
+        PFREE(idname);
+
+    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 = PMALLOC(sizeof(*nct));
+            ZeroMemory(nct, sizeof(*nct));
+
+            nct->type = credtype_id_krb5;
+            nct->ordinal = 1;
+
+            LoadString(hResModule, IDS_KRB5_NC_NAME, 
+                       wbuf, ARRAYLENGTH(wbuf));
+            StringCbLength(wbuf, sizeof(wbuf), &cbsize);
+            cbsize += sizeof(wchar_t);
+
+            nct->name = PMALLOC(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 = PMALLOC(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);
+
+            /* this can be NULL if the dialog was closed while the
+               plug-in thread was processing. */
+            if (d == NULL)
+                break;
+
+            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) {
+                    for (t = realms; t && *t; t = multi_string_next(t)) {
+                        SendDlgItemMessage(hwnd, IDC_NCK5_REALM, 
+                                           CB_ADDSTRING,
+                                           0, (LPARAM) t);
+                    }
+                    PFREE(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);
+                    PFREE(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(d, NULL);
+            }
+
+            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);
+
+            if (d == NULL)
+                break;
+
+            /* we only load the identity specific defaults if the user
+               hasn't changed the options */
+            khui_cw_lock_nc(nc);
+
+           /* ?: It might be better to not load identity defaults if
+              the user has already changed options in the dialog. */
+            if(/* !d->dirty && */ nc->n_identities > 0 &&
+               nc->subtype == KMSG_CRED_NEW_CREDS) {
+
+                k5_read_dlg_params(d, nc->identities[0]);
+
+                PostMessage(nct->hwnd_panel, KHUI_WM_NC_NOTIFY, 
+                            MAKEWPARAM(0,WMNC_DIALOG_SETUP), 0);
+            }
+
+            khui_cw_unlock_nc(nc);
+
+            /* reset the force-password-change flag if this is a new
+               identity. */
+            d->pwd_change = FALSE;
+        }
+
+        /* 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 (d == NULL)
+                break;
+
+            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 we are forcing a password change, then we don't do
+               anything here.  Note that if the identity changed, then
+               this field would have been reset, so we would proceed
+               as usual. */
+            if (d->pwd_change)
+                return KHM_ERROR_SUCCESS;
+
+#if 0
+            /* Clearing the prompts at this point is a bad idea since
+               the prompter depends on the prompts to know if this set
+               of prompts is the same as the new set and if so, use
+               the values entered in the old prompts as responses to
+               the new one. */
+            khui_cw_clear_prompts(nc);
+#endif
+
+            /* 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);
+
+                /* after the switch to the fiber, the dialog will be
+                   back in sync with the kinit thread. */
+                d->sync = TRUE;
+
+                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;
+
+                    /* Special case.  If the users' password has
+                       expired, we force a password change dialog on
+                       top of the new credentials dialog using a set
+                       of custom prompts, but only if we are the
+                       identity provider. */
+                    if (g_fjob.code == KRB5KDC_ERR_KEY_EXP &&
+                        is_k5_identpro) {
+
+                        k5_force_password_change(d);
+                        goto done_with_bad_princ;
+
+                    }
+
+                    /* we can't possibly have succeeded without a
+                       password */
+                    if(g_fjob.code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN && 
+                      is_k5_identpro) {
+                        kcdb_identity_set_flags(ident,
+                                                KCDB_IDENT_FLAG_INVALID,
+                                                KCDB_IDENT_FLAG_INVALID);
+
+                        khui_cw_clear_prompts(nc);
+                    }
+
+                    if (d->cred_message) {
+                        PFREE(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 = PMALLOC(cb);
+                        StringCbCopy(d->cred_message, cb, msg);
+                    }
+
+                done_with_bad_princ:
+
+                    k5_free_kinit_job();
+
+                    if (is_k5_identpro)
+                        kcdb_identity_set_flags(ident,
+                                                KCDB_IDENT_FLAG_UNKNOWN,
+                                                KCDB_IDENT_FLAG_UNKNOWN);
+
+
+                } 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,
+                                                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 = 0;
+
+            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;
+                if (d == NULL)
+                    break;
+
+                if (d->pwd_change) {
+                    /* we are forcing a password change */
+                    goto change_password;
+                }
+
+                _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;
+
+                       _reportf(L"Cancelling");
+                    } else if (nc->result == KHUI_NC_RESULT_PROCESS) {
+                        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_PROCESS) {
+                        /* 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)))) {
+                   _reportf(L"No password entered, but a valid TGT exists. Continuing");
+                    g_fjob.code = 0;
+               } else if (g_fjob.state == FIBER_STATE_NONE &&
+                           g_fjob.code == 0 &&
+                           nc->n_identities > 0 &&
+                           nc->identities[0] != NULL) {
+
+                    /* we had a password and we used it to get
+                       tickets.  We should reset the IMPORTED flag now
+                       since the tickets are not imported. */
+
+                    khm_krb5_set_identity_flags(nc->identities[0],
+                                                K5IDFLAG_IMPORTED,
+                                                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
+
+                    if (g_fjob.valid_principal &&
+                        nc->n_identities > 0 &&
+                        nc->identities[0]) {
+                        /* the principal was valid, so we can go ahead
+                           and update the LRU */
+                        k5_update_LRU(nc->identities[0]);
+                    }
+
+                } else if (nc->result == KHUI_NC_RESULT_PROCESS &&
+                           g_fjob.state == FIBER_STATE_NONE) {
+                    krb5_context ctx = NULL;
+
+                   _reportf(L"Tickets successfully acquired");
+
+                    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]);
+
+                    k5_write_dlg_params(d, nc->identities[0]);
+
+                    /* 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);
+
+                    if (nc->set_default) {
+                       _reportf(L"Setting default identity");
+                        kcdb_identity_set_default(nc->identities[0]);
+                    }
+
+                    /* If there is no default identity, then make this the default */
+                    kcdb_identity_refresh(nc->identities[0]);
+                    {
+                        khm_handle tdefault = NULL;
+
+                        if (KHM_SUCCEEDED(kcdb_identity_get_default(&tdefault))) {
+                            kcdb_identity_release(tdefault);
+                        } else {
+                           _reportf(L"There was no default identity.  Setting default");
+                            kcdb_identity_set_default(nc->identities[0]);
+                        }
+                    }
+
+                    /* and update the LRU */
+                    k5_update_LRU(nc->identities[0]);
+
+                    if (ctx != NULL)
+                        pkrb5_free_context(ctx);
+                } 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) {
+
+                FILETIME ftidexp = {0,0};
+                FILETIME ftcurrent;
+                khm_size cb;
+
+                GetSystemTimeAsFileTime(&ftcurrent);
+
+                _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) ||
+                   (nc->ctx.scope == KHUI_SCOPE_CRED &&
+                    nc->ctx.cred_type == credtype_id_krb5)) {
+                    int code;
+
+                   if (nc->ctx.scope == KHUI_SCOPE_CRED &&
+                       nc->ctx.cred != NULL) {
+
+                       /* get the expiration time for the identity first. */
+                       cb = sizeof(ftidexp);
+#ifdef DEBUG
+                       assert(nc->ctx.identity != NULL);
+#endif
+                       kcdb_identity_get_attr(nc->ctx.identity,
+                                              KCDB_ATTR_EXPIRE,
+                                              NULL,
+                                              &ftidexp,
+                                              &cb);
+
+                       code = khm_krb5_renew_cred(nc->ctx.cred);
+
+                    } else if (nc->ctx.scope == KHUI_SCOPE_IDENT &&
+                              nc->ctx.identity != 0) {
+                        /* get the current identity expiration time */
+                        cb = sizeof(ftidexp);
+
+                        kcdb_identity_get_attr(nc->ctx.identity,
+                                               KCDB_ATTR_EXPIRE,
+                                               NULL,
+                                               &ftidexp,
+                                               &cb);
+
+                        code = khm_krb5_renew_ident(nc->ctx.identity);
+                    } else {
+
+                       _reportf(L"No identity specified.  Can't renew Kerberos tickets");
+
+                        code = 1; /* it just has to be non-zero */
+                    }
+
+                    if (code == 0) {
+                       _reportf(L"Tickets successfully renewed");
+
+                        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 if (CompareFileTime(&ftcurrent, &ftidexp) < 0) {
+                        wchar_t tbuf[1024];
+                        DWORD suggestion;
+                        kherr_suggestion sug_id;
+
+                        /* if we failed to get new tickets, but the
+                           identity is still valid, then we assume that
+                           the current tickets are still good enough
+                           for other credential types to obtain their
+                           credentials. */
+
+                        khm_err_describe(code, tbuf, sizeof(tbuf),
+                                         &suggestion, &sug_id);
+
+                        _report_cs0(KHERR_WARNING, tbuf);
+                        if (suggestion)
+                            _suggest_mr(suggestion, sug_id);
+
+                        _resolve();
+
+                        khui_cw_set_response(nc, credtype_id_krb5, 
+                                             KHUI_NC_RESPONSE_EXIT |
+                                             KHUI_NC_RESPONSE_SUCCESS);
+                    } 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_PROCESS) {
+
+            change_password:
+                /* we jump here if there was a password change forced */
+
+                _begin_task(0);
+                _report_mr0(KHERR_NONE, MSG_CTX_PASSWD);
+                _describe();
+
+                khui_cw_lock_nc(nc);
+
+                if (nc->result == KHUI_NC_RESULT_CANCEL) {
+
+                    khui_cw_set_response(nc, credtype_id_krb5,
+                                         KHUI_NC_RESPONSE_SUCCESS |
+                                         KHUI_NC_RESPONSE_EXIT);
+
+                } else 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_PARAM;
+                        _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_PARAM;
+                        _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;
+                    else {
+                        khm_handle csp_idcfg = NULL;
+                        krb5_context ctx = NULL;
+
+                        /* we set a new password.  now we need to get
+                           initial credentials. */
+
+                        d = (k5_dlg_data *) nct->aux;
+
+                        if (d == NULL) {
+                            rv = KHM_ERROR_UNKNOWN;
+                            goto _pwd_exit;
+                        }
+
+                        if (nc->subtype == KMSG_CRED_PASSWORD) {
+                            /* since this was just a password change,
+                               we need to load new credentials options
+                               from the configuration store. */
+
+                            k5_read_dlg_params(d, nc->identities[0]);
+                        }
+
+                        /* the password change phase is now done */
+                        d->pwd_change = FALSE;
+
+#ifdef DEBUG
+                        _reportf(L"Calling khm_krb5_kinit()");
+#endif
+                        code = khm_krb5_kinit(NULL, /* context (create one) */
+                                              idname, /* principal_name */
+                                              npwd, /* new password */
+                                              NULL, /* ccache name (figure out the identity cc)*/
+                                              (krb5_deltat) d->tc_lifetime.current,
+                                              d->forwardable,
+                                              d->proxiable,
+                                              (krb5_deltat)((d->renewable)?d->tc_renew.current:0),
+                                              d->addressless, /* addressless */
+                                              d->publicIP, /* public IP */
+                                              NULL, /* prompter */
+                                              NULL /* prompter data */);
+
+                        if (code) {
+                            rv = KHM_ERROR_UNKNOWN;
+                            goto _pwd_exit;
+                        }
+
+                        /* save the settings that we used for
+                           obtaining the ticket. */
+                        if (nc->subtype == KMSG_CRED_NEW_CREDS) {
+
+                            k5_write_dlg_params(d, nc->identities[0]);
+
+                            /* and then update the LRU too */
+                            k5_update_LRU(nc->identities[0]);
+                        }
+
+                        /* and do a quick refresh of the krb5 tickets
+                           so that other plug-ins that depend on krb5
+                           can look up tickets inside NetIDMgr */
+                        khm_krb5_list_tickets(&ctx);
+
+                        /* if there was no default identity, we make
+                           this one the default. */
+                        kcdb_identity_refresh(nc->identities[0]);
+                        {
+                            khm_handle tdefault = NULL;
+
+                            if (KHM_SUCCEEDED(kcdb_identity_get_default(&tdefault))) {
+                                kcdb_identity_release(tdefault);
+                            } else {
+                                _reportf(L"There was no default identity.  Setting defualt");
+                                kcdb_identity_set_default(nc->identities[0]);
+                            }
+                        }
+
+                        if (ctx != NULL)
+                            pkrb5_free_context(ctx);
+
+                        if (nc->subtype == KMSG_CRED_PASSWORD) {
+                            /* if we obtained new credentials as a
+                               result of successfully changing the
+                               password, we also schedule an identity
+                               renewal for this identity.  This allows
+                               the other credential types to obtain
+                               credentials for this identity. */
+                            khui_action_context ctx;
+
+                            _reportf(L"Scheduling renewal of [%s] after password change",
+                                     widname);
+
+                            khui_context_create(&ctx,
+                                                KHUI_SCOPE_IDENT,
+                                                nc->identities[0],
+                                                KCDB_CREDTYPE_INVALID,
+                                                NULL);
+                            khui_action_trigger(KHUI_ACTION_RENEW_CRED,
+                                                &ctx);
+
+                            khui_context_release(&ctx);
+                        }
+                    }
+
+                    /* result is only set when code != 0 */
+                    if (code && result) {
+                        size_t len;
+
+                        StringCchLengthA(result, KHERR_MAXCCH_STRING,
+                                         &len);
+                        wresult = PMALLOC((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();
+
+                        PFREE(result);
+                        PFREE(wresult);
+
+                        /* 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)
+                PFREE(nct->name);
+            if (nct->credtext)
+                PFREE(nct->credtext);
+
+            PFREE(nct);
+
+            k5_free_kinit_job();
+        }
+        break;
+
+    case KMSG_CRED_IMPORT:
+        {
+            khm_int32 t = 0;
+
+#ifdef DEBUG
+            assert(csp_params);
+#endif
+            khc_read_int32(csp_params, L"MsLsaImport", &t);
+
+            if (t != K5_LSAIMPORT_NEVER) {
+                krb5_context ctx = NULL;
+                khm_handle id_default = NULL;
+                khm_handle id_imported = NULL;
+                BOOL imported;
+
+                imported = khm_krb5_ms2mit(NULL, (t == K5_LSAIMPORT_MATCH), TRUE,
+                                           &id_imported);
+                if (imported) {
+                    khm_krb5_list_tickets(&ctx);
+
+                    if (ctx)
+                        pkrb5_free_context(ctx);
+
+                    kcdb_identity_refresh(id_imported);
+
+                    if (KHM_SUCCEEDED(kcdb_identity_get_default(&id_default))) {
+                        kcdb_identity_release(id_default);
+                        id_default = NULL;
+                    } else {
+                        _reportf(L"There was no default identity.  Setting default");
+                        kcdb_identity_set_default(id_imported);
+                    }
+
+                    /* and update the LRU */
+                    k5_update_LRU(id_imported);
+                }
+
+                if (id_imported)
+                    kcdb_identity_release(id_imported);
+            }
+        }
+        break;
+    }
+
+    return rv;
+}
index bb481e0fbee01801d09d454685828661e04d9798..61331f3844718af0a15991d401e2cc25e0ccfa59 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<krbcred.h>\r
-#include<kherror.h>\r
-#include<khmsgtypes.h>\r
-#include<commctrl.h>\r
-#include<strsafe.h>\r
-#include<krb5.h>\r
-\r
-#ifdef DEBUG\r
-#include<assert.h>\r
-#endif\r
-\r
-khm_int32 credtype_id_krb5 = KCDB_CREDTYPE_INVALID;\r
-khm_boolean krb5_initialized = FALSE;\r
-khm_handle krb5_credset = NULL;\r
-\r
-khm_handle k5_sub = NULL;\r
-\r
-LPVOID k5_main_fiber = NULL;\r
-LPVOID k5_kinit_fiber = NULL;\r
-\r
-VOID CALLBACK k5_kinit_fiber_proc(PVOID lpParameter);\r
-\r
-krb5_context k5_identpro_ctx = NULL;\r
-\r
-/*  The system message handler.\r
-\r
-    Runs in the context of the plugin thread */\r
-khm_int32 KHMAPI \r
-k5_msg_system(khm_int32 msg_type, khm_int32 msg_subtype, \r
-              khm_ui_4 uparam, void * vparam)\r
-{\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    switch(msg_subtype) {\r
-    case KMSG_SYSTEM_INIT:\r
-        {\r
-            kcdb_credtype ct;\r
-            wchar_t buf[KCDB_MAXCCH_SHORT_DESC];\r
-            size_t cbsize;\r
-\r
-            /* perform critical registrations and initialization\r
-               stuff */\r
-            ZeroMemory(&ct, sizeof(ct));\r
-            ct.id = KCDB_CREDTYPE_AUTO;\r
-            ct.name = KRB5_CREDTYPE_NAME;\r
-\r
-            if(LoadString(hResModule, IDS_KRB5_SHORT_DESC, \r
-                          buf, ARRAYLENGTH(buf))) {\r
-                StringCbLength(buf, KCDB_MAXCB_SHORT_DESC, &cbsize);\r
-                cbsize += sizeof(wchar_t);\r
-                ct.short_desc = PMALLOC(cbsize);\r
-                StringCbCopy(ct.short_desc, cbsize, buf);\r
-            }\r
-\r
-            /* even though ideally we should be setting limits\r
-               based KCDB_MAXCB_LONG_DESC, our long description\r
-               actually fits nicely in KCDB_MAXCB_SHORT_DESC */\r
-            if(LoadString(hResModule, IDS_KRB5_LONG_DESC, \r
-                          buf, ARRAYLENGTH(buf))) {\r
-                StringCbLength(buf, KCDB_MAXCB_SHORT_DESC, &cbsize);\r
-                cbsize += sizeof(wchar_t);\r
-                ct.long_desc = PMALLOC(cbsize);\r
-                StringCbCopy(ct.long_desc, cbsize, buf);\r
-            }\r
-\r
-            ct.icon = NULL; /* TODO: set a proper icon */\r
-\r
-            kmq_create_subscription(k5_msg_callback, &ct.sub);\r
-\r
-            ct.is_equal = khm_krb5_creds_is_equal;\r
-\r
-            rv = kcdb_credtype_register(&ct, &credtype_id_krb5);\r
-\r
-            if(KHM_SUCCEEDED(rv))\r
-                rv = kcdb_credset_create(&krb5_credset);\r
-\r
-            if(ct.short_desc)\r
-                PFREE(ct.short_desc);\r
-\r
-            if(ct.long_desc)\r
-                PFREE(ct.long_desc);\r
-\r
-            if(KHM_SUCCEEDED(rv)) {\r
-                krb5_context ctx = NULL;\r
-\r
-                krb5_initialized = TRUE;\r
-\r
-                /* now convert this thread to a fiber and create a\r
-                   separate fiber to do kinit stuff */\r
-                k5_main_fiber = ConvertThreadToFiber(NULL);\r
-                k5_kinit_fiber = CreateFiber(0,k5_kinit_fiber_proc,NULL);\r
-\r
-                ZeroMemory(&g_fjob, sizeof(g_fjob));\r
-\r
-                kmq_create_subscription(k5_msg_callback, &k5_sub);\r
-\r
-                k5_register_config_panels();\r
-\r
-                khm_krb5_list_tickets(&ctx);\r
-\r
-                if(ctx != NULL)\r
-                    pkrb5_free_context(ctx);\r
-            }\r
-        }\r
-        break;\r
-\r
-    case KMSG_SYSTEM_EXIT:\r
-\r
-        k5_unregister_config_panels();\r
-\r
-        if(credtype_id_krb5 >= 0) {\r
-            /* basically just unregister the credential type */\r
-            kcdb_credtype_unregister(credtype_id_krb5);\r
-\r
-            /* kcdb knows how to deal with bad handles */\r
-            kcdb_credset_delete(krb5_credset);\r
-            krb5_credset = NULL;\r
-        }\r
-\r
-        if(k5_main_fiber != NULL) {\r
-            if (k5_kinit_fiber) {\r
-#ifdef DEBUG\r
-                assert(k5_kinit_fiber != GetCurrentFiber());\r
-#endif\r
-#ifdef CLEANUP_FIBERS_ON_EXIT\r
-                DeleteFiber(k5_kinit_fiber);\r
-                CloseHandle(k5_kinit_fiber);\r
-#endif\r
-                k5_kinit_fiber = NULL;\r
-            }\r
-\r
-            k5_main_fiber = NULL;\r
-        }\r
-\r
-        if(k5_sub != NULL) {\r
-            kmq_delete_subscription(k5_sub);\r
-            k5_sub = NULL;\r
-        }\r
-\r
-        break;\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-khm_int32 KHMAPI\r
-k5_msg_kcdb(khm_int32 msg_type, khm_int32 msg_subtype,\r
-            khm_ui_4 uparam, void * vparam)\r
-{\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    switch(msg_subtype) {\r
-    case KMSG_KCDB_IDENT:\r
-        if (uparam == KCDB_OP_DELCONFIG) {\r
-            k5_remove_from_LRU((khm_handle) vparam);\r
-        }\r
-        break;\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-\r
-/* Handler for CRED type messages\r
-\r
-    Runs in the context of the Krb5 plugin\r
-*/\r
-khm_int32 KHMAPI \r
-k5_msg_cred(khm_int32 msg_type, khm_int32 msg_subtype, \r
-            khm_ui_4 uparam, void * vparam)\r
-{\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    switch(msg_subtype) {\r
-    case KMSG_CRED_REFRESH:\r
-        {\r
-            krb5_context ctx = NULL;\r
-\r
-            khm_krb5_list_tickets(&ctx);\r
-\r
-            if(ctx != NULL)\r
-                pkrb5_free_context(ctx);\r
-        }\r
-        break;\r
-\r
-    case KMSG_CRED_DESTROY_CREDS:\r
-        {\r
-            khui_action_context * ctx;\r
-\r
-            ctx = (khui_action_context *) vparam;\r
-\r
-            if (ctx->credset) {\r
-                _begin_task(0);\r
-                _report_mr0(KHERR_INFO, MSG_ERR_CTX_DESTROY_CREDS);\r
-                _describe();\r
-\r
-                khm_krb5_destroy_by_credset(ctx->credset);\r
-\r
-                _end_task();\r
-            }\r
-        }\r
-        break;\r
-\r
-    case KMSG_CRED_PP_BEGIN:\r
-        k5_pp_begin((khui_property_sheet *) vparam);\r
-        break;\r
-\r
-    case KMSG_CRED_PP_END:\r
-        k5_pp_end((khui_property_sheet *) vparam);\r
-        break;\r
-\r
-    default:\r
-        if(IS_CRED_ACQ_MSG(msg_subtype))\r
-            return k5_msg_cred_dialog(msg_type, msg_subtype, \r
-                                      uparam, vparam);\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-/*  The main message handler.  We don't do much here, except delegate\r
-    to other message handlers\r
-\r
-    Runs in the context of the Krb5 plugin\r
-*/\r
-khm_int32 KHMAPI \r
-k5_msg_callback(khm_int32 msg_type, khm_int32 msg_subtype, \r
-                khm_ui_4 uparam, void * vparam)\r
-{\r
-    switch(msg_type) {\r
-    case KMSG_SYSTEM:\r
-        return k5_msg_system(msg_type, msg_subtype, uparam, vparam);\r
-    case KMSG_CRED:\r
-        return k5_msg_cred(msg_type, msg_subtype, uparam, vparam);\r
-    case KMSG_KCDB:\r
-        return k5_msg_kcdb(msg_type, msg_subtype, uparam, vparam);\r
-    }\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+
+#ifdef DEBUG
+#include<assert.h>
+#endif
+
+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 = PMALLOC(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 = PMALLOC(cbsize);
+                StringCbCopy(ct.long_desc, cbsize, buf);
+            }
+
+            ct.icon = NULL; /* TODO: set a proper icon */
+
+            kmq_create_subscription(k5_msg_callback, &ct.sub);
+
+            ct.is_equal = khm_krb5_creds_is_equal;
+
+            rv = kcdb_credtype_register(&ct, &credtype_id_krb5);
+
+            if(KHM_SUCCEEDED(rv))
+                rv = kcdb_credset_create(&krb5_credset);
+
+            if(ct.short_desc)
+                PFREE(ct.short_desc);
+
+            if(ct.long_desc)
+                PFREE(ct.long_desc);
+
+            if(KHM_SUCCEEDED(rv)) {
+                krb5_context ctx = NULL;
+
+                krb5_initialized = TRUE;
+
+                /* 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);
+
+                k5_register_config_panels();
+
+                khm_krb5_list_tickets(&ctx);
+
+                if(ctx != NULL)
+                    pkrb5_free_context(ctx);
+            }
+        }
+        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) {
+            if (k5_kinit_fiber) {
+#ifdef DEBUG
+                assert(k5_kinit_fiber != GetCurrentFiber());
+#endif
+#ifdef CLEANUP_FIBERS_ON_EXIT
+                DeleteFiber(k5_kinit_fiber);
+                CloseHandle(k5_kinit_fiber);
+#endif
+                k5_kinit_fiber = NULL;
+            }
+
+            k5_main_fiber = NULL;
+        }
+
+        if(k5_sub != NULL) {
+            kmq_delete_subscription(k5_sub);
+            k5_sub = NULL;
+        }
+
+        break;
+    }
+
+    return rv;
+}
+
+khm_int32 KHMAPI
+k5_msg_kcdb(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_KCDB_IDENT:
+        if (uparam == KCDB_OP_DELCONFIG) {
+            k5_remove_from_LRU((khm_handle) vparam);
+        }
+        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) {
+                _begin_task(0);
+                _report_mr0(KHERR_INFO, MSG_ERR_CTX_DESTROY_CREDS);
+                _describe();
+
+                khm_krb5_destroy_by_credset(ctx->credset);
+
+                _end_task();
+            }
+        }
+        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_KCDB:
+        return k5_msg_kcdb(msg_type, msg_subtype, uparam, vparam);
+    }
+    return KHM_ERROR_SUCCESS;
+}
index 0d8d27276e4ceda7eae3ed37b7852e8d50120b25..312b576fd19032ace93e700465d09516d8a7140b 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<krbcred.h>\r
-#include<kherror.h>\r
-#include<khmsgtypes.h>\r
-#include<commctrl.h>\r
-#include<strsafe.h>\r
-#include<krb5.h>\r
-#ifdef DEBUG\r
-#include<assert.h>\r
-#endif\r
-\r
-/* Property page\r
-\r
-   Runs in the context of the UI thread.\r
-   */\r
-INT_PTR CALLBACK krb5_pp_proc(HWND hwnd,\r
-    UINT uMsg,\r
-    WPARAM wParam,\r
-    LPARAM lParam\r
-    ) \r
-{\r
-    switch(uMsg) {\r
-    case WM_INITDIALOG:\r
-        {\r
-            khui_property_sheet * s;\r
-            PROPSHEETPAGE * p;\r
-            wchar_t buf[512];\r
-            wchar_t unavailable[64];\r
-            khm_size cbsize;\r
-            khm_int32 rv;\r
-            khm_int32 tflags;\r
-\r
-            p = (PROPSHEETPAGE *) lParam;\r
-            s = (khui_property_sheet *) p->lParam;\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-            SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) s);\r
-#pragma warning(pop)\r
-\r
-            LoadString(hResModule, IDS_UNAVAILABLE,\r
-                       unavailable, ARRAYLENGTH(unavailable));\r
-\r
-            if(s->cred) {\r
-                cbsize = sizeof(buf);\r
-                kcdb_cred_get_name(s->cred, buf, &cbsize);\r
-                SetDlgItemText(hwnd, IDC_PPK5_NAME, buf);\r
-\r
-                cbsize = sizeof(buf);\r
-                rv = kcdb_cred_get_attr_string(s->cred, \r
-                                               KCDB_ATTR_ISSUE, \r
-                                               buf, &cbsize, 0);\r
-                if (KHM_SUCCEEDED(rv))\r
-                    SetDlgItemText(hwnd, IDC_PPK5_ISSUE, buf);\r
-                else\r
-                    SetDlgItemText(hwnd, IDC_PPK5_ISSUE, unavailable);\r
-\r
-                cbsize = sizeof(buf);\r
-                rv = kcdb_cred_get_attr_string(s->cred, \r
-                                               KCDB_ATTR_EXPIRE, \r
-                                               buf, &cbsize, 0);\r
-                if (KHM_SUCCEEDED(rv))\r
-                    SetDlgItemText(hwnd, IDC_PPK5_VALID, buf);\r
-                else\r
-                    SetDlgItemText(hwnd, IDC_PPK5_VALID, unavailable);\r
-\r
-                cbsize = sizeof(buf);\r
-                rv = kcdb_cred_get_attr_string(s->cred, \r
-                                               KCDB_ATTR_RENEW_EXPIRE, \r
-                                               buf, &cbsize, 0);\r
-                if (KHM_SUCCEEDED(rv))\r
-                    SetDlgItemText(hwnd, IDC_PPK5_RENEW, buf);\r
-                else\r
-                    SetDlgItemText(hwnd, IDC_PPK5_RENEW, unavailable);\r
-\r
-                tflags = 0;\r
-                cbsize = sizeof(tflags);\r
-                rv = kcdb_cred_get_attr(s->cred,\r
-                                        attr_id_krb5_flags,\r
-                                        NULL,\r
-                                        &tflags,\r
-                                        &cbsize);\r
-                if (KHM_SUCCEEDED(rv)) {\r
-\r
-#define ADDBITFLAG(f,s) \\r
-   if (tflags & f) {    \\r
-     LoadString(hResModule, s, buf, ARRAYLENGTH(buf)); \\r
-     SendDlgItemMessage(hwnd, IDC_PPK5_FLAGS, LB_ADDSTRING, 0, (LPARAM) buf); \\r
-   }\r
-\r
-                    ADDBITFLAG(TKT_FLG_FORWARDABLE, IDS_FLG_FORWARDABLE);\r
-                    ADDBITFLAG(TKT_FLG_FORWARDED, IDS_FLG_FORWARDED);\r
-                    ADDBITFLAG(TKT_FLG_PROXIABLE, IDS_FLG_PROXIABLE);\r
-                    ADDBITFLAG(TKT_FLG_PROXY, IDS_FLG_PROXY);\r
-                    ADDBITFLAG(TKT_FLG_MAY_POSTDATE, IDS_FLG_MAY_POSTDATE);\r
-                    ADDBITFLAG(TKT_FLG_POSTDATED, IDS_FLG_POSTDATED);\r
-                    ADDBITFLAG(TKT_FLG_INVALID, IDS_FLG_INVALID);\r
-                    ADDBITFLAG(TKT_FLG_RENEWABLE, IDS_FLG_RENEWABLE);\r
-                    ADDBITFLAG(TKT_FLG_INITIAL, IDS_FLG_INITIAL);\r
-                    ADDBITFLAG(TKT_FLG_PRE_AUTH, IDS_FLG_PRE_AUTH);\r
-                    ADDBITFLAG(TKT_FLG_HW_AUTH, IDS_FLG_HW_AUTH);\r
-                    ADDBITFLAG(TKT_FLG_TRANSIT_POLICY_CHECKED, IDS_FLG_TRANSIT_POL);\r
-                    ADDBITFLAG(TKT_FLG_OK_AS_DELEGATE, IDS_FLG_OK_DELEGATE);\r
-                    ADDBITFLAG(TKT_FLG_ANONYMOUS, IDS_FLG_ANONYMOUS);\r
-\r
-#undef ADDBITFLAG\r
-\r
-                }\r
-            } else {\r
-#ifdef DEBUG\r
-                assert(FALSE);\r
-#endif\r
-            }\r
-        }\r
-        return FALSE;\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-void k5_pp_begin(khui_property_sheet * s)\r
-{\r
-    PROPSHEETPAGE *p;\r
-\r
-    if(s->credtype == credtype_id_krb5 &&\r
-       s->cred) {\r
-        p = PMALLOC(sizeof(*p));\r
-        ZeroMemory(p, sizeof(*p));\r
-\r
-        p->dwSize = sizeof(*p);\r
-        p->dwFlags = 0;\r
-        p->hInstance = hResModule;\r
-        p->pszTemplate = MAKEINTRESOURCE(IDD_PP_KRB5C);\r
-        p->pfnDlgProc = krb5_pp_proc;\r
-        p->lParam = (LPARAM) s;\r
-        khui_ps_add_page(s, credtype_id_krb5, 0, p, NULL);\r
-    }\r
-}\r
-\r
-void k5_pp_end(khui_property_sheet * s)\r
-{\r
-    khui_property_page * p = NULL;\r
-\r
-    khui_ps_find_page(s, credtype_id_krb5, &p);\r
-    if(p) {\r
-        if(p->p_page)\r
-            PFREE(p->p_page);\r
-        p->p_page = NULL;\r
-    }\r
-}\r
-\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+#ifdef DEBUG
+#include<assert.h>
+#endif
+
+/* 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];
+            wchar_t unavailable[64];
+            khm_size cbsize;
+            khm_int32 rv;
+            khm_int32 tflags;
+
+            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)
+
+            LoadString(hResModule, IDS_UNAVAILABLE,
+                       unavailable, ARRAYLENGTH(unavailable));
+
+            if(s->cred) {
+                cbsize = sizeof(buf);
+                kcdb_cred_get_name(s->cred, buf, &cbsize);
+                SetDlgItemText(hwnd, IDC_PPK5_NAME, buf);
+
+                cbsize = sizeof(buf);
+                rv = kcdb_cred_get_attr_string(s->cred, 
+                                               KCDB_ATTR_ISSUE, 
+                                               buf, &cbsize, 0);
+                if (KHM_SUCCEEDED(rv))
+                    SetDlgItemText(hwnd, IDC_PPK5_ISSUE, buf);
+                else
+                    SetDlgItemText(hwnd, IDC_PPK5_ISSUE, unavailable);
+
+                cbsize = sizeof(buf);
+                rv = kcdb_cred_get_attr_string(s->cred, 
+                                               KCDB_ATTR_EXPIRE, 
+                                               buf, &cbsize, 0);
+                if (KHM_SUCCEEDED(rv))
+                    SetDlgItemText(hwnd, IDC_PPK5_VALID, buf);
+                else
+                    SetDlgItemText(hwnd, IDC_PPK5_VALID, unavailable);
+
+                cbsize = sizeof(buf);
+                rv = kcdb_cred_get_attr_string(s->cred, 
+                                               KCDB_ATTR_RENEW_EXPIRE, 
+                                               buf, &cbsize, 0);
+                if (KHM_SUCCEEDED(rv))
+                    SetDlgItemText(hwnd, IDC_PPK5_RENEW, buf);
+                else
+                    SetDlgItemText(hwnd, IDC_PPK5_RENEW, unavailable);
+
+                tflags = 0;
+                cbsize = sizeof(tflags);
+                rv = kcdb_cred_get_attr(s->cred,
+                                        attr_id_krb5_flags,
+                                        NULL,
+                                        &tflags,
+                                        &cbsize);
+                if (KHM_SUCCEEDED(rv)) {
+
+#define ADDBITFLAG(f,s) \
+   if (tflags & f) {    \
+     LoadString(hResModule, s, buf, ARRAYLENGTH(buf)); \
+     SendDlgItemMessage(hwnd, IDC_PPK5_FLAGS, LB_ADDSTRING, 0, (LPARAM) buf); \
+   }
+
+                    ADDBITFLAG(TKT_FLG_FORWARDABLE, IDS_FLG_FORWARDABLE);
+                    ADDBITFLAG(TKT_FLG_FORWARDED, IDS_FLG_FORWARDED);
+                    ADDBITFLAG(TKT_FLG_PROXIABLE, IDS_FLG_PROXIABLE);
+                    ADDBITFLAG(TKT_FLG_PROXY, IDS_FLG_PROXY);
+                    ADDBITFLAG(TKT_FLG_MAY_POSTDATE, IDS_FLG_MAY_POSTDATE);
+                    ADDBITFLAG(TKT_FLG_POSTDATED, IDS_FLG_POSTDATED);
+                    ADDBITFLAG(TKT_FLG_INVALID, IDS_FLG_INVALID);
+                    ADDBITFLAG(TKT_FLG_RENEWABLE, IDS_FLG_RENEWABLE);
+                    ADDBITFLAG(TKT_FLG_INITIAL, IDS_FLG_INITIAL);
+                    ADDBITFLAG(TKT_FLG_PRE_AUTH, IDS_FLG_PRE_AUTH);
+                    ADDBITFLAG(TKT_FLG_HW_AUTH, IDS_FLG_HW_AUTH);
+                    ADDBITFLAG(TKT_FLG_TRANSIT_POLICY_CHECKED, IDS_FLG_TRANSIT_POL);
+                    ADDBITFLAG(TKT_FLG_OK_AS_DELEGATE, IDS_FLG_OK_DELEGATE);
+                    ADDBITFLAG(TKT_FLG_ANONYMOUS, IDS_FLG_ANONYMOUS);
+
+#undef ADDBITFLAG
+
+                }
+            } else {
+#ifdef DEBUG
+                assert(FALSE);
+#endif
+            }
+        }
+        return FALSE;
+    }
+
+    return FALSE;
+}
+
+void k5_pp_begin(khui_property_sheet * s)
+{
+    PROPSHEETPAGE *p;
+
+    if(s->credtype == credtype_id_krb5 &&
+       s->cred) {
+        p = PMALLOC(sizeof(*p));
+        ZeroMemory(p, sizeof(*p));
+
+        p->dwSize = sizeof(*p);
+        p->dwFlags = 0;
+        p->hInstance = hResModule;
+        p->pszTemplate = MAKEINTRESOURCE(IDD_PP_KRB5C);
+        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)
+            PFREE(p->p_page);
+        p->p_page = NULL;
+    }
+}
+
index d552fd0c8e555ebfb8e9863e32bafefa4571eb4f..0048f7185e14c0ab1e6a115c3d64797c5db1f899 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KRBAFSCRED_H\r
-#define __KHIMAIRA_KRBAFSCRED_H\r
-\r
-#include<windows.h>\r
-\r
-/* While we generally pull resources out of hResModule, the message\r
-   strings for all the languages are kept in the main DLL. */\r
-#define KHERR_HMODULE hInstance\r
-#define KHERR_FACILITY k5_facility\r
-#define KHERR_FACILITY_ID 64\r
-\r
-#include<netidmgr.h>\r
-\r
-#include<krb5funcs.h>\r
-#include<krb5common.h>\r
-#include<errorfuncs.h>\r
-#include<dynimport.h>\r
-\r
-#include<langres.h>\r
-#include<datarep.h>\r
-#include<krb5_msgs.h>\r
-\r
-typedef enum tag_k5_lsa_import {\r
-    K5_LSAIMPORT_NEVER = 0,\r
-    K5_LSAIMPORT_ALWAYS = 1,\r
-    K5_LSAIMPORT_MATCH = 2,     /* only when the principal name matches */\r
-} k5_lsa_import;\r
-\r
-#define TYPENAME_ENCTYPE        L"EncType"\r
-#define TYPENAME_ADDR_LIST      L"AddrList"\r
-#define TYPENAME_KRB5_FLAGS     L"Krb5Flags"\r
-#define TYPENAME_KRB5_PRINC     L"Krb5Principal"\r
-#define TYPENAME_KVNO           L"Kvno"\r
-\r
-#define ATTRNAME_KEY_ENCTYPE    L"KeyEncType"\r
-#define ATTRNAME_TKT_ENCTYPE    L"TktEncType"\r
-#define ATTRNAME_ADDR_LIST      L"AddrList"\r
-#define ATTRNAME_KRB5_FLAGS     L"Krb5Flags"\r
-#define ATTRNAME_KRB5_CCNAME    L"Krb5CCName"\r
-#define ATTRNAME_KVNO           L"Kvno"\r
-#define ATTRNAME_KRB5_IDFLAGS   L"Krb5IDFlags"\r
-\r
-/* Flag bits for Krb5IDFlags property */\r
-\r
-/* identity was imported from MSLSA: */\r
-#define K5IDFLAG_IMPORTED       0x00000001\r
-\r
-void init_krb();\r
-void exit_krb();\r
-KHMEXP khm_int32 KHMAPI init_module(kmm_module h_module);\r
-KHMEXP khm_int32 KHMAPI exit_module(kmm_module h_module);\r
-\r
-/* globals */\r
-extern kmm_module h_khModule;\r
-extern HMODULE hResModule;\r
-extern HINSTANCE hInstance;\r
-extern const wchar_t * k5_facility;\r
-\r
-extern khm_int32 type_id_enctype;\r
-extern khm_int32 type_id_addr_list;\r
-extern khm_int32 type_id_krb5_flags;\r
-extern khm_int32 type_id_krb5_princ;\r
-extern khm_int32 type_id_kvno;\r
-\r
-extern BOOL      type_regd_krb5_princ;\r
-\r
-extern khm_int32 attr_id_key_enctype;\r
-extern khm_int32 attr_id_tkt_enctype;\r
-extern khm_int32 attr_id_addr_list;\r
-extern khm_int32 attr_id_krb5_flags;\r
-extern khm_int32 attr_id_krb5_ccname;\r
-extern khm_int32 attr_id_kvno;\r
-extern khm_int32 attr_id_krb5_idflags;\r
-\r
-extern khm_ui_4  k5_commctl_version;\r
-\r
-#define IS_COMMCTL6() (k5_commctl_version >= 0x60000)\r
-\r
-/* Configuration spaces */\r
-#define CSNAME_KRB5CRED      L"Krb5Cred"\r
-#define CSNAME_PARAMS        L"Parameters"\r
-#define CSNAME_PROMPTCACHE   L"PromptCache"\r
-#define CSNAME_REALMS        L"Realms"\r
-\r
-/* plugin constants */\r
-#define KRB5_PLUGIN_NAME    L"Krb5Cred"\r
-#define KRB5_IDENTPRO_NAME  L"Krb5Ident"\r
-\r
-#define KRB5_CREDTYPE_NAME  L"Krb5Cred"\r
-\r
-/* limits */\r
-/* maximum number of characters in a realm name */\r
-#define K5_MAXCCH_REALM 256\r
-\r
-/* maximum number of characters in a host name */\r
-#define K5_MAXCCH_HOST  128\r
-\r
-/* maximum number of KDC's per realm */\r
-#define K5_MAX_KDC      64\r
-\r
-/* maximum number of domains that map to a realm */\r
-#define K5_MAX_DOMAIN_MAPPINGS 32\r
-\r
-extern khm_handle csp_plugins;\r
-extern khm_handle csp_krbcred;\r
-extern khm_handle csp_params;\r
-\r
-extern kconf_schema schema_krbconfig[];\r
-\r
-/* other globals */\r
-extern khm_int32 credtype_id_krb5;\r
-\r
-extern khm_boolean krb5_initialized;\r
-\r
-extern khm_handle krb5_credset;\r
-\r
-extern khm_handle k5_sub;\r
-\r
-extern krb5_context k5_identpro_ctx;\r
-\r
-extern BOOL is_k5_identpro;\r
-\r
-/* plugin callbacks */\r
-khm_int32 KHMAPI k5_msg_callback(khm_int32 msg_type, khm_int32 msg_subtype, khm_ui_4 uparam, void * vparam);\r
-khm_int32 KHMAPI k5_ident_callback(khm_int32 msg_type, khm_int32 msg_subtype, khm_ui_4 uparam, void * vparam);\r
-\r
-/* kinit fiber */\r
-typedef struct _fiber_job_t {\r
-    int     command;\r
-\r
-    khui_new_creds * nc;\r
-    khui_new_creds_by_type * nct;\r
-    HWND    dialog;\r
-\r
-    khm_handle identity;\r
-    char *  principal;\r
-    char *  password;\r
-    char *  ccache;\r
-    krb5_deltat lifetime;\r
-    DWORD   forwardable;\r
-    DWORD   proxiable;\r
-    DWORD   renewable;\r
-    krb5_deltat renew_life;\r
-    DWORD   addressless;\r
-    DWORD   publicIP;\r
-\r
-    int     code;\r
-    int     state;\r
-    int     prompt_set;\r
-\r
-    BOOL    null_password;\r
-    BOOL    valid_principal;\r
-    BOOL    retry_if_valid_principal;\r
-} fiber_job;\r
-\r
-extern fiber_job g_fjob;   /* global fiber job object */\r
-\r
-#define FIBER_CMD_KINIT     1\r
-#define FIBER_CMD_CANCEL    2\r
-#define FIBER_CMD_CONTINUE  3\r
-\r
-#define FIBER_STATE_NONE          0\r
-#define FIBER_STATE_KINIT         1\r
-#define FIBER_STATE_RETRY_KINIT   2\r
-\r
-#define K5_SET_CRED_MSG     WMNC_USER\r
-\r
-void \r
-k5_pp_begin(khui_property_sheet * s);\r
-\r
-void \r
-k5_pp_end(khui_property_sheet * s);\r
-\r
-khm_int32 KHMAPI \r
-k5_msg_cred_dialog(khm_int32 msg_type, \r
-                   khm_int32 msg_subtype, \r
-                   khm_ui_4 uparam, \r
-                   void * vparam);\r
-\r
-khm_int32 KHMAPI \r
-k5_msg_ident(khm_int32 msg_type, \r
-               khm_int32 msg_subtype, \r
-               khm_ui_4 uparam, \r
-               void * vparam);\r
-\r
-khm_int32\r
-k5_remove_from_LRU(khm_handle identity);\r
-\r
-int \r
-k5_get_realm_from_nc(khui_new_creds * nc, \r
-                     wchar_t * buf, \r
-                     khm_size cch_buf);\r
-\r
-void\r
-k5_register_config_panels(void);\r
-\r
-void\r
-k5_unregister_config_panels(void);\r
-\r
-INT_PTR CALLBACK \r
-k5_ccconfig_dlgproc(HWND hwnd,\r
-                    UINT uMsg,\r
-                    WPARAM wParam,\r
-                    LPARAM lParam);\r
-\r
-INT_PTR CALLBACK \r
-k5_id_tab_dlgproc(HWND hwndDlg,\r
-                  UINT uMsg,\r
-                  WPARAM wParam,\r
-                  LPARAM lParam);\r
-\r
-INT_PTR CALLBACK \r
-k5_ids_tab_dlgproc(HWND hwnd,\r
-                   UINT uMsg,\r
-                   WPARAM wParam,\r
-                   LPARAM lParam);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<netidmgr.h>
+
+#include<krb5funcs.h>
+#include<krb5common.h>
+#include<errorfuncs.h>
+#include<dynimport.h>
+
+#include<langres.h>
+#include<datarep.h>
+#include<krb5_msgs.h>
+
+typedef enum tag_k5_lsa_import {
+    K5_LSAIMPORT_NEVER = 0,
+    K5_LSAIMPORT_ALWAYS = 1,
+    K5_LSAIMPORT_MATCH = 2,     /* only when the principal name matches */
+} k5_lsa_import;
+
+#define TYPENAME_ENCTYPE        L"EncType"
+#define TYPENAME_ADDR_LIST      L"AddrList"
+#define TYPENAME_KRB5_FLAGS     L"Krb5Flags"
+#define TYPENAME_KRB5_PRINC     L"Krb5Principal"
+#define TYPENAME_KVNO           L"Kvno"
+
+#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"
+#define ATTRNAME_KVNO           L"Kvno"
+#define ATTRNAME_KRB5_IDFLAGS   L"Krb5IDFlags"
+
+/* Flag bits for Krb5IDFlags property */
+
+/* identity was imported from MSLSA: */
+#define K5IDFLAG_IMPORTED       0x00000001
+
+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 type_id_krb5_princ;
+extern khm_int32 type_id_kvno;
+
+extern BOOL      type_regd_krb5_princ;
+
+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;
+extern khm_int32 attr_id_kvno;
+extern khm_int32 attr_id_krb5_idflags;
+
+extern khm_ui_4  k5_commctl_version;
+
+#define IS_COMMCTL6() (k5_commctl_version >= 0x60000)
+
+/* Configuration spaces */
+#define CSNAME_KRB5CRED      L"Krb5Cred"
+#define CSNAME_PARAMS        L"Parameters"
+#define CSNAME_PROMPTCACHE   L"PromptCache"
+#define CSNAME_REALMS        L"Realms"
+
+/* plugin constants */
+#define KRB5_PLUGIN_NAME    L"Krb5Cred"
+#define KRB5_IDENTPRO_NAME  L"Krb5Ident"
+
+#define KRB5_CREDTYPE_NAME  L"Krb5Cred"
+
+/* limits */
+/* maximum number of characters in a realm name */
+#define K5_MAXCCH_REALM 256
+
+/* maximum number of characters in a host name */
+#define K5_MAXCCH_HOST  128
+
+/* maximum number of KDC's per realm */
+#define K5_MAX_KDC      64
+
+/* maximum number of domains that map to a realm */
+#define K5_MAX_DOMAIN_MAPPINGS 32
+
+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);
+khm_int32 KHMAPI k5_ident_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;
+    BOOL    valid_principal;
+    BOOL    retry_if_valid_principal;
+} 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
+#define FIBER_STATE_RETRY_KINIT   2
+
+#define K5_SET_CRED_MSG     WMNC_USER
+
+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);
+
+khm_int32
+k5_remove_from_LRU(khm_handle identity);
+
+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);
+
+INT_PTR CALLBACK 
+k5_ccconfig_dlgproc(HWND hwnd,
+                    UINT uMsg,
+                    WPARAM wParam,
+                    LPARAM lParam);
+
+INT_PTR CALLBACK 
+k5_id_tab_dlgproc(HWND hwndDlg,
+                  UINT uMsg,
+                  WPARAM wParam,
+                  LPARAM lParam);
+
+INT_PTR CALLBACK 
+k5_ids_tab_dlgproc(HWND hwnd,
+                   UINT uMsg,
+                   WPARAM wParam,
+                   LPARAM lParam);
+
+#endif
index 0e62ecda1d0c3d65e7d3f51d3aa29baa41f91d6e..117754b3e8e913dcc4d35131c1666e2a11ecadc4 100644 (file)
-//{{NO_DEPENDENCIES}}\r
-// Microsoft Visual C++ generated include file.\r
-// Used by C:\work\pismere\athena\auth\krb5\src\windows\identity\plugins\krb5\lang\en_us\langres.rc\r
-//\r
-#define IDS_UNK_ADDR_FMT                101\r
-#define IDD_NC_KRB5                     102\r
-#define IDS_KRB5_CREDTEXT_0             102\r
-#define IDS_KRB5_CCNAME_SHORT_DESC      103\r
-#define IDS_KEY_ENCTYPE_SHORT_DESC      104\r
-#define IDD_CONFIG                      104\r
-#define IDS_TKT_ENCTYPE_SHORT_DESC      105\r
-#define IDD_CFG_REALMS                  105\r
-#define IDS_KEY_ENCTYPE_LONG_DESC       106\r
-#define IDD_CFG_IDS_TAB                 106\r
-#define IDS_TKT_ENCTYPE_LONG_DESC       107\r
-#define IDD_PP_KRB5C                    107\r
-#define IDS_ADDR_LIST_SHORT_DESC        108\r
-#define IDD_PP_KRB5                     108\r
-#define IDS_ADDR_LIST_LONG_DESC         109\r
-#define IDD_CFG_ID_TAB                  109\r
-#define IDS_ETYPE_NULL                  110\r
-#define IDD_NC_KRB5_PASSWORD            110\r
-#define IDS_ETYPE_DES_CBC_CRC           111\r
-#define IDD_CFG_CACHES                  111\r
-#define IDS_ETYPE_DES_CBC_MD4           112\r
-#define IDI_PLUGIN                      112\r
-#define IDS_ETYPE_DES_CBC_MD5           113\r
-#define IDI_DELETED                     113\r
-#define IDS_ETYPE_DES_CBC_RAW           114\r
-#define IDI_NEW                         114\r
-#define IDS_ETYPE_DES3_CBC_SHA          115\r
-#define IDI_NORMAL                      115\r
-#define IDS_ETYPE_DES3_CBC_RAW          116\r
-#define IDI_MODIFIED                    116\r
-#define IDS_ETYPE_DES_HMAC_SHA1         117\r
-#define IDS_ETYPE_DES3_CBC_SHA1         118\r
-#define IDS_ETYPE_AES128_CTS_HMAC_SHA1_96 119\r
-#define IDS_ETYPE_AES256_CTS_HMAC_SHA1_96 120\r
-#define IDS_ETYPE_ARCFOUR_HMAC          121\r
-#define IDS_ETYPE_ARCFOUR_HMAC_EXP      122\r
-#define IDS_ETYPE_UNKNOWN               123\r
-#define IDS_ETYPE_LOCAL_DES3_HMAC_SHA1  124\r
-#define IDS_ETYPE_LOCAL_RC4_MD4         125\r
-#define IDS_KRB5_SHORT_DESC             126\r
-#define IDS_KRB5_LONG_DESC              127\r
-#define IDS_KRB4_SHORT_DESC             128\r
-#define IDS_KRB4_LONG_DESC              129\r
-#define IDS_KRB5_FLAGS_SHORT_DESC       130\r
-#define IDS_RENEW_TILL_SHORT_DESC       131\r
-#define IDS_RENEW_TILL_LONG_DESC        132\r
-#define IDS_RENEW_FOR_SHORT_DESC        133\r
-#define IDS_RENEW_FOR_LONG_DESC         134\r
-#define IDS_KRB5_CCNAME_LONG_DESC       135\r
-#define IDS_NC_USERNAME                 136\r
-#define IDS_NC_REALM                    137\r
-#define IDS_KRB5_WARNING                138\r
-#define IDS_K5ERR_NAME_EXPIRED          139\r
-#define IDS_K5ERR_KEY_EXPIRED           140\r
-#define IDS_KRB5_WARN_FMT               141\r
-#define IDS_K5ERR_FMT                   142\r
-#define IDS_K5CFG_SHORT_DESC            143\r
-#define IDS_K5CFG_LONG_DESC             144\r
-#define IDS_K5RLM_SHORT_DESC            145\r
-#define IDS_K5RLM_LONG_DESC             146\r
-#define IDS_K5CFG_IDS_SHORT_DESC        147\r
-#define IDS_K5CFG_IDS_LONG_DESC         148\r
-#define IDS_K5CFG_ID_SHORT_DESC         149\r
-#define IDS_K5CFG_ID_LONG_DESC          150\r
-#define IDS_PLUGIN_DESC                 151\r
-#define IDS_NC_PWD_BANNER               152\r
-#define IDS_NC_PWD_PWD                  153\r
-#define IDS_NC_PWD_NPWD                 154\r
-#define IDS_NC_PWD_NPWD_AGAIN           155\r
-#define IDS_KRB5_CREDTEXT_P0            156\r
-#define IDS_K5CFG_IMPORT_OPTIONS        157\r
-#define IDS_IDENTPRO_DESC               158\r
-#define IDS_K5CCC_SHORT_DESC            159\r
-#define IDS_K5CCC_LONG_DESC             160\r
-#define IDS_CFG_FCTITLE                 161\r
-#define IDS_CFG_FCN_WARNING             162\r
-#define IDS_CFG_FCN_W_NOTFOUND          163\r
-#define IDS_CFG_FCN_W_RELATIVE          164\r
-#define IDS_CFG_FCOPENTITLE             165\r
-#define IDS_UNAVAILABLE                 166\r
-#define IDS_FLG_FORWARDABLE             167\r
-#define IDS_FLG_FORWARDED               168\r
-#define IDS_FLG_PROXIABLE               169\r
-#define IDS_FLG_PROXY                   170\r
-#define IDS_FLG_MAY_POSTDATE            171\r
-#define IDS_FLG_POSTDATED               172\r
-#define IDS_FLG_INVALID                 173\r
-#define IDS_FLG_RENEWABLE               174\r
-#define IDS_FLG_INITIAL                 175\r
-#define IDS_FLG_PRE_AUTH                176\r
-#define IDS_FLG_HW_AUTH                 177\r
-#define IDS_FLG_TRANSIT_POL             178\r
-#define IDS_FLG_OK_DELEGATE             179\r
-#define IDS_FLG_ANONYMOUS               180\r
-#define IDS_K5ERR_CANTWRITEPROFILE      181\r
-#define IDS_K5ERR_PROFNOWRITE           182\r
-#define IDS_K5ERR_PROFUSETEMP           183\r
-#define IDS_K5ERR_PROFSUGGEST           184\r
-#define IDS_CFG_RE_REALMS               185\r
-#define IDS_CFG_RE_KDCS                 186\r
-#define IDS_CFG_RE_DMAPS                187\r
-#define IDS_CFG_RE_KDCS_R               188\r
-#define IDS_CFG_RE_DMAPS_R              189\r
-#define IDS_CFG_RE_HEAD_SVR             190\r
-#define IDS_CFG_RE_HEAD_ADMIN           191\r
-#define IDS_CFG_RE_HEAD_MASTER          192\r
-#define IDS_CFG_RE_HEAD_DOMAIN          193\r
-#define IDS_CFG_RE_NEWREALM             194\r
-#define IDS_YES                         195\r
-#define IDS_NO                          196\r
-#define IDS_CFG_RE_NEWSERVER            197\r
-#define IDS_CFG_RE_NEWDMAP              198\r
-#define IDS_KRB5_NC_NAME                199\r
-#define IDS_NCERR_IDENT_TOO_LONG        200\r
-#define IDS_NCERR_IDENT_INVALID         201\r
-#define IDS_NCERR_IDENT_UNKNOWN         202\r
-#define IDS_CFG_RE_ARNUT                203\r
-#define IDS_CFG_RE_ARNUM                204\r
-#define IDS_CFG_RE_ASNUT                205\r
-#define IDS_CFG_RE_ASNUM                206\r
-#define IDS_CFG_RE_DMNUT                207\r
-#define IDS_CFG_RE_DMNUM                208\r
-#define IDS_CFG_RE_MNR                  209\r
-#define IDS_CFG_RE_MDR                  210\r
-#define IDS_CFG_RE_MNK                  211\r
-#define IDS_CFG_RE_MDK                  212\r
-#define IDS_CFG_RE_MAK                  213\r
-#define IDS_CFG_RE_MMK                  214\r
-#define IDS_CFG_RE_MND                  215\r
-#define IDS_CFG_RE_MDD                  216\r
-#define IDS_KVNO_SHORT_DESC             217\r
-#define IDS_KVNO_LONG_DESC              218\r
-#define IDC_NCK5_RENEWABLE              1002\r
-#define IDC_NCK5_FORWARDABLE            1004\r
-#define IDC_NCK5_REALM                  1005\r
-#define IDC_NCK5_ADD_REALMS             1006\r
-#define IDC_NCK5_LIFETIME_EDIT          1008\r
-#define IDC_NCK5_RENEW_EDIT             1009\r
-#define IDC_PPK5_CRENEW                 1014\r
-#define IDC_PPK5_CFORWARD               1015\r
-#define IDC_PPK5_CPROXY                 1016\r
-#define IDC_PPK5_NAME                   1017\r
-#define IDC_PPK5_ISSUE                  1018\r
-#define IDC_PPK5_VALID                  1019\r
-#define IDC_PPK5_RENEW                  1020\r
-#define IDC_CHECK2                      1022\r
-#define IDC_CHECK4                      1024\r
-#define IDC_PPK5_LIFETIME               1024\r
-#define IDC_CHECK5                      1025\r
-#define IDC_CFG_LBL_REALM               1025\r
-#define IDC_CFG_DEFREALM                1026\r
-#define IDC_CFG_LBL_CFGFILE             1029\r
-#define IDC_CFG_CFGFILE                 1030\r
-#define IDC_CFG_WINGRP                  1031\r
-#define IDC_LBL_IMPORT                  1032\r
-#define IDC_CFG_IMPORT                  1033\r
-#define IDC_CFG_LBL_HOSTNAME            1034\r
-#define IDC_CFG_HOSTNAME                1035\r
-#define IDC_CFG_LBL_DOMAIN              1036\r
-#define IDC_CFG_DOMAIN                  1037\r
-#define IDC_CFG_CREATECONFIG            1038\r
-#define IDC_CFG_BROWSE                  1039\r
-#define IDC_CFG_CFGFILEGRP              1040\r
-#define IDC_CFG_CFGREALMS               1041\r
-#define IDC_CFG_BROWSE2                 1042\r
-#define IDC_CFG_REALMS                  1044\r
-#define IDC_CFG_DOMAINGRP               1045\r
-#define IDC_CFG_SERVERSGRP              1046\r
-#define IDC_LIST3                       1047\r
-#define IDC_CFG_KDC                     1047\r
-#define IDC_LIST4                       1048\r
-#define IDC_CFG_DMAP                    1048\r
-#define IDC_CFG_LBL_DEFLIFE             1049\r
-#define IDC_CFG_DEFLIFE                 1050\r
-#define IDC_CFG_LBL_DEFRLIFE            1051\r
-#define IDC_CFG_DEFRLIFE                1052\r
-#define IDC_CFG_LIFEGRP                 1053\r
-#define IDC_CFG_LRNG_MIN                1054\r
-#define IDC_CFG_LRNG_MAX                1055\r
-#define IDC_CFG_RLRNG_MIN               1056\r
-#define IDC_CFG_RLRNG_MAX               1057\r
-#define IDC_CFG_CCACHE                  1058\r
-#define IDC_CFG_FCGRP                   1059\r
-#define IDC_CFG_FCLIST                  1060\r
-#define IDC_CFG_FCNAME                  1062\r
-#define IDC_CFG_ADD                     1064\r
-#define IDC_CFG_REMOVE                  1065\r
-#define IDC_CFG_INCAPI                  1066\r
-#define IDC_CFG_INCMSLSA                1067\r
-#define IDC_PPK5_FLAGS                  1072\r
-#define IDC_CFG_INCREALMS               1073\r
-#define IDC_NCK5_ADDRESS                1074\r
-#define IDC_IPADDRESS1                  1075\r
-#define IDC_NCK5_PUBLICIP               1075\r
-#define IDC_CFG_PUBLICIP                1075\r
-#define IDC_CFG_RENEW                   1076\r
-#define IDC_CHECK3                      1077\r
-#define IDC_CFG_ADDRESSLESS             1077\r
-#define IDC_CFG_FORWARD                 1078\r
-#define IDC_CHECK1                      1079\r
-#define ID_FOO_BAR                      40001\r
-\r
-// Next default values for new objects\r
-// \r
-#ifdef APSTUDIO_INVOKED\r
-#ifndef APSTUDIO_READONLY_SYMBOLS\r
-#define _APS_NEXT_RESOURCE_VALUE        219\r
-#define _APS_NEXT_COMMAND_VALUE         40002\r
-#define _APS_NEXT_CONTROL_VALUE         1080\r
-#define _APS_NEXT_SYMED_VALUE           101\r
-#endif\r
-#endif\r
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by C:\work\pismere\athena\auth\krb5\src\windows\identity\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 IDD_CFG_CACHES                  111
+#define IDS_ETYPE_DES_CBC_MD4           112
+#define IDI_PLUGIN                      112
+#define IDS_ETYPE_DES_CBC_MD5           113
+#define IDI_DELETED                     113
+#define IDS_ETYPE_DES_CBC_RAW           114
+#define IDI_NEW                         114
+#define IDS_ETYPE_DES3_CBC_SHA          115
+#define IDI_NORMAL                      115
+#define IDS_ETYPE_DES3_CBC_RAW          116
+#define IDI_MODIFIED                    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 IDS_IDENTPRO_DESC               158
+#define IDS_K5CCC_SHORT_DESC            159
+#define IDS_K5CCC_LONG_DESC             160
+#define IDS_CFG_FCTITLE                 161
+#define IDS_CFG_FCN_WARNING             162
+#define IDS_CFG_FCN_W_NOTFOUND          163
+#define IDS_CFG_FCN_W_RELATIVE          164
+#define IDS_CFG_FCOPENTITLE             165
+#define IDS_UNAVAILABLE                 166
+#define IDS_FLG_FORWARDABLE             167
+#define IDS_FLG_FORWARDED               168
+#define IDS_FLG_PROXIABLE               169
+#define IDS_FLG_PROXY                   170
+#define IDS_FLG_MAY_POSTDATE            171
+#define IDS_FLG_POSTDATED               172
+#define IDS_FLG_INVALID                 173
+#define IDS_FLG_RENEWABLE               174
+#define IDS_FLG_INITIAL                 175
+#define IDS_FLG_PRE_AUTH                176
+#define IDS_FLG_HW_AUTH                 177
+#define IDS_FLG_TRANSIT_POL             178
+#define IDS_FLG_OK_DELEGATE             179
+#define IDS_FLG_ANONYMOUS               180
+#define IDS_K5ERR_CANTWRITEPROFILE      181
+#define IDS_K5ERR_PROFNOWRITE           182
+#define IDS_K5ERR_PROFUSETEMP           183
+#define IDS_K5ERR_PROFSUGGEST           184
+#define IDS_CFG_RE_REALMS               185
+#define IDS_CFG_RE_KDCS                 186
+#define IDS_CFG_RE_DMAPS                187
+#define IDS_CFG_RE_KDCS_R               188
+#define IDS_CFG_RE_DMAPS_R              189
+#define IDS_CFG_RE_HEAD_SVR             190
+#define IDS_CFG_RE_HEAD_ADMIN           191
+#define IDS_CFG_RE_HEAD_MASTER          192
+#define IDS_CFG_RE_HEAD_DOMAIN          193
+#define IDS_CFG_RE_NEWREALM             194
+#define IDS_YES                         195
+#define IDS_NO                          196
+#define IDS_CFG_RE_NEWSERVER            197
+#define IDS_CFG_RE_NEWDMAP              198
+#define IDS_KRB5_NC_NAME                199
+#define IDS_NCERR_IDENT_TOO_LONG        200
+#define IDS_NCERR_IDENT_INVALID         201
+#define IDS_NCERR_IDENT_UNKNOWN         202
+#define IDS_CFG_RE_ARNUT                203
+#define IDS_CFG_RE_ARNUM                204
+#define IDS_CFG_RE_ASNUT                205
+#define IDS_CFG_RE_ASNUM                206
+#define IDS_CFG_RE_DMNUT                207
+#define IDS_CFG_RE_DMNUM                208
+#define IDS_CFG_RE_MNR                  209
+#define IDS_CFG_RE_MDR                  210
+#define IDS_CFG_RE_MNK                  211
+#define IDS_CFG_RE_MDK                  212
+#define IDS_CFG_RE_MAK                  213
+#define IDS_CFG_RE_MMK                  214
+#define IDS_CFG_RE_MND                  215
+#define IDS_CFG_RE_MDD                  216
+#define IDS_KVNO_SHORT_DESC             217
+#define IDS_KVNO_LONG_DESC              218
+#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_BROWSE2                 1042
+#define IDC_CFG_REALMS                  1044
+#define IDC_CFG_DOMAINGRP               1045
+#define IDC_CFG_SERVERSGRP              1046
+#define IDC_LIST3                       1047
+#define IDC_CFG_KDC                     1047
+#define IDC_LIST4                       1048
+#define IDC_CFG_DMAP                    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
+#define IDC_CFG_FCGRP                   1059
+#define IDC_CFG_FCLIST                  1060
+#define IDC_CFG_FCNAME                  1062
+#define IDC_CFG_ADD                     1064
+#define IDC_CFG_REMOVE                  1065
+#define IDC_CFG_INCAPI                  1066
+#define IDC_CFG_INCMSLSA                1067
+#define IDC_PPK5_FLAGS                  1072
+#define IDC_CFG_INCREALMS               1073
+#define IDC_NCK5_ADDRESS                1074
+#define IDC_IPADDRESS1                  1075
+#define IDC_NCK5_PUBLICIP               1075
+#define IDC_CFG_PUBLICIP                1075
+#define IDC_CFG_RENEW                   1076
+#define IDC_CHECK3                      1077
+#define IDC_CFG_ADDRESSLESS             1077
+#define IDC_CFG_FORWARD                 1078
+#define IDC_CHECK1                      1079
+#define ID_FOO_BAR                      40001
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        219
+#define _APS_NEXT_COMMAND_VALUE         40002
+#define _APS_NEXT_CONTROL_VALUE         1080
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
index ed5b3e4c3d8e4c05af43cc26bfe1ad856eda8b93..8500162669f40e9b0f5b114682d9f6aa233849fe 100644 (file)
-/*\r
- * Copyright (c) 2006 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include "credprov.h"\r
-#include <assert.h>\r
-\r
-/* Dialog procedures and support functions for handling configuration\r
-   dialogs for per-identity configuration. When the configuration\r
-   dialog is activated, an instance of this dialog will be created for\r
-   each identity that the user touches. */\r
-\r
-/* The structure that we use to hold state information for the\r
-   dialog. */\r
-typedef struct tag_config_id_dlg_data {\r
-    khui_config_init_data cfg;  /* instance information for this\r
-                                   dialog */\r
-\r
-    khm_handle ident;           /* handle to the identity for this\r
-                                   dialog */\r
-\r
-    /* TODO: Add any fields for holding state here */\r
-} config_id_dlg_data;\r
-\r
-INT_PTR CALLBACK\r
-config_id_dlgproc(HWND hwnd,\r
-                  UINT uMsg,\r
-                  WPARAM wParam,\r
-                  LPARAM lParam) {\r
-\r
-    config_id_dlg_data * d;\r
-\r
-    switch (uMsg) {\r
-    case WM_INITDIALOG:\r
-        {\r
-            wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-            khm_size cb;\r
-            khm_int32 rv;\r
-\r
-            d = malloc(sizeof(*d));\r
-            assert(d);\r
-            ZeroMemory(d, sizeof(*d));\r
-\r
-            /* for subpanels, lParam is a pointer to a\r
-               khui_config_init_data strucutre that provides the\r
-               instance and context information.  It's not a\r
-               persistent strucutre, so we have to make a copy. */\r
-            d->cfg = *((khui_config_init_data *) lParam);\r
-\r
-            cb = sizeof(idname);\r
-            rv = khui_cfg_get_name(d->cfg.ctx_node, idname, &cb);\r
-            assert(KHM_SUCCEEDED(rv));\r
-\r
-            rv = kcdb_identity_create(idname, 0, &d->ident);\r
-            assert(KHM_SUCCEEDED(rv));\r
-\r
-            /* TODO: perform any other required initialization */\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-            SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);\r
-#pragma warning(pop)\r
-        }\r
-        break;\r
-\r
-    case KHUI_WM_CFG_NOTIFY:\r
-        d = (config_id_dlg_data *)\r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        if (HIWORD(wParam) == WMCFG_APPLY) {\r
-            /* TODO: apply changes */\r
-\r
-            return TRUE;\r
-        }\r
-        break;\r
-\r
-    case WM_DESTROY:\r
-        {\r
-            d = (config_id_dlg_data *)\r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-            if (d) {\r
-                if (d->ident)\r
-                    kcdb_identity_release(d->ident);\r
-\r
-                /* TODO: perform any other required uninitialization */\r
-\r
-                free(d);\r
-            }\r
-        }\r
-        break;\r
-    }\r
-\r
-    return FALSE;\r
-\r
-}\r
+/*
+ * Copyright (c) 2006 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 "credprov.h"
+#include <assert.h>
+
+/* Dialog procedures and support functions for handling configuration
+   dialogs for per-identity configuration. When the configuration
+   dialog is activated, an instance of this dialog will be created for
+   each identity that the user touches. */
+
+/* The structure that we use to hold state information for the
+   dialog. */
+typedef struct tag_config_id_dlg_data {
+    khui_config_init_data cfg;  /* instance information for this
+                                   dialog */
+
+    khm_handle ident;           /* handle to the identity for this
+                                   dialog */
+
+    /* TODO: Add any fields for holding state here */
+} config_id_dlg_data;
+
+INT_PTR CALLBACK
+config_id_dlgproc(HWND hwnd,
+                  UINT uMsg,
+                  WPARAM wParam,
+                  LPARAM lParam) {
+
+    config_id_dlg_data * d;
+
+    switch (uMsg) {
+    case WM_INITDIALOG:
+        {
+            wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
+            khm_size cb;
+            khm_int32 rv;
+
+            d = malloc(sizeof(*d));
+            assert(d);
+            ZeroMemory(d, sizeof(*d));
+
+            /* for subpanels, lParam is a pointer to a
+               khui_config_init_data strucutre that provides the
+               instance and context information.  It's not a
+               persistent strucutre, so we have to make a copy. */
+            d->cfg = *((khui_config_init_data *) lParam);
+
+            cb = sizeof(idname);
+            rv = khui_cfg_get_name(d->cfg.ctx_node, idname, &cb);
+            assert(KHM_SUCCEEDED(rv));
+
+            rv = kcdb_identity_create(idname, 0, &d->ident);
+            assert(KHM_SUCCEEDED(rv));
+
+            /* TODO: perform any other required initialization */
+
+#pragma warning(push)
+#pragma warning(disable: 4244)
+            SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);
+#pragma warning(pop)
+        }
+        break;
+
+    case KHUI_WM_CFG_NOTIFY:
+        d = (config_id_dlg_data *)
+            GetWindowLongPtr(hwnd, DWLP_USER);
+
+        if (HIWORD(wParam) == WMCFG_APPLY) {
+            /* TODO: apply changes */
+
+            return TRUE;
+        }
+        break;
+
+    case WM_DESTROY:
+        {
+            d = (config_id_dlg_data *)
+                GetWindowLongPtr(hwnd, DWLP_USER);
+
+            if (d) {
+                if (d->ident)
+                    kcdb_identity_release(d->ident);
+
+                /* TODO: perform any other required uninitialization */
+
+                free(d);
+            }
+        }
+        break;
+    }
+
+    return FALSE;
+
+}
index 4139512079d55b74454acc300d2412e7e2d2e03d..8d6f0081aad6d3622a968610196cc2d5cf761d31 100644 (file)
@@ -1,96 +1,96 @@
-/*\r
- * Copyright (c) 2006 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include "credprov.h"\r
-#include<assert.h>\r
-\r
-/* Dialog procedures and support functions for handling configuration\r
-   dialogs for all identities. */\r
-\r
-/* The structure that we use to hold state information for the\r
-   dialog. */\r
-typedef struct tag_config_ids_dlg_data {\r
-    khui_config_init_data cfg;  /* instance information for this\r
-                                   dialog */\r
-\r
-    /* TODO: Add any fields for holding state here */\r
-} config_ids_dlg_data;\r
-\r
-INT_PTR CALLBACK\r
-config_ids_dlgproc(HWND hwnd,\r
-                   UINT uMsg,\r
-                   WPARAM wParam,\r
-                   LPARAM lParam) {\r
-\r
-    config_ids_dlg_data * d;\r
-\r
-    switch (uMsg) {\r
-    case WM_INITDIALOG:\r
-        {\r
-            d = malloc(sizeof(*d));\r
-            assert(d);\r
-            ZeroMemory(d, sizeof(*d));\r
-\r
-            /* for subpanels, lParam is a pointer to a\r
-               khui_config_init_data strucutre that provides the\r
-               instance and context information.  It's not a\r
-               persistent strucutre, so we have to make a copy. */\r
-            d->cfg = *((khui_config_init_data *) lParam);\r
-\r
-            /* TODO: perform any additional initialization */\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-            SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);\r
-#pragma warning(pop)\r
-        }\r
-        break;\r
-\r
-    case KHUI_WM_CFG_NOTIFY:\r
-        d = (config_ids_dlg_data *)\r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        if (HIWORD(wParam) == WMCFG_APPLY) {\r
-            /* TODO: apply changes */\r
-\r
-            return TRUE;\r
-        }\r
-        break;\r
-\r
-    case WM_DESTROY:\r
-        d = (config_ids_dlg_data *)\r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        if (d) {\r
-            /* TODO: Perform any additional uninitialization */\r
-\r
-            free (d);\r
-        }\r
-        break;\r
-    }\r
-\r
-    return FALSE;\r
-}\r
+/*
+ * Copyright (c) 2006 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 "credprov.h"
+#include<assert.h>
+
+/* Dialog procedures and support functions for handling configuration
+   dialogs for all identities. */
+
+/* The structure that we use to hold state information for the
+   dialog. */
+typedef struct tag_config_ids_dlg_data {
+    khui_config_init_data cfg;  /* instance information for this
+                                   dialog */
+
+    /* TODO: Add any fields for holding state here */
+} config_ids_dlg_data;
+
+INT_PTR CALLBACK
+config_ids_dlgproc(HWND hwnd,
+                   UINT uMsg,
+                   WPARAM wParam,
+                   LPARAM lParam) {
+
+    config_ids_dlg_data * d;
+
+    switch (uMsg) {
+    case WM_INITDIALOG:
+        {
+            d = malloc(sizeof(*d));
+            assert(d);
+            ZeroMemory(d, sizeof(*d));
+
+            /* for subpanels, lParam is a pointer to a
+               khui_config_init_data strucutre that provides the
+               instance and context information.  It's not a
+               persistent strucutre, so we have to make a copy. */
+            d->cfg = *((khui_config_init_data *) lParam);
+
+            /* TODO: perform any additional initialization */
+
+#pragma warning(push)
+#pragma warning(disable: 4244)
+            SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);
+#pragma warning(pop)
+        }
+        break;
+
+    case KHUI_WM_CFG_NOTIFY:
+        d = (config_ids_dlg_data *)
+            GetWindowLongPtr(hwnd, DWLP_USER);
+
+        if (HIWORD(wParam) == WMCFG_APPLY) {
+            /* TODO: apply changes */
+
+            return TRUE;
+        }
+        break;
+
+    case WM_DESTROY:
+        d = (config_ids_dlg_data *)
+            GetWindowLongPtr(hwnd, DWLP_USER);
+
+        if (d) {
+            /* TODO: Perform any additional uninitialization */
+
+            free (d);
+        }
+        break;
+    }
+
+    return FALSE;
+}
index 3461ac0ccce731c85c4af6c64c1aea084d123b0b..87a2a15fc630bdc8376d38df7c888842636ada42 100644 (file)
@@ -1,99 +1,99 @@
-/*\r
- * Copyright (c) 2006 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include "credprov.h"\r
-#include <assert.h>\r
-\r
-/* Dialog procedures and support functions for handling configuration\r
-   dialogs for general plug-in configuration. */\r
-\r
-/* Structure for holding dialog data for the configuration window. */\r
-typedef struct tag_config_main_dlg_data {\r
-    khui_config_node cnode;\r
-\r
-    /* TODO: add fields as needed */\r
-} config_main_dlg_data;\r
-\r
-INT_PTR CALLBACK\r
-config_dlgproc(HWND hwnd,\r
-               UINT uMsg,\r
-               WPARAM wParam,\r
-               LPARAM lParam) {\r
-\r
-    config_main_dlg_data * d;\r
-\r
-    switch (uMsg) {\r
-    case WM_INITDIALOG:\r
-        d = malloc(sizeof(*d));\r
-        assert(d);\r
-        ZeroMemory(d, sizeof(*d));\r
-\r
-        /* for configuration panels that are not subpanels, lParam is\r
-           a held handle to the configuration node.  The handle will\r
-           be held for the lifetime of the window. */\r
-\r
-        d->cnode = (khui_config_node) lParam;\r
-\r
-        /* TODO: perform any other required initialization stuff\r
-           here */\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-        SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);\r
-#pragma warning(pop)\r
-\r
-        break;\r
-\r
-    case KHUI_WM_CFG_NOTIFY:\r
-        {\r
-            d = (config_main_dlg_data *)\r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-            /* WMCFG_APPLY is the only notification we care about */\r
-\r
-            if (HIWORD(wParam) == WMCFG_APPLY) {\r
-                /* TODO: Apply changes and update the state */\r
-\r
-                return TRUE;\r
-            }\r
-        }\r
-        break;\r
-\r
-    case WM_DESTROY:\r
-        d = (config_main_dlg_data *)\r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        /* TODO: perform any other required uninitialization here */\r
-\r
-        if (d)\r
-            free(d);\r
-\r
-        break;\r
-    }\r
-\r
-    return FALSE;\r
-\r
-}\r
+/*
+ * Copyright (c) 2006 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 "credprov.h"
+#include <assert.h>
+
+/* Dialog procedures and support functions for handling configuration
+   dialogs for general plug-in configuration. */
+
+/* Structure for holding dialog data for the configuration window. */
+typedef struct tag_config_main_dlg_data {
+    khui_config_node cnode;
+
+    /* TODO: add fields as needed */
+} config_main_dlg_data;
+
+INT_PTR CALLBACK
+config_dlgproc(HWND hwnd,
+               UINT uMsg,
+               WPARAM wParam,
+               LPARAM lParam) {
+
+    config_main_dlg_data * d;
+
+    switch (uMsg) {
+    case WM_INITDIALOG:
+        d = malloc(sizeof(*d));
+        assert(d);
+        ZeroMemory(d, sizeof(*d));
+
+        /* for configuration panels that are not subpanels, lParam is
+           a held handle to the configuration node.  The handle will
+           be held for the lifetime of the window. */
+
+        d->cnode = (khui_config_node) lParam;
+
+        /* TODO: perform any other required initialization stuff
+           here */
+
+#pragma warning(push)
+#pragma warning(disable: 4244)
+        SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);
+#pragma warning(pop)
+
+        break;
+
+    case KHUI_WM_CFG_NOTIFY:
+        {
+            d = (config_main_dlg_data *)
+                GetWindowLongPtr(hwnd, DWLP_USER);
+
+            /* WMCFG_APPLY is the only notification we care about */
+
+            if (HIWORD(wParam) == WMCFG_APPLY) {
+                /* TODO: Apply changes and update the state */
+
+                return TRUE;
+            }
+        }
+        break;
+
+    case WM_DESTROY:
+        d = (config_main_dlg_data *)
+            GetWindowLongPtr(hwnd, DWLP_USER);
+
+        /* TODO: perform any other required uninitialization here */
+
+        if (d)
+            free(d);
+
+        break;
+    }
+
+    return FALSE;
+
+}
index da07756dad899f47330ae40beb42afb00edb0f26..cd8db44ee32081c0a356b86c532bd31bc2a7ae56 100644 (file)
-/*\r
- * Copyright (c) 2006 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include "credprov.h"\r
-#include<assert.h>\r
-\r
-/* This file provides handlers for the credentials acquisition\r
-   messages including handling the user interface for the new\r
-   credentials dialogs. */\r
-\r
-/*********************************************************************\r
-\r
-These are stubs for the Window message for the dialog panel.  This\r
-dialog panel is the one that is added to the new credentials window\r
-for obtaining new credentials.\r
-\r
-Note that all the UI callbacks run under the UI thread.\r
-\r
- *********************************************************************/\r
-\r
-/* This structure will hold all the state information we will need to\r
-   access from the new credentials panel for our credentials type. */\r
-struct nc_dialog_data {\r
-    khui_new_creds * nc;\r
-    khui_new_creds_by_type * nct;\r
-\r
-    /* TODO: add any other state information here */\r
-};\r
-\r
-/* Note: This callback runs under the UI thread */\r
-INT_PTR\r
-handle_wm_initdialog(HWND hwnd, WPARAM wParam, LPARAM lParam) {\r
-    khui_new_creds * nc = NULL;\r
-    khui_new_creds_by_type * nct = NULL;\r
-    struct nc_dialog_data * d = NULL;\r
-\r
-    nc = (khui_new_creds *) lParam;\r
-    khui_cw_find_type(nc, credtype_id, &nct);\r
-\r
-    assert(nct);\r
-\r
-    d = malloc(sizeof(*d));\r
-    ZeroMemory(d, sizeof(*d));\r
-\r
-    d->nc = nc;\r
-    d->nct = nct;\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-    SetWindowLongPtr(hwnd, DWLP_USER, (LPARAM) d);\r
-#pragma warning(pop)\r
-\r
-    nct->aux = (LPARAM) d;      /* we can use the auxiliary field to\r
-                                   hold a pointer to d */\r
-\r
-    /* TODO: Perform any additional initialization here */\r
-\r
-    return FALSE;\r
-}\r
-\r
-/* Note: This callback runs under the UI thread */\r
-INT_PTR\r
-handle_khui_wm_nc_notify(HWND hwnd, WPARAM wParam, LPARAM lParam) {\r
-\r
-    struct nc_dialog_data * d;\r
-\r
-    /* Refer to the khui_wm_nc_notifications enumeration in the\r
-       NetIDMgr SDK for the full list of notification messages that\r
-       can be sent. */\r
-\r
-    d = (struct nc_dialog_data *) GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-    if (!d)\r
-        return TRUE;\r
-\r
-    /* these should be set by now */\r
-    assert(d->nc);\r
-    assert(d->nct);\r
-\r
-    switch (HIWORD(wParam)) {\r
-    case WMNC_UPDATE_CREDTEXT:\r
-        {\r
-            wchar_t fmt[KHUI_MAXCCH_LONG_DESC];\r
-            wchar_t tbuf[256];\r
-\r
-            /* we are being requested to update the credentials\r
-               text. We already allocated a buffer when we created the\r
-               nct structure.  So we can just set the text here.*/\r
-\r
-            /* TODO: The credtext should reflect the credentials that\r
-               will be obtained when the new credentials operation\r
-               completes. */\r
-\r
-            LoadString(hResModule, IDS_NC_CT_TEMPLATE,\r
-                       fmt, ARRAYLENGTH(fmt));\r
-\r
-            LoadString(hResModule, IDS_GEN_NONE,\r
-                       tbuf, ARRAYLENGTH(tbuf));\r
-\r
-            assert(d->nct->credtext);\r
-\r
-            StringCbPrintf(d->nct->credtext, KHUI_MAXCB_LONG_DESC,\r
-                           fmt, tbuf);\r
-        }\r
-        break;\r
-\r
-    case WMNC_CREDTEXT_LINK:\r
-        break;\r
-\r
-    case WMNC_IDENTITY_CHANGE:\r
-        break;\r
-\r
-    case WMNC_DIALOG_PREPROCESS:\r
-        break;\r
-    }\r
-\r
-    return TRUE;\r
-}\r
-\r
-/* Note: This callback runs under the UI thread */\r
-INT_PTR\r
-handle_wm_command(HWND hwnd, WPARAM wParam, LPARAM lParam) {\r
-\r
-    struct nc_dialog_data * d;\r
-\r
-    d = (struct nc_dialog_data *) GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-    /* TODO: handle WM_COMMAND */\r
-    return FALSE;\r
-}\r
-\r
-/* Note: This callback runs under the UI thread */\r
-INT_PTR\r
-handle_wm_destroy(HWND hwnd, WPARAM wParam, LPARAM lParam) {\r
-\r
-    struct nc_dialog_data * d;\r
-\r
-    d = (struct nc_dialog_data *) GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-    if (d) {\r
-        d->nc = NULL;\r
-        d->nct = NULL;\r
-\r
-        free(d);\r
-    }\r
-\r
-    /* TODO: Perform any additional uninitialization */\r
-\r
-    return FALSE;\r
-}\r
-\r
-/* Dialog procedure for the new credentials panel for our credentials\r
-   type.  We just dispatch messages here to other functions here.\r
-\r
-   Note that this procedure runs under the UI thread.\r
- */\r
-INT_PTR CALLBACK\r
-nc_dlg_proc(HWND hwnd,\r
-            UINT uMsg,\r
-            WPARAM wParam,\r
-            LPARAM lParam) {\r
-\r
-    switch (uMsg) {\r
-    case WM_INITDIALOG:\r
-        return handle_wm_initdialog(hwnd, wParam, lParam);\r
-\r
-    case WM_COMMAND:\r
-        return handle_wm_command(hwnd, wParam, lParam);\r
-\r
-    case KHUI_WM_NC_NOTIFY:\r
-        return handle_khui_wm_nc_notify(hwnd, wParam, lParam);\r
-\r
-    case WM_DESTROY:\r
-        return handle_wm_destroy(hwnd, wParam, lParam);\r
-\r
-        /* TODO: add code for handling other windows messages here. */\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-/*******************************************************************\r
-\r
-The following section contains function stubs for each of the\r
-credentials messages that a credentials provider is likely to want to\r
-handle.  It doesn't include a few messages, but they should be easy to\r
-add.  Please see the documentation for each of the KMSG_CRED_*\r
-messages for documentation on how to handle each of the messages.\r
-\r
-********************************************************************/\r
-\r
-\r
-/* Handler for KMSG_CRED_NEW_CREDS */\r
-khm_int32\r
-handle_kmsg_cred_new_creds(khui_new_creds * nc) {\r
-\r
-    wchar_t wshortdesc[KHUI_MAXCCH_SHORT_DESC];\r
-    size_t cb = 0;\r
-    khui_new_creds_by_type * nct = NULL;\r
-\r
-    /* This is a minimal handler that just adds a dialog pane to the\r
-       new credentials window to handle new credentials acquisition\r
-       for this credentials type. */\r
-\r
-    /* TODO: add additional initialization etc. as needed */\r
-\r
-    nct = malloc(sizeof(*nct));\r
-    ZeroMemory(nct, sizeof(*nct));\r
-\r
-    nct->type = credtype_id;\r
-    nct->ordinal = -1;\r
-\r
-    LoadString(hResModule, IDS_CT_SHORT_DESC,\r
-               wshortdesc, ARRAYLENGTH(wshortdesc));\r
-    StringCbLength(wshortdesc, sizeof(wshortdesc), &cb);\r
-#ifdef DEBUG\r
-    assert(cb > 0);\r
-#endif\r
-    cb += sizeof(wchar_t);\r
-\r
-    nct->name = malloc(cb);\r
-    StringCbCopy(nct->name, cb, wshortdesc);\r
-\r
-    /* while we are at it, we should also allocate space for the\r
-       credential text. */\r
-    nct->credtext = malloc(KHUI_MAXCB_LONG_DESC);\r
-    ZeroMemory(nct->credtext, KHUI_MAXCB_LONG_DESC);\r
-\r
-    nct->h_module = hResModule;\r
-    nct->dlg_proc = nc_dlg_proc;\r
-    nct->dlg_template = MAKEINTRESOURCE(IDD_NEW_CREDS);\r
-\r
-    khui_cw_add_type(nc, nct);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* Handler for KMSG_CRED_RENEW_CREDS */\r
-khm_int32\r
-handle_kmsg_cred_renew_creds(khui_new_creds * nc) {\r
-\r
-    khui_new_creds_by_type * nct;\r
-\r
-    /* This is a minimal handler that just adds this credential type\r
-       to the list of credential types that are participating in this\r
-       renewal operation. */\r
-\r
-    /* TODO: add additional initialization etc. as needed */\r
-\r
-    nct = malloc(sizeof(*nct));\r
-    ZeroMemory(nct, sizeof(*nct));\r
-\r
-    nct->type = credtype_id;\r
-\r
-    khui_cw_add_type(nc, nct);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* Handler for KMSG_CRED_DIALOG_PRESTART */\r
-khm_int32\r
-handle_kmsg_cred_dialog_prestart(khui_new_creds * nc) {\r
-    /* TODO: Handle this message */\r
-\r
-    /* The message is sent after the dialog has been created.  The\r
-       window handle for the created dialog can be accessed through\r
-       the hwnd_panel member of the khui_new_creds_by_type structure\r
-       that was added for this credentials type. */\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* Handler for KMSG_CRED_DIALOG_NEW_IDENTITY */\r
-/* Not a message sent out by NetIDMgr.  See documentation of\r
-   KMSG_CRED_DIALOG_NEW_IDENTITY  */\r
-khm_int32\r
-handle_kmsg_cred_dialog_new_identity(khm_ui_4 uparam,\r
-                                     void *   vparam) {\r
-    /* TODO: Handle this message */\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* Handler for KMSG_CRED_DIALOG_NEW_OPTIONS */\r
-/* Not a message sent out by NetIDMgr.  See documentation of\r
-   KMSG_CRED_DIALOG_NEW_OPTIONS */\r
-khm_int32\r
-handle_kmsg_cred_dialog_new_options(khm_ui_4 uparam,\r
-                                    void *   vparam) {\r
-    /* TODO: Handle this message */\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* Handler for KMSG_CRED_PROCESS */\r
-khm_int32\r
-handle_kmsg_cred_process(khui_new_creds * nc) {\r
-    /* TODO: Handle this message */\r
-\r
-    /* This is where the credentials acquisition should be performed\r
-       as determined by the UI.  Note that this message is sent even\r
-       when the user clicks 'cancel'.  The value of nc->result should\r
-       be checked before performing any credentials acquisition.  If\r
-       the value is KHUI_NC_RESULT_CANCEL, then no credentials should\r
-       be acquired.  Otherwise, the value would be\r
-       KHUI_NC_RESULT_PROCESS. */\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* Handler for KMSG_CRED_END */\r
-khm_int32\r
-handle_kmsg_cred_end(khui_new_creds * nc) {\r
-\r
-    khui_new_creds_by_type * nct = NULL;\r
-\r
-    /* TODO: Perform any additional uninitialization as needed. */\r
-\r
-    khui_cw_find_type(nc, credtype_id, &nct);\r
-\r
-    if (nct) {\r
-\r
-        khui_cw_del_type(nc, credtype_id);\r
-\r
-        if (nct->name)\r
-            free(nct->name);\r
-        if (nct->credtext)\r
-            free(nct->credtext);\r
-\r
-        free(nct);\r
-\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* Handler for KMSG_CRED_IMPORT */\r
-khm_int32\r
-handle_kmsg_cred_import(void) {\r
-\r
-    /* TODO: Handle this message */\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-\r
-/******************************************************\r
- Dispatch each message to individual handlers above.\r
- */\r
-khm_int32 KHMAPI\r
-handle_cred_acq_msg(khm_int32 msg_type,\r
-                    khm_int32 msg_subtype,\r
-                    khm_ui_4  uparam,\r
-                    void *    vparam) {\r
-\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    switch(msg_subtype) {\r
-    case KMSG_CRED_NEW_CREDS:\r
-        return handle_kmsg_cred_new_creds((khui_new_creds *) vparam);\r
-\r
-    case KMSG_CRED_RENEW_CREDS:\r
-        return handle_kmsg_cred_renew_creds((khui_new_creds *) vparam);\r
-\r
-    case KMSG_CRED_DIALOG_PRESTART:\r
-        return handle_kmsg_cred_dialog_prestart((khui_new_creds *) vparam);\r
-\r
-    case KMSG_CRED_PROCESS:\r
-        return handle_kmsg_cred_process((khui_new_creds *) vparam);\r
-\r
-    case KMSG_CRED_DIALOG_NEW_IDENTITY:\r
-        return handle_kmsg_cred_dialog_new_identity(uparam, vparam);\r
-\r
-    case KMSG_CRED_DIALOG_NEW_OPTIONS:\r
-        return handle_kmsg_cred_dialog_new_options(uparam, vparam);\r
-\r
-    case KMSG_CRED_END:\r
-        return handle_kmsg_cred_end((khui_new_creds *) vparam);\r
-\r
-    case KMSG_CRED_IMPORT:\r
-        return handle_kmsg_cred_import();\r
-    }\r
-\r
-    return rv;\r
-}\r
+/*
+ * Copyright (c) 2006 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 "credprov.h"
+#include<assert.h>
+
+/* This file provides handlers for the credentials acquisition
+   messages including handling the user interface for the new
+   credentials dialogs. */
+
+/*********************************************************************
+
+These are stubs for the Window message for the dialog panel.  This
+dialog panel is the one that is added to the new credentials window
+for obtaining new credentials.
+
+Note that all the UI callbacks run under the UI thread.
+
+ *********************************************************************/
+
+/* This structure will hold all the state information we will need to
+   access from the new credentials panel for our credentials type. */
+struct nc_dialog_data {
+    khui_new_creds * nc;
+    khui_new_creds_by_type * nct;
+
+    /* TODO: add any other state information here */
+};
+
+/* Note: This callback runs under the UI thread */
+INT_PTR
+handle_wm_initdialog(HWND hwnd, WPARAM wParam, LPARAM lParam) {
+    khui_new_creds * nc = NULL;
+    khui_new_creds_by_type * nct = NULL;
+    struct nc_dialog_data * d = NULL;
+
+    nc = (khui_new_creds *) lParam;
+    khui_cw_find_type(nc, credtype_id, &nct);
+
+    assert(nct);
+
+    d = malloc(sizeof(*d));
+    ZeroMemory(d, sizeof(*d));
+
+    d->nc = nc;
+    d->nct = nct;
+
+#pragma warning(push)
+#pragma warning(disable: 4244)
+    SetWindowLongPtr(hwnd, DWLP_USER, (LPARAM) d);
+#pragma warning(pop)
+
+    nct->aux = (LPARAM) d;      /* we can use the auxiliary field to
+                                   hold a pointer to d */
+
+    /* TODO: Perform any additional initialization here */
+
+    return FALSE;
+}
+
+/* Note: This callback runs under the UI thread */
+INT_PTR
+handle_khui_wm_nc_notify(HWND hwnd, WPARAM wParam, LPARAM lParam) {
+
+    struct nc_dialog_data * d;
+
+    /* Refer to the khui_wm_nc_notifications enumeration in the
+       NetIDMgr SDK for the full list of notification messages that
+       can be sent. */
+
+    d = (struct nc_dialog_data *) GetWindowLongPtr(hwnd, DWLP_USER);
+
+    if (!d)
+        return TRUE;
+
+    /* these should be set by now */
+    assert(d->nc);
+    assert(d->nct);
+
+    switch (HIWORD(wParam)) {
+    case WMNC_UPDATE_CREDTEXT:
+        {
+            wchar_t fmt[KHUI_MAXCCH_LONG_DESC];
+            wchar_t tbuf[256];
+
+            /* we are being requested to update the credentials
+               text. We already allocated a buffer when we created the
+               nct structure.  So we can just set the text here.*/
+
+            /* TODO: The credtext should reflect the credentials that
+               will be obtained when the new credentials operation
+               completes. */
+
+            LoadString(hResModule, IDS_NC_CT_TEMPLATE,
+                       fmt, ARRAYLENGTH(fmt));
+
+            LoadString(hResModule, IDS_GEN_NONE,
+                       tbuf, ARRAYLENGTH(tbuf));
+
+            assert(d->nct->credtext);
+
+            StringCbPrintf(d->nct->credtext, KHUI_MAXCB_LONG_DESC,
+                           fmt, tbuf);
+        }
+        break;
+
+    case WMNC_CREDTEXT_LINK:
+        break;
+
+    case WMNC_IDENTITY_CHANGE:
+        break;
+
+    case WMNC_DIALOG_PREPROCESS:
+        break;
+    }
+
+    return TRUE;
+}
+
+/* Note: This callback runs under the UI thread */
+INT_PTR
+handle_wm_command(HWND hwnd, WPARAM wParam, LPARAM lParam) {
+
+    struct nc_dialog_data * d;
+
+    d = (struct nc_dialog_data *) GetWindowLongPtr(hwnd, DWLP_USER);
+
+    /* TODO: handle WM_COMMAND */
+    return FALSE;
+}
+
+/* Note: This callback runs under the UI thread */
+INT_PTR
+handle_wm_destroy(HWND hwnd, WPARAM wParam, LPARAM lParam) {
+
+    struct nc_dialog_data * d;
+
+    d = (struct nc_dialog_data *) GetWindowLongPtr(hwnd, DWLP_USER);
+
+    if (d) {
+        d->nc = NULL;
+        d->nct = NULL;
+
+        free(d);
+    }
+
+    /* TODO: Perform any additional uninitialization */
+
+    return FALSE;
+}
+
+/* Dialog procedure for the new credentials panel for our credentials
+   type.  We just dispatch messages here to other functions here.
+
+   Note that this procedure runs under the UI thread.
+ */
+INT_PTR CALLBACK
+nc_dlg_proc(HWND hwnd,
+            UINT uMsg,
+            WPARAM wParam,
+            LPARAM lParam) {
+
+    switch (uMsg) {
+    case WM_INITDIALOG:
+        return handle_wm_initdialog(hwnd, wParam, lParam);
+
+    case WM_COMMAND:
+        return handle_wm_command(hwnd, wParam, lParam);
+
+    case KHUI_WM_NC_NOTIFY:
+        return handle_khui_wm_nc_notify(hwnd, wParam, lParam);
+
+    case WM_DESTROY:
+        return handle_wm_destroy(hwnd, wParam, lParam);
+
+        /* TODO: add code for handling other windows messages here. */
+    }
+
+    return FALSE;
+}
+
+/*******************************************************************
+
+The following section contains function stubs for each of the
+credentials messages that a credentials provider is likely to want to
+handle.  It doesn't include a few messages, but they should be easy to
+add.  Please see the documentation for each of the KMSG_CRED_*
+messages for documentation on how to handle each of the messages.
+
+********************************************************************/
+
+
+/* Handler for KMSG_CRED_NEW_CREDS */
+khm_int32
+handle_kmsg_cred_new_creds(khui_new_creds * nc) {
+
+    wchar_t wshortdesc[KHUI_MAXCCH_SHORT_DESC];
+    size_t cb = 0;
+    khui_new_creds_by_type * nct = NULL;
+
+    /* This is a minimal handler that just adds a dialog pane to the
+       new credentials window to handle new credentials acquisition
+       for this credentials type. */
+
+    /* TODO: add additional initialization etc. as needed */
+
+    nct = malloc(sizeof(*nct));
+    ZeroMemory(nct, sizeof(*nct));
+
+    nct->type = credtype_id;
+    nct->ordinal = -1;
+
+    LoadString(hResModule, IDS_CT_SHORT_DESC,
+               wshortdesc, ARRAYLENGTH(wshortdesc));
+    StringCbLength(wshortdesc, sizeof(wshortdesc), &cb);
+#ifdef DEBUG
+    assert(cb > 0);
+#endif
+    cb += sizeof(wchar_t);
+
+    nct->name = malloc(cb);
+    StringCbCopy(nct->name, cb, wshortdesc);
+
+    /* while we are at it, we should also allocate space for the
+       credential text. */
+    nct->credtext = malloc(KHUI_MAXCB_LONG_DESC);
+    ZeroMemory(nct->credtext, KHUI_MAXCB_LONG_DESC);
+
+    nct->h_module = hResModule;
+    nct->dlg_proc = nc_dlg_proc;
+    nct->dlg_template = MAKEINTRESOURCE(IDD_NEW_CREDS);
+
+    khui_cw_add_type(nc, nct);
+
+    return KHM_ERROR_SUCCESS;
+}
+
+/* Handler for KMSG_CRED_RENEW_CREDS */
+khm_int32
+handle_kmsg_cred_renew_creds(khui_new_creds * nc) {
+
+    khui_new_creds_by_type * nct;
+
+    /* This is a minimal handler that just adds this credential type
+       to the list of credential types that are participating in this
+       renewal operation. */
+
+    /* TODO: add additional initialization etc. as needed */
+
+    nct = malloc(sizeof(*nct));
+    ZeroMemory(nct, sizeof(*nct));
+
+    nct->type = credtype_id;
+
+    khui_cw_add_type(nc, nct);
+
+    return KHM_ERROR_SUCCESS;
+}
+
+/* Handler for KMSG_CRED_DIALOG_PRESTART */
+khm_int32
+handle_kmsg_cred_dialog_prestart(khui_new_creds * nc) {
+    /* TODO: Handle this message */
+
+    /* The message is sent after the dialog has been created.  The
+       window handle for the created dialog can be accessed through
+       the hwnd_panel member of the khui_new_creds_by_type structure
+       that was added for this credentials type. */
+    return KHM_ERROR_SUCCESS;
+}
+
+/* Handler for KMSG_CRED_DIALOG_NEW_IDENTITY */
+/* Not a message sent out by NetIDMgr.  See documentation of
+   KMSG_CRED_DIALOG_NEW_IDENTITY  */
+khm_int32
+handle_kmsg_cred_dialog_new_identity(khm_ui_4 uparam,
+                                     void *   vparam) {
+    /* TODO: Handle this message */
+    return KHM_ERROR_SUCCESS;
+}
+
+/* Handler for KMSG_CRED_DIALOG_NEW_OPTIONS */
+/* Not a message sent out by NetIDMgr.  See documentation of
+   KMSG_CRED_DIALOG_NEW_OPTIONS */
+khm_int32
+handle_kmsg_cred_dialog_new_options(khm_ui_4 uparam,
+                                    void *   vparam) {
+    /* TODO: Handle this message */
+    return KHM_ERROR_SUCCESS;
+}
+
+/* Handler for KMSG_CRED_PROCESS */
+khm_int32
+handle_kmsg_cred_process(khui_new_creds * nc) {
+    /* TODO: Handle this message */
+
+    /* This is where the credentials acquisition should be performed
+       as determined by the UI.  Note that this message is sent even
+       when the user clicks 'cancel'.  The value of nc->result should
+       be checked before performing any credentials acquisition.  If
+       the value is KHUI_NC_RESULT_CANCEL, then no credentials should
+       be acquired.  Otherwise, the value would be
+       KHUI_NC_RESULT_PROCESS. */
+
+    return KHM_ERROR_SUCCESS;
+}
+
+/* Handler for KMSG_CRED_END */
+khm_int32
+handle_kmsg_cred_end(khui_new_creds * nc) {
+
+    khui_new_creds_by_type * nct = NULL;
+
+    /* TODO: Perform any additional uninitialization as needed. */
+
+    khui_cw_find_type(nc, credtype_id, &nct);
+
+    if (nct) {
+
+        khui_cw_del_type(nc, credtype_id);
+
+        if (nct->name)
+            free(nct->name);
+        if (nct->credtext)
+            free(nct->credtext);
+
+        free(nct);
+
+    }
+
+    return KHM_ERROR_SUCCESS;
+}
+
+/* Handler for KMSG_CRED_IMPORT */
+khm_int32
+handle_kmsg_cred_import(void) {
+
+    /* TODO: Handle this message */
+
+    return KHM_ERROR_SUCCESS;
+}
+
+
+/******************************************************
+ Dispatch each message to individual handlers above.
+ */
+khm_int32 KHMAPI
+handle_cred_acq_msg(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_NEW_CREDS:
+        return handle_kmsg_cred_new_creds((khui_new_creds *) vparam);
+
+    case KMSG_CRED_RENEW_CREDS:
+        return handle_kmsg_cred_renew_creds((khui_new_creds *) vparam);
+
+    case KMSG_CRED_DIALOG_PRESTART:
+        return handle_kmsg_cred_dialog_prestart((khui_new_creds *) vparam);
+
+    case KMSG_CRED_PROCESS:
+        return handle_kmsg_cred_process((khui_new_creds *) vparam);
+
+    case KMSG_CRED_DIALOG_NEW_IDENTITY:
+        return handle_kmsg_cred_dialog_new_identity(uparam, vparam);
+
+    case KMSG_CRED_DIALOG_NEW_OPTIONS:
+        return handle_kmsg_cred_dialog_new_options(uparam, vparam);
+
+    case KMSG_CRED_END:
+        return handle_kmsg_cred_end((khui_new_creds *) vparam);
+
+    case KMSG_CRED_IMPORT:
+        return handle_kmsg_cred_import();
+    }
+
+    return rv;
+}
index 1851eaa4b4b3f07ca5d5f0b3b6b331cb5d02c8b0..14670934d4a41c6ae1e2dfa2e1772f6c0451d9b4 100644 (file)
-/*\r
- * Copyright (c) 2006 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-/* only include this header file once */\r
-#pragma once\r
-\r
-#ifndef _UNICODE\r
-#ifndef RC_INVOKED\r
-/* This template relies on _UNICODE being defined to call the correct\r
-   APIs. */\r
-#error  This template needs to be compiled with _UNICODE\r
-#endif\r
-#endif\r
-\r
-/* Pull in configuration macros from the Makefile */\r
-#include "credacq_config.h"\r
-\r
-/* declare a few macros about our plugin */\r
-\r
-/* The following macro will be used throughout the template to refer\r
-   to the name of the plugin.  The macro is actually defined the\r
-   Makefile generated configuration header file.  Modify the\r
-   PLUGINNAME Makefile macro.*/\r
-#ifndef MYPLUGIN_NAME\r
-#error  MYPLUGIN_NAME not defined\r
-#endif\r
-\r
-/* Also define the unicde equivalent of the name.  In general strings\r
-   in NetIDMgr are unicode. */\r
-#define MYPLUGIN_NAMEW _T(MYPLUGIN_NAME)\r
-\r
-/* The name of the module.  This is distinct from the name of the\r
-   plugin for several reasons.  One of which is that a single module\r
-   can provide multiple plugins.  Also, having a module name distinct\r
-   from a plugin name allows multiple vendors to provide the same\r
-   plugin.  For example, the module name for the MIT Kerberos 5 plugin\r
-   is MITKrb5 while the plugin name is Krb5Cred.  The macro is\r
-   actually defined in the Makefile generated configuration header\r
-   file.  Modify the MODULENAME Makefile macro.*/\r
-#ifndef MYMODULE_NAME\r
-#error  MYMODULE_NAME not defined\r
-#endif\r
-\r
-#define MYMODULE_NAMEW _T(MYMODULE_NAME)\r
-\r
-/* When logging events from our plugin, the event logging API can\r
-   optionally take a facility name to provide a friendly label to\r
-   identify where each event came from.  We will default to the plugin\r
-   name, although it can be anything. */\r
-#define MYPLUGIN_FACILITYW MYPLUGIN_NAMEW\r
-\r
-/* Base name of the DLL that will be providing the plugin.  We use it\r
-   to construct names of the DLLs that will contain localized\r
-   resources.  This is defined in the Makefile and fed in to the build\r
-   through there.  The macro to change in the Makefile is\r
-   DLLBASENAME. */\r
-#ifndef MYPLUGIN_DLLBASE\r
-#error   MYPLUGIN_DLLBASE Not defined!\r
-#endif\r
-\r
-#define MYPLUGIN_DLLBASEW _T(MYPLUGIN_DLLBASE)\r
-\r
-/* Name of the credentials type that will be registered by the plugin.\r
-   This macro is actually defined in the Makefile generated\r
-   configuration header file.  Change the CREDTYPENAME macro in the\r
-   Makefile. */\r
-#ifndef MYCREDTYPE_NAME\r
-#error  MYCREDTYPE_NAME not defined\r
-#endif\r
-\r
-#define MYCREDTYPE_NAMEW _T(MYCREDTYPE_NAME)\r
-\r
-/* Configuration node names.  We just concatenate a few strings\r
-   together, although you should feel free to completely define your\r
-   own. */\r
-\r
-#define CONFIGNODE_MAIN   MYCREDTYPE_NAMEW L"Config"\r
-#define CONFIGNODE_ALL_ID MYCREDTYPE_NAMEW L"AllIdents"\r
-#define CONFIGNODE_PER_ID MYCREDTYPE_NAMEW L"PerIdent"\r
-\r
-#include<windows.h>\r
-/* include the standard NetIDMgr header files */\r
-#include<netidmgr.h>\r
-#include<tchar.h>\r
-\r
-/* declarations for language resources */\r
-#include "langres.h"\r
-\r
-#ifndef NOSTRSAFE\r
-#include<strsafe.h>\r
-#endif\r
-\r
-/***************************************************\r
- Externals\r
-***************************************************/\r
-\r
-extern kmm_module h_khModule;\r
-extern HINSTANCE  hInstance;\r
-extern HMODULE    hResModule;\r
-\r
-extern const wchar_t * my_facility;\r
-\r
-extern khm_int32 credtype_id;\r
-\r
-/* Function declarations */\r
-\r
-/* in plugin.c */\r
-khm_int32 KHMAPI\r
-plugin_msg_proc(khm_int32 msg_type,\r
-                khm_int32 msg_subtype,\r
-                khm_ui_4  uparam,\r
-                void * vparam);\r
-\r
-/* in credtype.c */\r
-khm_int32 KHMAPI\r
-cred_is_equal(khm_handle cred1,\r
-              khm_handle cred2,\r
-              void * rock);\r
-\r
-/* in credacq.c */\r
-khm_int32 KHMAPI\r
-handle_cred_acq_msg(khm_int32 msg_type,\r
-                    khm_int32 msg_subtype,\r
-                    khm_ui_4  uparam,\r
-                    void *    vparam);\r
-\r
-/* in proppage.c */\r
-INT_PTR CALLBACK\r
-pp_cred_dlg_proc(HWND hwnd,\r
-                 UINT uMsg,\r
-                 WPARAM wParam,\r
-                 LPARAM lParam);\r
-\r
-/* in config_id.c */\r
-INT_PTR CALLBACK\r
-config_id_dlgproc(HWND hwndDlg,\r
-                  UINT uMsg,\r
-                  WPARAM wParam,\r
-                  LPARAM lParam);\r
-\r
-/* in config_ids.c */\r
-INT_PTR CALLBACK\r
-config_ids_dlgproc(HWND hwndDlg,\r
-                   UINT uMsg,\r
-                   WPARAM wParam,\r
-                   LPARAM lParam);\r
-\r
-/* in config_main.c */\r
-INT_PTR CALLBACK\r
-config_dlgproc(HWND hwndDlg,\r
-               UINT uMsg,\r
-               WPARAM wParam,\r
-               LPARAM lParam);\r
+/*
+ * Copyright (c) 2006 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/* $Id$ */
+
+/* only include this header file once */
+#pragma once
+
+#ifndef _UNICODE
+#ifndef RC_INVOKED
+/* This template relies on _UNICODE being defined to call the correct
+   APIs. */
+#error  This template needs to be compiled with _UNICODE
+#endif
+#endif
+
+/* Pull in configuration macros from the Makefile */
+#include "credacq_config.h"
+
+/* declare a few macros about our plugin */
+
+/* The following macro will be used throughout the template to refer
+   to the name of the plugin.  The macro is actually defined the
+   Makefile generated configuration header file.  Modify the
+   PLUGINNAME Makefile macro.*/
+#ifndef MYPLUGIN_NAME
+#error  MYPLUGIN_NAME not defined
+#endif
+
+/* Also define the unicde equivalent of the name.  In general strings
+   in NetIDMgr are unicode. */
+#define MYPLUGIN_NAMEW _T(MYPLUGIN_NAME)
+
+/* The name of the module.  This is distinct from the name of the
+   plugin for several reasons.  One of which is that a single module
+   can provide multiple plugins.  Also, having a module name distinct
+   from a plugin name allows multiple vendors to provide the same
+   plugin.  For example, the module name for the MIT Kerberos 5 plugin
+   is MITKrb5 while the plugin name is Krb5Cred.  The macro is
+   actually defined in the Makefile generated configuration header
+   file.  Modify the MODULENAME Makefile macro.*/
+#ifndef MYMODULE_NAME
+#error  MYMODULE_NAME not defined
+#endif
+
+#define MYMODULE_NAMEW _T(MYMODULE_NAME)
+
+/* When logging events from our plugin, the event logging API can
+   optionally take a facility name to provide a friendly label to
+   identify where each event came from.  We will default to the plugin
+   name, although it can be anything. */
+#define MYPLUGIN_FACILITYW MYPLUGIN_NAMEW
+
+/* Base name of the DLL that will be providing the plugin.  We use it
+   to construct names of the DLLs that will contain localized
+   resources.  This is defined in the Makefile and fed in to the build
+   through there.  The macro to change in the Makefile is
+   DLLBASENAME. */
+#ifndef MYPLUGIN_DLLBASE
+#error   MYPLUGIN_DLLBASE Not defined!
+#endif
+
+#define MYPLUGIN_DLLBASEW _T(MYPLUGIN_DLLBASE)
+
+/* Name of the credentials type that will be registered by the plugin.
+   This macro is actually defined in the Makefile generated
+   configuration header file.  Change the CREDTYPENAME macro in the
+   Makefile. */
+#ifndef MYCREDTYPE_NAME
+#error  MYCREDTYPE_NAME not defined
+#endif
+
+#define MYCREDTYPE_NAMEW _T(MYCREDTYPE_NAME)
+
+/* Configuration node names.  We just concatenate a few strings
+   together, although you should feel free to completely define your
+   own. */
+
+#define CONFIGNODE_MAIN   MYCREDTYPE_NAMEW L"Config"
+#define CONFIGNODE_ALL_ID MYCREDTYPE_NAMEW L"AllIdents"
+#define CONFIGNODE_PER_ID MYCREDTYPE_NAMEW L"PerIdent"
+
+#include<windows.h>
+/* include the standard NetIDMgr header files */
+#include<netidmgr.h>
+#include<tchar.h>
+
+/* declarations for language resources */
+#include "langres.h"
+
+#ifndef NOSTRSAFE
+#include<strsafe.h>
+#endif
+
+/***************************************************
+ Externals
+***************************************************/
+
+extern kmm_module h_khModule;
+extern HINSTANCE  hInstance;
+extern HMODULE    hResModule;
+
+extern const wchar_t * my_facility;
+
+extern khm_int32 credtype_id;
+
+/* Function declarations */
+
+/* in plugin.c */
+khm_int32 KHMAPI
+plugin_msg_proc(khm_int32 msg_type,
+                khm_int32 msg_subtype,
+                khm_ui_4  uparam,
+                void * vparam);
+
+/* in credtype.c */
+khm_int32 KHMAPI
+cred_is_equal(khm_handle cred1,
+              khm_handle cred2,
+              void * rock);
+
+/* in credacq.c */
+khm_int32 KHMAPI
+handle_cred_acq_msg(khm_int32 msg_type,
+                    khm_int32 msg_subtype,
+                    khm_ui_4  uparam,
+                    void *    vparam);
+
+/* in proppage.c */
+INT_PTR CALLBACK
+pp_cred_dlg_proc(HWND hwnd,
+                 UINT uMsg,
+                 WPARAM wParam,
+                 LPARAM lParam);
+
+/* in config_id.c */
+INT_PTR CALLBACK
+config_id_dlgproc(HWND hwndDlg,
+                  UINT uMsg,
+                  WPARAM wParam,
+                  LPARAM lParam);
+
+/* in config_ids.c */
+INT_PTR CALLBACK
+config_ids_dlgproc(HWND hwndDlg,
+                   UINT uMsg,
+                   WPARAM wParam,
+                   LPARAM lParam);
+
+/* in config_main.c */
+INT_PTR CALLBACK
+config_dlgproc(HWND hwndDlg,
+               UINT uMsg,
+               WPARAM wParam,
+               LPARAM lParam);
index 039c644542a2883e9be9b9622aa6a8b92b282289..cee7df19a75f0cb5e716e5212cd77a8ef616b761 100644 (file)
@@ -1,52 +1,52 @@
-/*\r
- * Copyright (c) 2006 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AND\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include "credprov.h"\r
-\r
-/* Functions for handling our credentials type.\r
-*/\r
-\r
-khm_int32 KHMAPI\r
-cred_is_equal(khm_handle cred1,\r
-              khm_handle cred2,\r
-              void * rock) {\r
-\r
-    khm_int32 result;\r
-\r
-    /* TODO: Check any additional fields to determine if the two\r
-       credentials are equal or not. */\r
-\r
-    /* Note that this is actually a comparison function.  It should\r
-       return 0 if the credentials are found to be equal, and non-zero\r
-       if they are not.  We just set this to 0 if we don't need to\r
-       check any additional fields and accept the two credentials as\r
-       being equal.  By the time this function is called, the\r
-       identity, name and type of the credentials have already been\r
-       found to be equal. */\r
-    result = 0;\r
-\r
-    return result;\r
-}\r
+/*
+ * Copyright (c) 2006 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AND
+ * ACTION OF 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 "credprov.h"
+
+/* Functions for handling our credentials type.
+*/
+
+khm_int32 KHMAPI
+cred_is_equal(khm_handle cred1,
+              khm_handle cred2,
+              void * rock) {
+
+    khm_int32 result;
+
+    /* TODO: Check any additional fields to determine if the two
+       credentials are equal or not. */
+
+    /* Note that this is actually a comparison function.  It should
+       return 0 if the credentials are found to be equal, and non-zero
+       if they are not.  We just set this to 0 if we don't need to
+       check any additional fields and accept the two credentials as
+       being equal.  By the time this function is called, the
+       identity, name and type of the credentials have already been
+       found to be equal. */
+    result = 0;
+
+    return result;
+}
index 2b81c554de2925bf03f8f5c1cb288e607733b771..962c4cb223d83ee305281d21e5b500be7c53ddb6 100644 (file)
@@ -1,34 +1,34 @@
-//{{NO_DEPENDENCIES}}\r
-// Microsoft Visual C++ generated include file.\r
-// Used by C:\work\pismere\athena\auth\krb5\src\windows\identity\sample\templates\credprov\lang\en_us\langres.rc\r
-//\r
-#define IDD_PP_CRED                     106\r
-#define IDD_PP_IDENT                    107\r
-#define IDS_PLUGIN_DESC                 109\r
-#define IDS_CT_SHORT_DESC               110\r
-#define IDI_PLUGIN                      110\r
-#define IDS_CT_LONG_DESC                111\r
-#define IDD_NEW_CREDS                   112\r
-#define IDS_NC_CT_TEMPLATE              112\r
-#define IDS_NC_CT_TEMPLATE_NL           113\r
-#define IDD_CONFIG                      113\r
-#define IDS_GEN_NONE                    114\r
-#define IDD_CONFIG_ID                   114\r
-#define IDS_CFG_SHORT_DESC              115\r
-#define IDD_CONFIG_IDS                  115\r
-#define IDS_CFG_LONG_DESC               116\r
-#define IDS_CFG_IDS_SHORT_DESC          117\r
-#define IDS_CFG_IDS_LONG_DESC           118\r
-#define IDS_CFG_ID_SHORT_DESC           119\r
-#define IDS_CFG_ID_LONG_DESC            120\r
-\r
-// Next default values for new objects\r
-// \r
-#ifdef APSTUDIO_INVOKED\r
-#ifndef APSTUDIO_READONLY_SYMBOLS\r
-#define _APS_NEXT_RESOURCE_VALUE        116\r
-#define _APS_NEXT_COMMAND_VALUE         40001\r
-#define _APS_NEXT_CONTROL_VALUE         1039\r
-#define _APS_NEXT_SYMED_VALUE           101\r
-#endif\r
-#endif\r
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by C:\work\pismere\athena\auth\krb5\src\windows\identity\sample\templates\credprov\lang\en_us\langres.rc
+//
+#define IDD_PP_CRED                     106
+#define IDD_PP_IDENT                    107
+#define IDS_PLUGIN_DESC                 109
+#define IDS_CT_SHORT_DESC               110
+#define IDI_PLUGIN                      110
+#define IDS_CT_LONG_DESC                111
+#define IDD_NEW_CREDS                   112
+#define IDS_NC_CT_TEMPLATE              112
+#define IDS_NC_CT_TEMPLATE_NL           113
+#define IDD_CONFIG                      113
+#define IDS_GEN_NONE                    114
+#define IDD_CONFIG_ID                   114
+#define IDS_CFG_SHORT_DESC              115
+#define IDD_CONFIG_IDS                  115
+#define IDS_CFG_LONG_DESC               116
+#define IDS_CFG_IDS_SHORT_DESC          117
+#define IDS_CFG_IDS_LONG_DESC           118
+#define IDS_CFG_ID_SHORT_DESC           119
+#define IDS_CFG_ID_LONG_DESC            120
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        116
+#define _APS_NEXT_COMMAND_VALUE         40001
+#define _APS_NEXT_CONTROL_VALUE         1039
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
index 7a5020573956e4591639c752d363969ea5e3b965..07da40d57ca3679749fd53c66c2d9141e2edf0af 100644 (file)
-/*\r
- * Copyright (c) 2006 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include "credprov.h"\r
-\r
-/* This file provides the entry points for the module.  The purpose of\r
-   each entry point is explained below.\r
-*/\r
-\r
-kmm_module h_khModule;          /* KMM's handle to this module */\r
-HINSTANCE hInstance;            /* handle to our DLL */\r
-HMODULE hResModule;             /* handle to DLL containing language specific resources */\r
-\r
-const wchar_t * my_facility = MYPLUGIN_FACILITYW;\r
-\r
-/* locales and n_locales are used to provide information to NetIDMgr\r
-   about the locales that we support.  Each locale that is supported\r
-   is represented by a single line below.  NetIDMgr will pick a\r
-   suitable locale from this list as described in the documentation\r
-   for kmm_set_locale_info(). */\r
-kmm_module_locale locales[] = {\r
-\r
-    /* there needs to be at least one language that is supported.\r
-       Here we declare that to be US English, and make it the\r
-       default. */\r
-    LOCALE_DEF(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),\r
-               MYPLUGIN_DLLBASEW L"_en_us.dll", /* this is the name of\r
-                                                  the DLL. We paste a\r
-                                                  trailer to basename\r
-                                                  of the DLL.  This\r
-                                                  DLL should reside in\r
-                                                  the same directory\r
-                                                  as the plugin\r
-                                                  DLL. */\r
-               KMM_MLOC_FLAG_DEFAULT)\r
-};\r
-int n_locales = ARRAYLENGTH(locales);\r
-\r
-/*******************************************************************\r
-   init_module\r
-   *****************************************************************\r
-\r
-   This is the entry point for the module.  Each module can provide\r
-   multiple plugins and each plugin will need a separate entry point.\r
-   Generally, the module entry point will set up localized resources\r
-   and register the plugins.\r
-\r
-*/\r
-KHMEXP khm_int32 KHMAPI init_module(kmm_module h_module) {\r
-\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    kmm_plugin_reg pi;\r
-    wchar_t description[KMM_MAXCCH_DESC];\r
-    int t;\r
-\r
-    h_khModule = h_module;\r
-\r
-    rv = kmm_set_locale_info(h_module, locales, n_locales);\r
-    if(KHM_SUCCEEDED(rv)) {\r
-        /* if the call succeeded, then NetIDMgr has picked a localized\r
-           resource DLL for us to use. */\r
-        hResModule = kmm_get_resource_hmodule(h_module);\r
-    } else\r
-        goto _exit;\r
-\r
-    /* TODO: Perform any other required initialization operations. */\r
-\r
-    /* register our plugin */\r
-    ZeroMemory(&pi, sizeof(pi));\r
-\r
-    pi.name = MYPLUGIN_NAMEW;   /* name of the plugin */\r
-    pi.type = KHM_PITYPE_CRED;  /* type.  This is a credentials\r
-                                   provider.  Setting this type has\r
-                                   the effect of having the plugin\r
-                                   entrypoint being automatically\r
-                                   subscribed to credentials provider\r
-                                   messages. */\r
-\r
-    /* An icon is optional, but we provide one anyway. */\r
-    pi.icon = LoadImage(hResModule, MAKEINTRESOURCE(IDI_PLUGIN),\r
-                        IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE);\r
-    pi.flags = 0;\r
-    pi.msg_proc = plugin_msg_proc;\r
-    pi.description = description;\r
-    pi.dependencies = NULL;\r
-    t = LoadString(hResModule, IDS_PLUGIN_DESC,\r
-                   description, ARRAYLENGTH(description));\r
-    if (!t)\r
-        description[0] = L'\0';\r
-    else\r
-        description[ARRAYLENGTH(description) - 1] = L'\0';\r
-\r
-    rv = kmm_provide_plugin(h_module, &pi);\r
-\r
-    /* TODO: register any additional plugins */\r
-\r
-    /* Returning a successful code (KHM_ERROR_SUCCESS) will cause the\r
-       plugins to be initialized.  If no plugin is successfully\r
-       registered while processing init_module or if a code other than\r
-       KHM_ERROR_SUCCESS is returned, the module will be immediately\r
-       unloaded. */\r
-\r
- _exit:\r
-    return rv;\r
-}\r
-\r
-/**********************************************************\r
-   Exit module\r
-   ********************************************************\r
-\r
-   Called by the NetIDMgr module manager when unloading the module.\r
-   This will get called even if the module is being unloaded due to an\r
-   error code returned by init_module().  This callback is required. */\r
-KHMEXP khm_int32 KHMAPI exit_module(kmm_module h_module) {\r
-\r
-    /* Unregistering the plugin is not required at this point. */\r
-\r
-    /* TODO: Perform any other required cleanup here. */\r
-\r
-    return KHM_ERROR_SUCCESS; /* the return code is ignored */\r
-}\r
-\r
-/* General DLL initialization.  It is advisable to not do anything\r
-   here and also keep in mind that the plugin will be loaded at a time\r
-   where some threads have already started.  So DLL_THREAD_ATTACH will\r
-   not fire for every thread.  In addition, the plugin will be\r
-   unloaded before the application and all the threads terminate. */\r
-BOOL WINAPI DllMain(HINSTANCE hinstDLL,\r
-                    DWORD fdwReason,\r
-                    LPVOID lpvReserved)\r
-{\r
-    switch(fdwReason) {\r
-        case DLL_PROCESS_ATTACH:\r
-            hInstance = hinstDLL;\r
-            break;\r
-\r
-        case DLL_PROCESS_DETACH:\r
-            break;\r
-\r
-        case DLL_THREAD_ATTACH:\r
-            break;\r
-\r
-        case DLL_THREAD_DETACH:\r
-            break;\r
-    }\r
-\r
-    return TRUE;\r
-}\r
+/*
+ * Copyright (c) 2006 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 "credprov.h"
+
+/* This file provides the entry points for the module.  The purpose of
+   each entry point is explained below.
+*/
+
+kmm_module h_khModule;          /* KMM's handle to this module */
+HINSTANCE hInstance;            /* handle to our DLL */
+HMODULE hResModule;             /* handle to DLL containing language specific resources */
+
+const wchar_t * my_facility = MYPLUGIN_FACILITYW;
+
+/* locales and n_locales are used to provide information to NetIDMgr
+   about the locales that we support.  Each locale that is supported
+   is represented by a single line below.  NetIDMgr will pick a
+   suitable locale from this list as described in the documentation
+   for kmm_set_locale_info(). */
+kmm_module_locale locales[] = {
+
+    /* there needs to be at least one language that is supported.
+       Here we declare that to be US English, and make it the
+       default. */
+    LOCALE_DEF(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
+               MYPLUGIN_DLLBASEW L"_en_us.dll", /* this is the name of
+                                                  the DLL. We paste a
+                                                  trailer to basename
+                                                  of the DLL.  This
+                                                  DLL should reside in
+                                                  the same directory
+                                                  as the plugin
+                                                  DLL. */
+               KMM_MLOC_FLAG_DEFAULT)
+};
+int n_locales = ARRAYLENGTH(locales);
+
+/*******************************************************************
+   init_module
+   *****************************************************************
+
+   This is the entry point for the module.  Each module can provide
+   multiple plugins and each plugin will need a separate entry point.
+   Generally, the module entry point will set up localized resources
+   and register the plugins.
+
+*/
+KHMEXP khm_int32 KHMAPI init_module(kmm_module h_module) {
+
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+    kmm_plugin_reg pi;
+    wchar_t description[KMM_MAXCCH_DESC];
+    int t;
+
+    h_khModule = h_module;
+
+    rv = kmm_set_locale_info(h_module, locales, n_locales);
+    if(KHM_SUCCEEDED(rv)) {
+        /* if the call succeeded, then NetIDMgr has picked a localized
+           resource DLL for us to use. */
+        hResModule = kmm_get_resource_hmodule(h_module);
+    } else
+        goto _exit;
+
+    /* TODO: Perform any other required initialization operations. */
+
+    /* register our plugin */
+    ZeroMemory(&pi, sizeof(pi));
+
+    pi.name = MYPLUGIN_NAMEW;   /* name of the plugin */
+    pi.type = KHM_PITYPE_CRED;  /* type.  This is a credentials
+                                   provider.  Setting this type has
+                                   the effect of having the plugin
+                                   entrypoint being automatically
+                                   subscribed to credentials provider
+                                   messages. */
+
+    /* An icon is optional, but we provide one anyway. */
+    pi.icon = LoadImage(hResModule, MAKEINTRESOURCE(IDI_PLUGIN),
+                        IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE);
+    pi.flags = 0;
+    pi.msg_proc = plugin_msg_proc;
+    pi.description = description;
+    pi.dependencies = NULL;
+    t = LoadString(hResModule, IDS_PLUGIN_DESC,
+                   description, ARRAYLENGTH(description));
+    if (!t)
+        description[0] = L'\0';
+    else
+        description[ARRAYLENGTH(description) - 1] = L'\0';
+
+    rv = kmm_provide_plugin(h_module, &pi);
+
+    /* TODO: register any additional plugins */
+
+    /* Returning a successful code (KHM_ERROR_SUCCESS) will cause the
+       plugins to be initialized.  If no plugin is successfully
+       registered while processing init_module or if a code other than
+       KHM_ERROR_SUCCESS is returned, the module will be immediately
+       unloaded. */
+
+ _exit:
+    return rv;
+}
+
+/**********************************************************
+   Exit module
+   ********************************************************
+
+   Called by the NetIDMgr module manager when unloading the module.
+   This will get called even if the module is being unloaded due to an
+   error code returned by init_module().  This callback is required. */
+KHMEXP khm_int32 KHMAPI exit_module(kmm_module h_module) {
+
+    /* Unregistering the plugin is not required at this point. */
+
+    /* TODO: Perform any other required cleanup here. */
+
+    return KHM_ERROR_SUCCESS; /* the return code is ignored */
+}
+
+/* General DLL initialization.  It is advisable to not do anything
+   here and also keep in mind that the plugin will be loaded at a time
+   where some threads have already started.  So DLL_THREAD_ATTACH will
+   not fire for every thread.  In addition, the plugin will be
+   unloaded before the application and all the threads terminate. */
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,
+                    DWORD fdwReason,
+                    LPVOID lpvReserved)
+{
+    switch(fdwReason) {
+        case DLL_PROCESS_ATTACH:
+            hInstance = hinstDLL;
+            break;
+
+        case DLL_PROCESS_DETACH:
+            break;
+
+        case DLL_THREAD_ATTACH:
+            break;
+
+        case DLL_THREAD_DETACH:
+            break;
+    }
+
+    return TRUE;
+}
index 31c9626a55efbc48998c6346323431b339e7ee84..9b1b0ce94a8358cf01275daae298f62ef0c7ddeb 100644 (file)
-/*\r
- * Copyright (c) 2006 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include "credprov.h"\r
-#include<assert.h>\r
-\r
-/* This file provides the message processing function and the support\r
-   routines for implementing our plugin.  Note that some of the\r
-   message processing routines have been moved to other source files\r
-   based on their use.\r
-*/\r
-\r
-khm_int32 credtype_id = KCDB_CREDTYPE_INVALID;\r
-khm_handle g_credset = NULL;\r
-\r
-/* Handler for system messages.  The only two we handle are\r
-   KMSG_SYSTEM_INIT and KMSG_SYSTEM_EXIT. */\r
-khm_int32 KHMAPI\r
-handle_kmsg_system(khm_int32 msg_type,\r
-                   khm_int32 msg_subtype,\r
-                   khm_ui_4  uparam,\r
-                   void *    vparam) {\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    switch (msg_subtype) {\r
-\r
-        /* This is the first message that will be received by a\r
-           plugin.  We use it to perform initialization operations\r
-           such as registering any credential types, data types and\r
-           attributes. */\r
-    case KMSG_SYSTEM_INIT:\r
-        {\r
-            kcdb_credtype ct;\r
-            wchar_t short_desc[KCDB_MAXCCH_SHORT_DESC];\r
-            wchar_t long_desc[KCDB_MAXCCH_LONG_DESC];\r
-            khui_config_node cnode;\r
-            khui_config_node_reg creg;\r
-\r
-            /* First and foremost, we need to register a credential\r
-               type. */\r
-            ZeroMemory(&ct, sizeof(ct));\r
-            ct.id = KCDB_CREDTYPE_AUTO;\r
-            ct.name = MYCREDTYPE_NAMEW;\r
-\r
-            short_desc[0] = L'\0';\r
-            LoadString(hResModule, IDS_CT_SHORT_DESC,\r
-                       short_desc, ARRAYLENGTH(short_desc));\r
-\r
-            long_desc[0] = L'\0';\r
-            LoadString(hResModule, IDS_CT_LONG_DESC,\r
-                       long_desc, ARRAYLENGTH(long_desc));\r
-\r
-            ct.icon = NULL;     /* We skip the icon for now, but you\r
-                                   can assign a handle to an icon\r
-                                   here.  The icon will be used to\r
-                                   represent the credentials type.*/\r
-\r
-            kmq_create_subscription(plugin_msg_proc, &ct.sub);\r
-\r
-            ct.is_equal = cred_is_equal;\r
-\r
-            rv = kcdb_credtype_register(&ct, &credtype_id);\r
-\r
-            /* We create a global credential set that we use in the\r
-               plug-in thread.  This alleviates the need to create one\r
-               everytime we need one. Keep in mind that this should\r
-               only be used in the plug-in thread and should not be\r
-               touched from the UI thread or any other thread. */\r
-            kcdb_credset_create(&g_credset);\r
-\r
-            /* TODO: Perform additional initialization operations. */\r
-\r
-            /* TODO: Also list out the credentials of this type that\r
-               already exist. */\r
-\r
-            /* Now we register our configuration panels. */\r
-\r
-\r
-            /* This configuration panel is the one that controls\r
-               general options.  We leave the identity specific and\r
-               identity defaults for other configuration panels. */\r
-\r
-            ZeroMemory(&creg, sizeof(creg));\r
-\r
-            short_desc[0] = L'\0';\r
-\r
-            LoadString(hResModule, IDS_CFG_SHORT_DESC,\r
-                       short_desc, ARRAYLENGTH(short_desc));\r
-\r
-            long_desc[0] = L'\0';\r
-\r
-            LoadString(hResModule, IDS_CFG_LONG_DESC,\r
-                       long_desc, ARRAYLENGTH(long_desc));\r
-\r
-            creg.name = CONFIGNODE_MAIN;\r
-            creg.short_desc = short_desc;\r
-            creg.long_desc = long_desc;\r
-            creg.h_module = hResModule;\r
-            creg.dlg_template = MAKEINTRESOURCE(IDD_CONFIG);\r
-            creg.dlg_proc = config_dlgproc;\r
-            creg.flags = 0;\r
-\r
-            khui_cfg_register(NULL, &creg);\r
-\r
-            /* Now we do the identity specific and identity default\r
-               configuration panels. "KhmIdentities" is a predefined\r
-               configuration node under which all the identity spcific\r
-               configuration is managed. */\r
-\r
-            if (KHM_FAILED(khui_cfg_open(NULL, L"KhmIdentities", &cnode))) {\r
-                /* this should always work */\r
-                assert(FALSE);\r
-                rv = KHM_ERROR_NOT_FOUND;\r
-                break;\r
-            }\r
-\r
-            /* First the tab panel for defaults for all identities */\r
-\r
-            ZeroMemory(&creg, sizeof(creg));\r
-\r
-            short_desc[0] = L'\0';\r
-            LoadString(hResModule, IDS_CFG_IDS_SHORT_DESC,\r
-                       short_desc, ARRAYLENGTH(short_desc));\r
-            long_desc[0] = L'\0';\r
-            LoadString(hResModule, IDS_CFG_IDS_LONG_DESC,\r
-                       long_desc, ARRAYLENGTH(long_desc));\r
-\r
-            creg.name = CONFIGNODE_ALL_ID;\r
-            creg.short_desc = short_desc;\r
-            creg.long_desc = long_desc;\r
-            creg.h_module = hResModule;\r
-            creg.dlg_template = MAKEINTRESOURCE(IDD_CONFIG_IDS);\r
-            creg.dlg_proc = config_ids_dlgproc;\r
-            creg.flags = KHUI_CNFLAG_SUBPANEL;\r
-\r
-            khui_cfg_register(cnode, &creg);\r
-\r
-            /* Now the panel for per identity configuration */\r
-\r
-            ZeroMemory(&creg, sizeof(creg));\r
-\r
-            short_desc[0] = L'\0';\r
-            LoadString(hResModule, IDS_CFG_ID_SHORT_DESC,\r
-                       short_desc, ARRAYLENGTH(short_desc));\r
-            long_desc[0] = L'\0';\r
-            LoadString(hResModule, IDS_CFG_ID_LONG_DESC,\r
-                       long_desc, ARRAYLENGTH(long_desc));\r
-\r
-            creg.name = CONFIGNODE_PER_ID;\r
-            creg.short_desc = short_desc;\r
-            creg.long_desc = long_desc;\r
-            creg.h_module = hResModule;\r
-            creg.dlg_template = MAKEINTRESOURCE(IDD_CONFIG_ID);\r
-            creg.dlg_proc = config_id_dlgproc;\r
-            creg.flags = KHUI_CNFLAG_SUBPANEL | KHUI_CNFLAG_PLURAL;\r
-\r
-            khui_cfg_register(cnode, &creg);\r
-\r
-            khui_cfg_release(cnode);\r
-        }\r
-        break;\r
-\r
-        /* This is the last message that will be received by the\r
-           plugin. */\r
-    case KMSG_SYSTEM_EXIT:\r
-        {\r
-            khui_config_node cnode;\r
-            khui_config_node cn_idents;\r
-\r
-            /* It should not be assumed that initialization of the\r
-               plugin went well at this point since we receive a\r
-               KMSG_SYSTEM_EXIT even if the initialization failed. */\r
-\r
-            if (credtype_id != KCDB_CREDTYPE_INVALID) {\r
-                kcdb_credtype_unregister(credtype_id);\r
-                credtype_id = KCDB_CREDTYPE_INVALID;\r
-            }\r
-\r
-            if (g_credset) {\r
-                kcdb_credset_delete(g_credset);\r
-                g_credset = NULL;\r
-            }\r
-\r
-            /* Now unregister any configuration nodes we registered. */\r
-\r
-            if (KHM_SUCCEEDED(khui_cfg_open(NULL, CONFIGNODE_MAIN, &cnode))) {\r
-                khui_cfg_remove(cnode);\r
-                khui_cfg_release(cnode);\r
-            }\r
-\r
-            if (KHM_SUCCEEDED(khui_cfg_open(NULL, L"KhmIdentities", &cn_idents))) {\r
-                if (KHM_SUCCEEDED(khui_cfg_open(cn_idents,\r
-                                                CONFIGNODE_ALL_ID,\r
-                                                &cnode))) {\r
-                    khui_cfg_remove(cnode);\r
-                    khui_cfg_release(cnode);\r
-                }\r
-\r
-                if (KHM_SUCCEEDED(khui_cfg_open(cn_idents,\r
-                                                CONFIGNODE_PER_ID,\r
-                                                &cnode))) {\r
-                    khui_cfg_remove(cnode);\r
-                    khui_cfg_release(cnode);\r
-                }\r
-\r
-                khui_cfg_release(cn_idents);\r
-            }\r
-\r
-            /* TODO: Perform additional uninitialization\r
-               operations. */\r
-        }\r
-        break;\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-/* Handler for credentials the refresh message. */\r
-khm_int32\r
-handle_kmsg_cred_refresh(void) {\r
-    /* TODO: Re-enumerate the credentials of our credentials type */\r
-\r
-    /*\r
-      Re-enumerating credentials would look something like this:\r
-\r
-      - flush all credentials from g_credset (kcdb_credset_flush())\r
-\r
-      - list out the credentials and add them to g_credset\r
-\r
-      - collect the credentials from g_credset to the root credentials\r
-        set. (kcdb_credset_collect())\r
-\r
-      Note that when listing credentials, each credential must be\r
-      populated with enough information to locate the actual\r
-      credential at a later time.\r
-     */\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* Handler for destroying credentials */\r
-khm_int32\r
-handle_kmsg_cred_destroy_creds(khui_action_context * ctx) {\r
-    /* TODO: Destroy credentials of our type as specified by the\r
-       action context passed in through vparam. */\r
-\r
-    /* The credential set in ctx->credset contains the credentials\r
-       that are to be destroyed. */\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* Begin a property sheet */\r
-khm_int32\r
-handle_kmsg_cred_pp_begin(khui_property_sheet * ps) {\r
-\r
-    /* TODO: Provide the information necessary to show a property\r
-       page for a credentials belonging to our credential type. */\r
-\r
-    PROPSHEETPAGE *p;\r
-\r
-    if (ps->credtype == credtype_id &&\r
-        ps->cred) {\r
-        /* We have been requested to show a property sheet for one of\r
-           our credentials. */\r
-        p = malloc(sizeof(*p));\r
-        ZeroMemory(p, sizeof(*p));\r
-\r
-        p->dwSize = sizeof(*p);\r
-        p->dwFlags = 0;\r
-        p->hInstance = hResModule;\r
-        p->pszTemplate = MAKEINTRESOURCE(IDD_PP_CRED);\r
-        p->pfnDlgProc = pp_cred_dlg_proc;\r
-        p->lParam = (LPARAM) ps;\r
-        khui_ps_add_page(ps, credtype_id, 0, p, NULL);\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* End a property sheet */\r
-khm_int32\r
-handle_kmsg_cred_pp_end(khui_property_sheet * ps) {\r
-    /* TODO: Handle the end of a property sheet. */\r
-\r
-    khui_property_page * p = NULL;\r
-\r
-    khui_ps_find_page(ps, credtype_id, &p);\r
-    if (p) {\r
-        if (p->p_page)\r
-            free(p->p_page);\r
-        p->p_page = NULL;\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* IP address change notification */\r
-khm_int32\r
-handle_kmsg_cred_addr_change(void) {\r
-    /* TODO: Handle this message. */\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* Message dispatcher for credentials messages. */\r
-khm_int32 KHMAPI\r
-handle_kmsg_cred(khm_int32 msg_type,\r
-                 khm_int32 msg_subtype,\r
-                 khm_ui_4  uparam,\r
-                 void *    vparam) {\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    switch(msg_subtype) {\r
-    case KMSG_CRED_REFRESH:\r
-        return handle_kmsg_cred_refresh();\r
-\r
-    case KMSG_CRED_DESTROY_CREDS:\r
-        return handle_kmsg_cred_destroy_creds((khui_action_context *) vparam);\r
-\r
-    case KMSG_CRED_PP_BEGIN:\r
-        return handle_kmsg_cred_pp_begin((khui_property_sheet *) vparam);\r
-\r
-    case KMSG_CRED_PP_END:\r
-        return handle_kmsg_cred_pp_end((khui_property_sheet *) vparam);\r
-\r
-    case KMSG_CRED_ADDR_CHANGE:\r
-        return handle_kmsg_cred_addr_change();\r
-\r
-    default:\r
-        /* Credentials acquisition messages are all handled in a\r
-           different source file. */\r
-        if (IS_CRED_ACQ_MSG(msg_subtype))\r
-            return handle_cred_acq_msg(msg_type, msg_subtype,\r
-                                       uparam, vparam);\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-\r
-/* This is the main message handler for our plugin.  All the plugin\r
-   messages end up here where we either handle it directly or dispatch\r
-   it to other handlers. */\r
-khm_int32 KHMAPI plugin_msg_proc(khm_int32 msg_type,\r
-                                 khm_int32 msg_subtype,\r
-                                 khm_ui_4  uparam,\r
-                                 void * vparam) {\r
-\r
-    switch(msg_type) {\r
-    case KMSG_SYSTEM:\r
-        return handle_kmsg_system(msg_type, msg_subtype, uparam, vparam);\r
-\r
-    case KMSG_CRED:\r
-        return handle_kmsg_cred(msg_type, msg_subtype, uparam, vparam);\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
+/*
+ * Copyright (c) 2006 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 "credprov.h"
+#include<assert.h>
+
+/* This file provides the message processing function and the support
+   routines for implementing our plugin.  Note that some of the
+   message processing routines have been moved to other source files
+   based on their use.
+*/
+
+khm_int32 credtype_id = KCDB_CREDTYPE_INVALID;
+khm_handle g_credset = NULL;
+
+/* Handler for system messages.  The only two we handle are
+   KMSG_SYSTEM_INIT and KMSG_SYSTEM_EXIT. */
+khm_int32 KHMAPI
+handle_kmsg_system(khm_int32 msg_type,
+                   khm_int32 msg_subtype,
+                   khm_ui_4  uparam,
+                   void *    vparam) {
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+
+    switch (msg_subtype) {
+
+        /* This is the first message that will be received by a
+           plugin.  We use it to perform initialization operations
+           such as registering any credential types, data types and
+           attributes. */
+    case KMSG_SYSTEM_INIT:
+        {
+            kcdb_credtype ct;
+            wchar_t short_desc[KCDB_MAXCCH_SHORT_DESC];
+            wchar_t long_desc[KCDB_MAXCCH_LONG_DESC];
+            khui_config_node cnode;
+            khui_config_node_reg creg;
+
+            /* First and foremost, we need to register a credential
+               type. */
+            ZeroMemory(&ct, sizeof(ct));
+            ct.id = KCDB_CREDTYPE_AUTO;
+            ct.name = MYCREDTYPE_NAMEW;
+
+            short_desc[0] = L'\0';
+            LoadString(hResModule, IDS_CT_SHORT_DESC,
+                       short_desc, ARRAYLENGTH(short_desc));
+
+            long_desc[0] = L'\0';
+            LoadString(hResModule, IDS_CT_LONG_DESC,
+                       long_desc, ARRAYLENGTH(long_desc));
+
+            ct.icon = NULL;     /* We skip the icon for now, but you
+                                   can assign a handle to an icon
+                                   here.  The icon will be used to
+                                   represent the credentials type.*/
+
+            kmq_create_subscription(plugin_msg_proc, &ct.sub);
+
+            ct.is_equal = cred_is_equal;
+
+            rv = kcdb_credtype_register(&ct, &credtype_id);
+
+            /* We create a global credential set that we use in the
+               plug-in thread.  This alleviates the need to create one
+               everytime we need one. Keep in mind that this should
+               only be used in the plug-in thread and should not be
+               touched from the UI thread or any other thread. */
+            kcdb_credset_create(&g_credset);
+
+            /* TODO: Perform additional initialization operations. */
+
+            /* TODO: Also list out the credentials of this type that
+               already exist. */
+
+            /* Now we register our configuration panels. */
+
+
+            /* This configuration panel is the one that controls
+               general options.  We leave the identity specific and
+               identity defaults for other configuration panels. */
+
+            ZeroMemory(&creg, sizeof(creg));
+
+            short_desc[0] = L'\0';
+
+            LoadString(hResModule, IDS_CFG_SHORT_DESC,
+                       short_desc, ARRAYLENGTH(short_desc));
+
+            long_desc[0] = L'\0';
+
+            LoadString(hResModule, IDS_CFG_LONG_DESC,
+                       long_desc, ARRAYLENGTH(long_desc));
+
+            creg.name = CONFIGNODE_MAIN;
+            creg.short_desc = short_desc;
+            creg.long_desc = long_desc;
+            creg.h_module = hResModule;
+            creg.dlg_template = MAKEINTRESOURCE(IDD_CONFIG);
+            creg.dlg_proc = config_dlgproc;
+            creg.flags = 0;
+
+            khui_cfg_register(NULL, &creg);
+
+            /* Now we do the identity specific and identity default
+               configuration panels. "KhmIdentities" is a predefined
+               configuration node under which all the identity spcific
+               configuration is managed. */
+
+            if (KHM_FAILED(khui_cfg_open(NULL, L"KhmIdentities", &cnode))) {
+                /* this should always work */
+                assert(FALSE);
+                rv = KHM_ERROR_NOT_FOUND;
+                break;
+            }
+
+            /* First the tab panel for defaults for all identities */
+
+            ZeroMemory(&creg, sizeof(creg));
+
+            short_desc[0] = L'\0';
+            LoadString(hResModule, IDS_CFG_IDS_SHORT_DESC,
+                       short_desc, ARRAYLENGTH(short_desc));
+            long_desc[0] = L'\0';
+            LoadString(hResModule, IDS_CFG_IDS_LONG_DESC,
+                       long_desc, ARRAYLENGTH(long_desc));
+
+            creg.name = CONFIGNODE_ALL_ID;
+            creg.short_desc = short_desc;
+            creg.long_desc = long_desc;
+            creg.h_module = hResModule;
+            creg.dlg_template = MAKEINTRESOURCE(IDD_CONFIG_IDS);
+            creg.dlg_proc = config_ids_dlgproc;
+            creg.flags = KHUI_CNFLAG_SUBPANEL;
+
+            khui_cfg_register(cnode, &creg);
+
+            /* Now the panel for per identity configuration */
+
+            ZeroMemory(&creg, sizeof(creg));
+
+            short_desc[0] = L'\0';
+            LoadString(hResModule, IDS_CFG_ID_SHORT_DESC,
+                       short_desc, ARRAYLENGTH(short_desc));
+            long_desc[0] = L'\0';
+            LoadString(hResModule, IDS_CFG_ID_LONG_DESC,
+                       long_desc, ARRAYLENGTH(long_desc));
+
+            creg.name = CONFIGNODE_PER_ID;
+            creg.short_desc = short_desc;
+            creg.long_desc = long_desc;
+            creg.h_module = hResModule;
+            creg.dlg_template = MAKEINTRESOURCE(IDD_CONFIG_ID);
+            creg.dlg_proc = config_id_dlgproc;
+            creg.flags = KHUI_CNFLAG_SUBPANEL | KHUI_CNFLAG_PLURAL;
+
+            khui_cfg_register(cnode, &creg);
+
+            khui_cfg_release(cnode);
+        }
+        break;
+
+        /* This is the last message that will be received by the
+           plugin. */
+    case KMSG_SYSTEM_EXIT:
+        {
+            khui_config_node cnode;
+            khui_config_node cn_idents;
+
+            /* It should not be assumed that initialization of the
+               plugin went well at this point since we receive a
+               KMSG_SYSTEM_EXIT even if the initialization failed. */
+
+            if (credtype_id != KCDB_CREDTYPE_INVALID) {
+                kcdb_credtype_unregister(credtype_id);
+                credtype_id = KCDB_CREDTYPE_INVALID;
+            }
+
+            if (g_credset) {
+                kcdb_credset_delete(g_credset);
+                g_credset = NULL;
+            }
+
+            /* Now unregister any configuration nodes we registered. */
+
+            if (KHM_SUCCEEDED(khui_cfg_open(NULL, CONFIGNODE_MAIN, &cnode))) {
+                khui_cfg_remove(cnode);
+                khui_cfg_release(cnode);
+            }
+
+            if (KHM_SUCCEEDED(khui_cfg_open(NULL, L"KhmIdentities", &cn_idents))) {
+                if (KHM_SUCCEEDED(khui_cfg_open(cn_idents,
+                                                CONFIGNODE_ALL_ID,
+                                                &cnode))) {
+                    khui_cfg_remove(cnode);
+                    khui_cfg_release(cnode);
+                }
+
+                if (KHM_SUCCEEDED(khui_cfg_open(cn_idents,
+                                                CONFIGNODE_PER_ID,
+                                                &cnode))) {
+                    khui_cfg_remove(cnode);
+                    khui_cfg_release(cnode);
+                }
+
+                khui_cfg_release(cn_idents);
+            }
+
+            /* TODO: Perform additional uninitialization
+               operations. */
+        }
+        break;
+    }
+
+    return rv;
+}
+
+/* Handler for credentials the refresh message. */
+khm_int32
+handle_kmsg_cred_refresh(void) {
+    /* TODO: Re-enumerate the credentials of our credentials type */
+
+    /*
+      Re-enumerating credentials would look something like this:
+
+      - flush all credentials from g_credset (kcdb_credset_flush())
+
+      - list out the credentials and add them to g_credset
+
+      - collect the credentials from g_credset to the root credentials
+        set. (kcdb_credset_collect())
+
+      Note that when listing credentials, each credential must be
+      populated with enough information to locate the actual
+      credential at a later time.
+     */
+
+    return KHM_ERROR_SUCCESS;
+}
+
+/* Handler for destroying credentials */
+khm_int32
+handle_kmsg_cred_destroy_creds(khui_action_context * ctx) {
+    /* TODO: Destroy credentials of our type as specified by the
+       action context passed in through vparam. */
+
+    /* The credential set in ctx->credset contains the credentials
+       that are to be destroyed. */
+
+    return KHM_ERROR_SUCCESS;
+}
+
+/* Begin a property sheet */
+khm_int32
+handle_kmsg_cred_pp_begin(khui_property_sheet * ps) {
+
+    /* TODO: Provide the information necessary to show a property
+       page for a credentials belonging to our credential type. */
+
+    PROPSHEETPAGE *p;
+
+    if (ps->credtype == credtype_id &&
+        ps->cred) {
+        /* We have been requested to show a property sheet for one of
+           our credentials. */
+        p = malloc(sizeof(*p));
+        ZeroMemory(p, sizeof(*p));
+
+        p->dwSize = sizeof(*p);
+        p->dwFlags = 0;
+        p->hInstance = hResModule;
+        p->pszTemplate = MAKEINTRESOURCE(IDD_PP_CRED);
+        p->pfnDlgProc = pp_cred_dlg_proc;
+        p->lParam = (LPARAM) ps;
+        khui_ps_add_page(ps, credtype_id, 0, p, NULL);
+    }
+
+    return KHM_ERROR_SUCCESS;
+}
+
+/* End a property sheet */
+khm_int32
+handle_kmsg_cred_pp_end(khui_property_sheet * ps) {
+    /* TODO: Handle the end of a property sheet. */
+
+    khui_property_page * p = NULL;
+
+    khui_ps_find_page(ps, credtype_id, &p);
+    if (p) {
+        if (p->p_page)
+            free(p->p_page);
+        p->p_page = NULL;
+    }
+
+    return KHM_ERROR_SUCCESS;
+}
+
+/* IP address change notification */
+khm_int32
+handle_kmsg_cred_addr_change(void) {
+    /* TODO: Handle this message. */
+
+    return KHM_ERROR_SUCCESS;
+}
+
+/* Message dispatcher for credentials messages. */
+khm_int32 KHMAPI
+handle_kmsg_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:
+        return handle_kmsg_cred_refresh();
+
+    case KMSG_CRED_DESTROY_CREDS:
+        return handle_kmsg_cred_destroy_creds((khui_action_context *) vparam);
+
+    case KMSG_CRED_PP_BEGIN:
+        return handle_kmsg_cred_pp_begin((khui_property_sheet *) vparam);
+
+    case KMSG_CRED_PP_END:
+        return handle_kmsg_cred_pp_end((khui_property_sheet *) vparam);
+
+    case KMSG_CRED_ADDR_CHANGE:
+        return handle_kmsg_cred_addr_change();
+
+    default:
+        /* Credentials acquisition messages are all handled in a
+           different source file. */
+        if (IS_CRED_ACQ_MSG(msg_subtype))
+            return handle_cred_acq_msg(msg_type, msg_subtype,
+                                       uparam, vparam);
+    }
+
+    return rv;
+}
+
+
+/* This is the main message handler for our plugin.  All the plugin
+   messages end up here where we either handle it directly or dispatch
+   it to other handlers. */
+khm_int32 KHMAPI plugin_msg_proc(khm_int32 msg_type,
+                                 khm_int32 msg_subtype,
+                                 khm_ui_4  uparam,
+                                 void * vparam) {
+
+    switch(msg_type) {
+    case KMSG_SYSTEM:
+        return handle_kmsg_system(msg_type, msg_subtype, uparam, vparam);
+
+    case KMSG_CRED:
+        return handle_kmsg_cred(msg_type, msg_subtype, uparam, vparam);
+    }
+
+    return KHM_ERROR_SUCCESS;
+}
index 734a58ee4c488db9bcc0b6d7b42204e6746cdabd..eaffde23e0c6d5f68ef1c2468b9fcad34c5178c9 100644 (file)
@@ -1,59 +1,59 @@
-/*\r
- * Copyright (c) 2006 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include "credprov.h"\r
-\r
-/* Dialog procedure and support code for displaying property sheets\r
-   for credentials of type MyCred. */\r
-\r
-/* Dialog procedure for the property sheet.  This will run under the\r
-   UI thread when a property sheet is being displayed for one of our\r
-   credentials.. */\r
-INT_PTR CALLBACK\r
-pp_cred_dlg_proc(HWND hwnd,\r
-                 UINT uMsg,\r
-                 WPARAM wParam,\r
-                 LPARAM lParam) {\r
-\r
-    switch (uMsg) {\r
-    case WM_INITDIALOG:\r
-        {\r
-            khui_property_sheet * ps;\r
-            PROPSHEETPAGE * p;\r
-\r
-            p = (PROPSHEETPAGE *) lParam;\r
-            ps = (khui_property_sheet *) p->lParam;\r
-\r
-            /* TODO: Populate the property sheet controls with values\r
-               extracted from the credential. (ps->cred) */\r
-\r
-            return FALSE;\r
-        }\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
+/*
+ * Copyright (c) 2006 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 "credprov.h"
+
+/* Dialog procedure and support code for displaying property sheets
+   for credentials of type MyCred. */
+
+/* Dialog procedure for the property sheet.  This will run under the
+   UI thread when a property sheet is being displayed for one of our
+   credentials.. */
+INT_PTR CALLBACK
+pp_cred_dlg_proc(HWND hwnd,
+                 UINT uMsg,
+                 WPARAM wParam,
+                 LPARAM lParam) {
+
+    switch (uMsg) {
+    case WM_INITDIALOG:
+        {
+            khui_property_sheet * ps;
+            PROPSHEETPAGE * p;
+
+            p = (PROPSHEETPAGE *) lParam;
+            ps = (khui_property_sheet *) p->lParam;
+
+            /* TODO: Populate the property sheet controls with values
+               extracted from the credential. (ps->cred) */
+
+            return FALSE;
+        }
+    }
+
+    return FALSE;
+}
+
index 68aef02773b39e687872576d40a2d0dd5e795c14..cc88705d745062ac7cf2bc719f58e5a418e8bc0c 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khmapp.h>\r
-#include<netidmgr_intver.h>\r
-#include<tlhelp32.h>\r
-\r
-#if DEBUG\r
-#include<assert.h>\r
-#endif\r
-\r
-INT_PTR CALLBACK\r
-about_dlg_proc(HWND hwnd,\r
-               UINT uMsg,\r
-               WPARAM wParam,\r
-               LPARAM lParam) {\r
-\r
-    switch(uMsg) {\r
-    case WM_INITDIALOG:\r
-        {\r
-            HANDLE hsnap;\r
-            HWND hw;\r
-\r
-            SetDlgItemText(hwnd, IDC_PRODUCT,\r
-                           TEXT(KH_VERSTR_PRODUCT_1033));\r
-            /* retain the original copyright strings */\r
-#ifdef OVERRIDE_COPYRIGHT\r
-            SetDlgItemText(hwnd, IDC_COPYRIGHT,\r
-                           TEXT(KH_VERSTR_COPYRIGHT_1033));\r
-#endif\r
-            SetDlgItemText(hwnd, IDC_BUILDINFO,\r
-                           TEXT(KH_VERSTR_BUILDINFO_1033));\r
-\r
-            hsnap = \r
-                CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,\r
-                                         0);\r
-\r
-            if (hsnap != INVALID_HANDLE_VALUE) {\r
-                LVCOLUMN lvc;\r
-                MODULEENTRY32 mod;\r
-                RECT r;\r
-\r
-                hw = GetDlgItem(hwnd, IDC_MODULES);\r
-#ifdef DEBUG\r
-                assert(hw != NULL);\r
-#endif\r
-\r
-                GetWindowRect(hw, &r);\r
-                OffsetRect(&r, -r.left, -r.top);\r
-\r
-                ZeroMemory(&lvc, sizeof(lvc));\r
-                lvc.mask = LVCF_TEXT | LVCF_WIDTH;\r
-\r
-                lvc.pszText = L"Name";\r
-                lvc.cx = r.right / 4;\r
-\r
-                ListView_InsertColumn(hw, 0, &lvc);\r
-\r
-                lvc.pszText = L"Path";\r
-                lvc.cx = (r.right * 3) / 4;\r
-                ListView_InsertColumn(hw, 1, &lvc);\r
-\r
-                ZeroMemory(&mod, sizeof(mod));\r
-                mod.dwSize = sizeof(mod);\r
-\r
-                /* done with columns, now for the actual data */\r
-                if (!Module32First(hsnap, &mod))\r
-                    goto _done_with_modules;\r
-\r
-                do {\r
-\r
-                    LVITEM lvi;\r
-                    int idx;\r
-\r
-                    ZeroMemory(&lvi, sizeof(lvi));\r
-\r
-                    lvi.mask = LVIF_TEXT;\r
-                    lvi.pszText = mod.szModule;\r
-                    idx = ListView_InsertItem(hw, &lvi);\r
-\r
-                    lvi.mask = LVIF_TEXT;\r
-                    lvi.iItem = idx;\r
-                    lvi.iSubItem = 1;\r
-                    lvi.pszText = mod.szExePath;\r
-                    ListView_SetItem(hw, &lvi);\r
-\r
-                    ZeroMemory(&mod, sizeof(mod));\r
-                    mod.dwSize = sizeof(mod);\r
-                } while(Module32Next(hsnap, &mod));\r
-\r
-            _done_with_modules:\r
-                CloseHandle(hsnap);\r
-            }\r
-\r
-            khm_add_dialog(hwnd);\r
-            khm_enter_modal(hwnd);\r
-        }\r
-        return FALSE;\r
-\r
-    case WM_DESTROY:\r
-        khm_del_dialog(hwnd);\r
-        return TRUE;\r
-\r
-    case WM_CLOSE:\r
-        khm_leave_modal();\r
-        DestroyWindow(hwnd);\r
-        return TRUE;\r
-\r
-    case WM_COMMAND:\r
-        if (wParam == MAKEWPARAM(IDOK, BN_CLICKED)) {\r
-            khm_leave_modal();\r
-            DestroyWindow(hwnd);\r
-        }\r
-        return TRUE;\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-void\r
-khm_create_about_window(void) {\r
-    HWND hwnd;\r
-    hwnd = CreateDialog(khm_hInstance,\r
-                        MAKEINTRESOURCE(IDD_ABOUT),\r
-                        khm_hwnd_main,\r
-                        about_dlg_proc);\r
-\r
-    ShowWindow(hwnd, SW_SHOW);\r
-    /* no need to keep track of the hwnd, since we add it to the\r
-       dialog chain in the dialog procedure */\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<netidmgr_intver.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));
+            /* retain the original copyright strings */
+#ifdef OVERRIDE_COPYRIGHT
+            SetDlgItemText(hwnd, IDC_COPYRIGHT,
+                           TEXT(KH_VERSTR_COPYRIGHT_1033));
+#endif
+            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));
+
+            _done_with_modules:
+                CloseHandle(hsnap);
+            }
+
+            khm_add_dialog(hwnd);
+            khm_enter_modal(hwnd);
+        }
+        return FALSE;
+
+    case WM_DESTROY:
+        khm_del_dialog(hwnd);
+        return TRUE;
+
+    case WM_CLOSE:
+        khm_leave_modal();
+        DestroyWindow(hwnd);
+        return TRUE;
+
+    case WM_COMMAND:
+        if (wParam == MAKEWPARAM(IDOK, BN_CLICKED)) {
+            khm_leave_modal();
+            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 */
+}
index 81b7e9047033ebb518f29ebf902b2bd6cb98bfe9..7da709d93350297d430c4512cc88e1b5b468a1b2 100644 (file)
@@ -1,33 +1,33 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_ABOUTWND_H\r
-#define __KHIMAIRA_ABOUTWND_H\r
-\r
-void\r
-khm_create_about_window(void);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
index b37fca5347ce0053dd30163aa0e2b0d7e6f9e0d9..36ef2beeeead71ccac192a4a78aa0d42a37e95fc 100644 (file)
@@ -1,94 +1,94 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khmapp.h>\r
-#include<iphlpapi.h>\r
-\r
-static HANDLE evt_terminate = NULL;\r
-static HANDLE h_thread = NULL;\r
-\r
-DWORD WINAPI\r
-addr_change_thread(LPVOID dummy) {\r
-\r
-    HANDLE h_waits[2];\r
-    HANDLE h_notify;\r
-\r
-    OVERLAPPED overlap;\r
-    DWORD ret;\r
-\r
-    PDESCTHREAD(L"Address change waiter", L"App");\r
-\r
-    ZeroMemory(&overlap, sizeof(overlap));\r
-\r
-    h_notify = NULL;\r
-    overlap.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
-\r
-    do {\r
-        ret = NotifyAddrChange(&h_notify, &overlap);\r
-\r
-        if (ret != ERROR_IO_PENDING) {\r
-            goto _end_thread;   /* some error */\r
-        }\r
-\r
-        h_waits[0] = overlap.hEvent;\r
-        h_waits[1] = evt_terminate;\r
-\r
-        ret = WaitForMultipleObjects(2, h_waits, FALSE, INFINITE);\r
-\r
-        if ( ret == WAIT_OBJECT_0 ) {\r
-            Sleep(3000);        /* wait for things to settle down */\r
-            kmq_post_message(KMSG_CRED, KMSG_CRED_ADDR_CHANGE, 0, 0);\r
-        } else {\r
-            goto _end_thread;\r
-        }\r
-    } while(TRUE);\r
-    \r
- _end_thread:\r
-    ExitThread(0);\r
-    return 0;                   /* unreachable */\r
-}\r
-\r
-void\r
-khm_addr_change_notifier_init(void) {\r
-    evt_terminate = CreateEvent(NULL, FALSE, FALSE, NULL);\r
-    h_thread = CreateThread(NULL,\r
-                            64 * 4096,\r
-                            addr_change_thread,\r
-                            NULL,\r
-                            0,\r
-                            NULL);\r
-}\r
-\r
-void\r
-khm_addr_change_notifier_exit(void) {\r
-    if (h_thread && evt_terminate) {\r
-        SetEvent(evt_terminate);\r
-        WaitForSingleObject(h_thread, INFINITE);\r
-\r
-        CloseHandle(h_thread);\r
-        CloseHandle(evt_terminate);\r
-    }\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<iphlpapi.h>
+
+static HANDLE evt_terminate = NULL;
+static HANDLE h_thread = NULL;
+
+DWORD WINAPI
+addr_change_thread(LPVOID dummy) {
+
+    HANDLE h_waits[2];
+    HANDLE h_notify;
+
+    OVERLAPPED overlap;
+    DWORD ret;
+
+    PDESCTHREAD(L"Address change waiter", L"App");
+
+    ZeroMemory(&overlap, sizeof(overlap));
+
+    h_notify = NULL;
+    overlap.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+    do {
+        ret = NotifyAddrChange(&h_notify, &overlap);
+
+        if (ret != ERROR_IO_PENDING) {
+            goto _end_thread;   /* some error */
+        }
+
+        h_waits[0] = overlap.hEvent;
+        h_waits[1] = evt_terminate;
+
+        ret = WaitForMultipleObjects(2, h_waits, FALSE, INFINITE);
+
+        if ( ret == WAIT_OBJECT_0 ) {
+            Sleep(3000);        /* wait for things to settle down */
+            kmq_post_message(KMSG_CRED, KMSG_CRED_ADDR_CHANGE, 0, 0);
+        } else {
+            goto _end_thread;
+        }
+    } while(TRUE);
+    
+ _end_thread:
+    ExitThread(0);
+    return 0;                   /* unreachable */
+}
+
+void
+khm_addr_change_notifier_init(void) {
+    evt_terminate = CreateEvent(NULL, FALSE, FALSE, NULL);
+    h_thread = CreateThread(NULL,
+                            64 * 4096,
+                            addr_change_thread,
+                            NULL,
+                            0,
+                            NULL);
+}
+
+void
+khm_addr_change_notifier_exit(void) {
+    if (h_thread && evt_terminate) {
+        SetEvent(evt_terminate);
+        WaitForSingleObject(h_thread, INFINITE);
+
+        CloseHandle(h_thread);
+        CloseHandle(evt_terminate);
+    }
+}
index 08c15041f43f6beafee144754690f819c8ec3a0c..2f605af8c353ff9a713791ed8606c03f254fb91c 100644 (file)
@@ -1,36 +1,36 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __NETIDMGR_ADDRCHANGE_H\r
-#define __NETIDMGR_ADDRCHANGE_H\r
-\r
-void\r
-khm_addr_change_notifier_init(void);\r
-\r
-void\r
-khm_addr_change_notifier_exit(void);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 __NETIDMGR_ADDRCHANGE_H
+#define __NETIDMGR_ADDRCHANGE_H
+
+void
+khm_addr_change_notifier_init(void);
+
+void
+khm_addr_change_notifier_exit(void);
+
+#endif
index 3e44282d168a266a57040ddb7a65d8c58440590d..46e2d0dc9379229da8e5b7838c491950147fe8b7 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_APPGLOBAL_H\r
-#define __KHIMAIRA_APPGLOBAL_H\r
-\r
-/* Helpfile */\r
-#define NIDM_HELPFILE              L"netidmgr.chm"\r
-\r
-/* global data */\r
-extern HINSTANCE khm_hInstance;\r
-extern int khm_nCmdShow;\r
-extern const wchar_t * khm_facility;\r
-extern kconf_schema schema_uiconfig[];\r
-extern khm_ui_4 khm_commctl_version;\r
-extern const khm_version app_version;\r
-\r
-#define IS_COMMCTL6() (khm_commctl_version >= 0x60000)\r
-\r
-/* The structure used to send command-line options to a remote\r
-   NetIDMgr session for versions prior to 1.2. */\r
-struct tag_khm_startup_options_v1 {\r
-    BOOL seen;\r
-    BOOL processing;\r
-\r
-    BOOL init;\r
-    BOOL import;\r
-    BOOL renew;\r
-    BOOL destroy;\r
-\r
-    BOOL autoinit;\r
-    BOOL exit;\r
-    BOOL error_exit;\r
-\r
-    BOOL no_main_window;\r
-};\r
-\r
-/* Used on NetIDMgr versions 1.2 and later */\r
-struct tag_khm_startup_options_v2 {\r
-    khm_int32 magic;\r
-    DWORD cb_size;\r
-\r
-    BOOL init;\r
-    BOOL import;\r
-    BOOL renew;\r
-    BOOL destroy;\r
-\r
-    BOOL autoinit;\r
-    BOOL remote_exit;\r
-\r
-    khm_int32 code;\r
-} khm_startup_options_xfer;\r
-\r
-#define STARTUP_OPTIONS_MAGIC 0x1f280e41\r
-\r
-/* Used internally. */\r
-typedef struct tag_khm_startup_options_int {\r
-    BOOL seen;\r
-    BOOL processing;\r
-    BOOL remote;\r
-\r
-    BOOL init;\r
-    BOOL import;\r
-    BOOL renew;\r
-    BOOL destroy;\r
-\r
-    BOOL autoinit;\r
-    BOOL exit;\r
-    BOOL remote_exit;\r
-\r
-    BOOL error_exit;\r
-\r
-    BOOL no_main_window;\r
-\r
-    LONG pending_renewals;\r
-} khm_startup_options;\r
-\r
-extern khm_startup_options khm_startup;\r
-\r
-/* Used to query a remote instance of NetIDMgr for the version. */\r
-typedef struct tag_khm_query_app_version_v1 {\r
-    khm_int32 magic;\r
-\r
-    khm_int32 code;\r
-\r
-    khm_version ver_caller;\r
-    khm_version ver_remote;\r
-\r
-    khm_boolean request_swap;\r
-} khm_query_app_version;\r
-\r
-#define KHM_QUERY_APP_VER_MAGIC 0x38f8c2eb\r
-\r
-void khm_add_dialog(HWND dlg);\r
-void khm_del_dialog(HWND dlg);\r
-BOOL khm_is_dialog_active(void);\r
-\r
-void khm_enter_modal(HWND hwnd);\r
-void khm_leave_modal(void);\r
-\r
-void khm_add_property_sheet(khui_property_sheet * s);\r
-void khm_del_property_sheet(khui_property_sheet * s);\r
-\r
-void khm_init_gui(void);\r
-void khm_exit_gui(void);\r
-\r
-void khm_parse_commandline();\r
-void khm_register_window_classes(void);\r
-\r
-HWND khm_html_help(HWND hwnd, wchar_t * suffix, UINT command, DWORD_PTR data);\r
-\r
-WPARAM khm_message_loop_int(khm_boolean * p_exit);\r
-\r
-int khm_compare_version(const khm_version * v1, const khm_version * v2);\r
-\r
-#define MAX_RES_STRING 1024\r
-\r
-#define ELLIPSIS L"..."\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
+
+/* Helpfile */
+#define NIDM_HELPFILE              L"netidmgr.chm"
+
+/* global data */
+extern HINSTANCE khm_hInstance;
+extern int khm_nCmdShow;
+extern const wchar_t * khm_facility;
+extern kconf_schema schema_uiconfig[];
+extern khm_ui_4 khm_commctl_version;
+extern const khm_version app_version;
+
+#define IS_COMMCTL6() (khm_commctl_version >= 0x60000)
+
+/* The structure used to send command-line options to a remote
+   NetIDMgr session for versions prior to 1.2. */
+struct tag_khm_startup_options_v1 {
+    BOOL seen;
+    BOOL processing;
+
+    BOOL init;
+    BOOL import;
+    BOOL renew;
+    BOOL destroy;
+
+    BOOL autoinit;
+    BOOL exit;
+    BOOL error_exit;
+
+    BOOL no_main_window;
+};
+
+/* Used on NetIDMgr versions 1.2 and later */
+struct tag_khm_startup_options_v2 {
+    khm_int32 magic;
+    DWORD cb_size;
+
+    BOOL init;
+    BOOL import;
+    BOOL renew;
+    BOOL destroy;
+
+    BOOL autoinit;
+    BOOL remote_exit;
+
+    khm_int32 code;
+} khm_startup_options_xfer;
+
+#define STARTUP_OPTIONS_MAGIC 0x1f280e41
+
+/* Used internally. */
+typedef struct tag_khm_startup_options_int {
+    BOOL seen;
+    BOOL processing;
+    BOOL remote;
+
+    BOOL init;
+    BOOL import;
+    BOOL renew;
+    BOOL destroy;
+
+    BOOL autoinit;
+    BOOL exit;
+    BOOL remote_exit;
+
+    BOOL error_exit;
+
+    BOOL no_main_window;
+
+    LONG pending_renewals;
+} khm_startup_options;
+
+extern khm_startup_options khm_startup;
+
+/* Used to query a remote instance of NetIDMgr for the version. */
+typedef struct tag_khm_query_app_version_v1 {
+    khm_int32 magic;
+
+    khm_int32 code;
+
+    khm_version ver_caller;
+    khm_version ver_remote;
+
+    khm_boolean request_swap;
+} khm_query_app_version;
+
+#define KHM_QUERY_APP_VER_MAGIC 0x38f8c2eb
+
+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);
+
+HWND khm_html_help(HWND hwnd, wchar_t * suffix, UINT command, DWORD_PTR data);
+
+WPARAM khm_message_loop_int(khm_boolean * p_exit);
+
+int khm_compare_version(const khm_version * v1, const khm_version * v2);
+
+#define MAX_RES_STRING 1024
+
+#define ELLIPSIS L"..."
+
+#endif
index bca270979237bf45148662163a34850ba2d24530..c7e9abc257522d8b1c232867d0701700df46eacd 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khmapp.h>\r
-#include<assert.h>\r
-\r
-static int text_sizes[] = {\r
-    4,6,8,9,10,12,14,16,18\r
-};\r
-\r
-typedef struct tag_dlg_data {\r
-    khui_config_node  node;\r
-    HWND              hwnd;\r
-    LOGFONT           lf_base;\r
-    LOGFONT           lf_work;\r
-    HFONT             c_font_normal;\r
-    HFONT             c_font_bold;\r
-    int               size_idx[ARRAYLENGTH(text_sizes)];\r
-} dlg_data;\r
-\r
-static void\r
-read_params(HWND hwnd, dlg_data * d) {\r
-\r
-    HDC hdc;\r
-\r
-    hdc = GetWindowDC(hwnd);\r
-\r
-    khm_get_cw_element_font(hdc,\r
-                            NULL,\r
-                            FALSE,\r
-                            &d->lf_base);\r
-\r
-    d->lf_work = d->lf_base;\r
-\r
-    ReleaseDC(hwnd, hdc);\r
-}\r
-\r
-\r
-static void\r
-write_params(dlg_data * d) {\r
-    khm_boolean applied = FALSE;\r
-\r
-    if (memcmp(&d->lf_work, &d->lf_base, sizeof(LOGFONT))) {\r
-        khm_set_cw_element_font(NULL, &d->lf_work);\r
-        d->lf_base = d->lf_work;\r
-        applied = TRUE;\r
-    }\r
-\r
-    khui_cfg_set_flags(d->node,\r
-                       (applied)? KHUI_CNFLAG_APPLIED: 0,\r
-                       KHUI_CNFLAG_APPLIED | KHUI_CNFLAG_MODIFIED);\r
-}\r
-\r
-static void\r
-check_for_modification(dlg_data * d) {\r
-\r
-    khui_cfg_set_flags(d->node,\r
-                       ((memcmp(&d->lf_work, &d->lf_base, sizeof(LOGFONT)))?\r
-                        KHUI_CNFLAG_MODIFIED: 0),\r
-                       KHUI_CNFLAG_MODIFIED);\r
-}\r
-\r
-static void\r
-refresh_view(HWND hwnd, dlg_data * d) {\r
-    wchar_t sample[256];\r
-    HFONT hf;\r
-    LOGFONT lf;\r
-\r
-    LoadString(khm_hInstance, IDS_APR_SAMPLE_TEXT_NORMAL,\r
-               sample, ARRAYLENGTH(sample));\r
-\r
-    SetDlgItemText(hwnd, IDC_CFG_SAMPLE_NORMAL, sample);\r
-\r
-    LoadString(khm_hInstance, IDS_APR_SAMPLE_TEXT_SEL,\r
-               sample, ARRAYLENGTH(sample));\r
-\r
-    SetDlgItemText(hwnd, IDC_CFG_SAMPLE_BOLD, sample);\r
-\r
-    lf = d->lf_work;\r
-    hf = CreateFontIndirect(&lf);\r
-    if (hf == NULL)\r
-        return;\r
-\r
-    SendDlgItemMessage(hwnd, IDC_CFG_SAMPLE_NORMAL, WM_SETFONT, (WPARAM) hf, TRUE);\r
-\r
-    if (d->c_font_normal)\r
-        DeleteObject(d->c_font_normal);\r
-\r
-    d->c_font_normal = hf;\r
-\r
-    lf.lfWeight = FW_BOLD;\r
-\r
-    hf = CreateFontIndirect(&lf);\r
-    if (hf == NULL)\r
-        return;\r
-\r
-    SendDlgItemMessage(hwnd, IDC_CFG_SAMPLE_BOLD, WM_SETFONT, (WPARAM) hf, TRUE);\r
-\r
-    if (d->c_font_bold)\r
-        DeleteObject(d->c_font_bold);\r
-\r
-    d->c_font_bold = hf;\r
-}\r
-\r
-struct sel_update_blob {\r
-    dlg_data * d;\r
-    HDC hdc;\r
-};\r
-\r
-static int CALLBACK\r
-enum_font_proc(ENUMLOGFONTEXDV * plfe,\r
-               ENUMTEXTMETRIC * pntm,\r
-               DWORD font_type,\r
-               LPARAM lParam) {\r
-    struct sel_update_blob * blob = (struct sel_update_blob *) lParam;\r
-    LOGFONT * plf = &plfe->elfEnumLogfontEx.elfLogFont;\r
-    LRESULT lr;\r
-\r
-    lr = SendDlgItemMessage(blob->d->hwnd,\r
-                            IDC_CFG_FONTS,\r
-                            CB_SELECTSTRING,\r
-                            (WPARAM) -1,\r
-                            (LPARAM) plf->lfFaceName);\r
-\r
-    if (lr == CB_ERR) {\r
-        SendDlgItemMessage(blob->d->hwnd,\r
-                           IDC_CFG_FONTS,\r
-                           CB_SELECTSTRING,\r
-                           (WPARAM) -1,\r
-                           (LPARAM) plfe->elfEnumLogfontEx.elfFullName);\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-static void\r
-update_selection(dlg_data * d, BOOL update_fonts, BOOL update_effects) {\r
-    LOGFONT lf;\r
-    struct sel_update_blob blob;\r
-    HDC hdc;\r
-\r
-    if (update_fonts) {\r
-\r
-        ZeroMemory(&lf, sizeof(lf));\r
-\r
-        lf.lfCharSet = ANSI_CHARSET;\r
-        StringCbCopy(lf.lfFaceName, sizeof(lf.lfFaceName),\r
-                     d->lf_work.lfFaceName);\r
-\r
-        hdc = GetWindowDC(d->hwnd);\r
-\r
-        blob.d = d;\r
-        blob.hdc = hdc;\r
-\r
-        EnumFontFamiliesEx(hdc, &lf, (FONTENUMPROC) enum_font_proc,\r
-                           (LPARAM) &blob, 0);\r
-\r
-        ReleaseDC(d->hwnd, hdc);\r
-    }\r
-\r
-    if (update_effects) {\r
-        int i;\r
-        HDC hdc;\r
-        int pt_height;\r
-\r
-        if (d->lf_work.lfWeight >= FW_BOLD)\r
-            CheckDlgButton(d->hwnd, IDC_CFG_BOLD, BST_CHECKED);\r
-        else\r
-            CheckDlgButton(d->hwnd, IDC_CFG_BOLD, BST_UNCHECKED);\r
-\r
-        if (d->lf_work.lfItalic)\r
-            CheckDlgButton(d->hwnd, IDC_CFG_ITALICS, BST_CHECKED);\r
-        else\r
-            CheckDlgButton(d->hwnd, IDC_CFG_ITALICS, BST_UNCHECKED);\r
-\r
-        hdc = GetWindowDC(d->hwnd);\r
-\r
-        pt_height = MulDiv(d->lf_work.lfHeight, 72,\r
-                           GetDeviceCaps(hdc, LOGPIXELSY));\r
-\r
-\r
-        ReleaseDC(d->hwnd, hdc);\r
-\r
-        if (pt_height < 0)\r
-            pt_height = - pt_height;\r
-\r
-        for (i=0; i < ARRAYLENGTH(text_sizes); i++) {\r
-            if (text_sizes[i] >= pt_height)\r
-                break;\r
-        }\r
-\r
-        if (i >= ARRAYLENGTH(text_sizes))\r
-            i = ARRAYLENGTH(text_sizes) - 1;\r
-\r
-        SendDlgItemMessage(d->hwnd, IDC_CFG_SIZE, CB_SETCURSEL,\r
-                           d->size_idx[i], 0);\r
-    }\r
-}\r
-\r
-static int CALLBACK\r
-enum_font_families_proc(ENUMLOGFONTEXDV * plfe,\r
-                        ENUMTEXTMETRIC * pntm,\r
-                        DWORD font_type,\r
-                        LPARAM lParam) {\r
-\r
-    dlg_data * d = (dlg_data *) lParam;\r
-\r
-    SendDlgItemMessage(d->hwnd, IDC_CFG_FONTS,\r
-                       CB_ADDSTRING, 0,\r
-                       (LPARAM) plfe->elfEnumLogfontEx.elfLogFont.lfFaceName);\r
-\r
-    return TRUE;\r
-}\r
-\r
-INT_PTR CALLBACK\r
-khm_cfg_appearance_proc(HWND hwnd,\r
-                        UINT uMsg,\r
-                        WPARAM wParam,\r
-                        LPARAM lParam) {\r
-\r
-    dlg_data * d;\r
-\r
-    switch(uMsg) {\r
-    case WM_INITDIALOG:\r
-        {\r
-            HWND hw_cb;\r
-            LOGFONT lf;\r
-            HDC hdc;\r
-            int i;\r
-            wchar_t buf[4];\r
-\r
-            d = PMALLOC(sizeof(*d));\r
-#ifdef DEBUG\r
-            assert(d != NULL);\r
-#endif\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-            SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);\r
-#pragma warning(pop)\r
-\r
-            ZeroMemory(d, sizeof(*d));\r
-\r
-            d->node = (khui_config_node) lParam;\r
-            d->hwnd = hwnd;\r
-\r
-            read_params(hwnd, d);\r
-\r
-            hw_cb = GetDlgItem(hwnd, IDC_CFG_FONTS);\r
-#ifdef DEBUG\r
-            assert(hw_cb);\r
-#endif\r
-            SendMessage(hw_cb, CB_RESETCONTENT, 0, 0);\r
-\r
-            ZeroMemory(&lf, sizeof(lf));\r
-            lf.lfCharSet = ANSI_CHARSET;\r
-\r
-            hdc = GetWindowDC(hwnd);\r
-\r
-            EnumFontFamiliesEx(hdc, &lf, (FONTENUMPROC) enum_font_families_proc,\r
-                               (LPARAM) d, 0);\r
-\r
-            ReleaseDC(hwnd, hdc);\r
-\r
-\r
-            for (i=0; i < ARRAYLENGTH(text_sizes); i++) {\r
-                LRESULT idx;\r
-\r
-                StringCbPrintf(buf, sizeof(buf), L"%d", text_sizes[i]);\r
-\r
-                idx = SendDlgItemMessage(hwnd, IDC_CFG_SIZE,\r
-                                         CB_ADDSTRING, 0, (LPARAM) buf);\r
-\r
-                SendDlgItemMessage(hwnd, IDC_CFG_SIZE,\r
-                                   CB_SETITEMDATA, idx, text_sizes[i]);\r
-\r
-                d->size_idx[i] = (int) idx;\r
-            }\r
-\r
-            update_selection(d, TRUE, TRUE);\r
-\r
-            refresh_view(hwnd, d);\r
-        }\r
-        return FALSE;\r
-\r
-    case WM_COMMAND:\r
-        d = (dlg_data *) (LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        if (wParam == MAKEWPARAM(IDC_CFG_FONTS, CBN_SELCHANGE)) {\r
-            LRESULT idx;\r
-            wchar_t facename[LF_FACESIZE];\r
-\r
-            idx = SendDlgItemMessage(hwnd, IDC_CFG_FONTS,\r
-                                     CB_GETCURSEL,\r
-                                     0, 0);\r
-\r
-            if (idx == CB_ERR)\r
-                return TRUE;\r
-\r
-            if (SendDlgItemMessage(hwnd, IDC_CFG_FONTS,\r
-                                   CB_GETLBTEXTLEN, idx, 0)\r
-                >= ARRAYLENGTH(facename))\r
-                return TRUE;\r
-\r
-            SendDlgItemMessage(hwnd, IDC_CFG_FONTS,\r
-                               CB_GETLBTEXT, idx,\r
-                               (LPARAM) facename);\r
-\r
-            ZeroMemory(d->lf_work.lfFaceName,\r
-                       sizeof(d->lf_work.lfFaceName));\r
-\r
-            StringCbCopy(d->lf_work.lfFaceName,\r
-                         sizeof(d->lf_work.lfFaceName),\r
-                         facename);\r
-\r
-            update_selection(d, FALSE, FALSE);\r
-\r
-            refresh_view(hwnd, d);\r
-\r
-            check_for_modification(d);\r
-\r
-        } else if (wParam == MAKEWPARAM(IDC_CFG_BOLD, BN_CLICKED)) {\r
-\r
-            if (IsDlgButtonChecked(hwnd, IDC_CFG_BOLD) == BST_CHECKED) {\r
-                d->lf_work.lfWeight = FW_BOLD;\r
-            } else {\r
-                d->lf_work.lfWeight = 0;\r
-            }\r
-\r
-            refresh_view(hwnd, d);\r
-\r
-            check_for_modification(d);\r
-\r
-        } else if (wParam == MAKEWPARAM(IDC_CFG_ITALICS, BN_CLICKED)) {\r
-\r
-            d->lf_work.lfItalic =\r
-                (IsDlgButtonChecked(hwnd, IDC_CFG_ITALICS) == BST_CHECKED);\r
-\r
-            refresh_view(hwnd, d);\r
-\r
-            check_for_modification(d);\r
-\r
-        } else if (wParam == MAKEWPARAM(IDC_CFG_REVERT, BN_CLICKED)) {\r
-            HDC hdc;\r
-\r
-            hdc = GetWindowDC(hwnd);\r
-\r
-            khm_get_cw_element_font(hdc, NULL, TRUE, &d->lf_work);\r
-\r
-            ReleaseDC(hwnd, hdc);\r
-\r
-            update_selection(d, TRUE, TRUE);\r
-\r
-            refresh_view(hwnd, d);\r
-\r
-            check_for_modification(d);\r
-\r
-        } else if (wParam == MAKEWPARAM(IDC_CFG_SIZE, CBN_SELCHANGE)) {\r
-            HDC hdc;\r
-            LPARAM idx;\r
-            int points;\r
-\r
-            idx = SendDlgItemMessage(hwnd, IDC_CFG_SIZE,\r
-                                     CB_GETCURSEL, 0, 0);\r
-            if (idx == CB_ERR)\r
-                return TRUE;\r
-\r
-            points = (int) SendDlgItemMessage(hwnd, IDC_CFG_SIZE,\r
-                                              CB_GETITEMDATA, idx, 0);\r
-\r
-            hdc = GetWindowDC(hwnd);\r
-\r
-            d->lf_work.lfHeight = -MulDiv(points,\r
-                                          GetDeviceCaps(hdc, LOGPIXELSY),\r
-                                          72);\r
-\r
-            ReleaseDC(hwnd, hdc);\r
-\r
-            refresh_view(hwnd, d);\r
-\r
-            check_for_modification(d);\r
-        }\r
-\r
-        return TRUE;\r
-\r
-    case WM_DESTROY:\r
-        d = (dlg_data *) (LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        if (d) {\r
-            if (d->c_font_bold)\r
-                DeleteObject(d->c_font_bold);\r
-\r
-            if (d->c_font_normal)\r
-                DeleteObject(d->c_font_normal);\r
-\r
-            PFREE(d);\r
-        }\r
-        return TRUE;\r
-\r
-    case KHUI_WM_CFG_NOTIFY:\r
-        d = (dlg_data *) (LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        if (HIWORD(wParam) == WMCFG_APPLY) {\r
-            write_params(d);\r
-            khui_action_trigger(KHUI_ACTION_LAYOUT_RELOAD, NULL);\r
-        }\r
-\r
-        return TRUE;\r
-    }\r
-\r
-    return FALSE;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 int text_sizes[] = {
+    4,6,8,9,10,12,14,16,18
+};
+
+typedef struct tag_dlg_data {
+    khui_config_node  node;
+    HWND              hwnd;
+    LOGFONT           lf_base;
+    LOGFONT           lf_work;
+    HFONT             c_font_normal;
+    HFONT             c_font_bold;
+    int               size_idx[ARRAYLENGTH(text_sizes)];
+} dlg_data;
+
+static void
+read_params(HWND hwnd, dlg_data * d) {
+
+    HDC hdc;
+
+    hdc = GetWindowDC(hwnd);
+
+    khm_get_cw_element_font(hdc,
+                            NULL,
+                            FALSE,
+                            &d->lf_base);
+
+    d->lf_work = d->lf_base;
+
+    ReleaseDC(hwnd, hdc);
+}
+
+
+static void
+write_params(dlg_data * d) {
+    khm_boolean applied = FALSE;
+
+    if (memcmp(&d->lf_work, &d->lf_base, sizeof(LOGFONT))) {
+        khm_set_cw_element_font(NULL, &d->lf_work);
+        d->lf_base = d->lf_work;
+        applied = TRUE;
+    }
+
+    khui_cfg_set_flags(d->node,
+                       (applied)? KHUI_CNFLAG_APPLIED: 0,
+                       KHUI_CNFLAG_APPLIED | KHUI_CNFLAG_MODIFIED);
+}
+
+static void
+check_for_modification(dlg_data * d) {
+
+    khui_cfg_set_flags(d->node,
+                       ((memcmp(&d->lf_work, &d->lf_base, sizeof(LOGFONT)))?
+                        KHUI_CNFLAG_MODIFIED: 0),
+                       KHUI_CNFLAG_MODIFIED);
+}
+
+static void
+refresh_view(HWND hwnd, dlg_data * d) {
+    wchar_t sample[256];
+    HFONT hf;
+    LOGFONT lf;
+
+    LoadString(khm_hInstance, IDS_APR_SAMPLE_TEXT_NORMAL,
+               sample, ARRAYLENGTH(sample));
+
+    SetDlgItemText(hwnd, IDC_CFG_SAMPLE_NORMAL, sample);
+
+    LoadString(khm_hInstance, IDS_APR_SAMPLE_TEXT_SEL,
+               sample, ARRAYLENGTH(sample));
+
+    SetDlgItemText(hwnd, IDC_CFG_SAMPLE_BOLD, sample);
+
+    lf = d->lf_work;
+    hf = CreateFontIndirect(&lf);
+    if (hf == NULL)
+        return;
+
+    SendDlgItemMessage(hwnd, IDC_CFG_SAMPLE_NORMAL, WM_SETFONT, (WPARAM) hf, TRUE);
+
+    if (d->c_font_normal)
+        DeleteObject(d->c_font_normal);
+
+    d->c_font_normal = hf;
+
+    lf.lfWeight = FW_BOLD;
+
+    hf = CreateFontIndirect(&lf);
+    if (hf == NULL)
+        return;
+
+    SendDlgItemMessage(hwnd, IDC_CFG_SAMPLE_BOLD, WM_SETFONT, (WPARAM) hf, TRUE);
+
+    if (d->c_font_bold)
+        DeleteObject(d->c_font_bold);
+
+    d->c_font_bold = hf;
+}
+
+struct sel_update_blob {
+    dlg_data * d;
+    HDC hdc;
+};
+
+static int CALLBACK
+enum_font_proc(ENUMLOGFONTEXDV * plfe,
+               ENUMTEXTMETRIC * pntm,
+               DWORD font_type,
+               LPARAM lParam) {
+    struct sel_update_blob * blob = (struct sel_update_blob *) lParam;
+    LOGFONT * plf = &plfe->elfEnumLogfontEx.elfLogFont;
+    LRESULT lr;
+
+    lr = SendDlgItemMessage(blob->d->hwnd,
+                            IDC_CFG_FONTS,
+                            CB_SELECTSTRING,
+                            (WPARAM) -1,
+                            (LPARAM) plf->lfFaceName);
+
+    if (lr == CB_ERR) {
+        SendDlgItemMessage(blob->d->hwnd,
+                           IDC_CFG_FONTS,
+                           CB_SELECTSTRING,
+                           (WPARAM) -1,
+                           (LPARAM) plfe->elfEnumLogfontEx.elfFullName);
+    }
+
+    return FALSE;
+}
+
+static void
+update_selection(dlg_data * d, BOOL update_fonts, BOOL update_effects) {
+    LOGFONT lf;
+    struct sel_update_blob blob;
+    HDC hdc;
+
+    if (update_fonts) {
+
+        ZeroMemory(&lf, sizeof(lf));
+
+        lf.lfCharSet = ANSI_CHARSET;
+        StringCbCopy(lf.lfFaceName, sizeof(lf.lfFaceName),
+                     d->lf_work.lfFaceName);
+
+        hdc = GetWindowDC(d->hwnd);
+
+        blob.d = d;
+        blob.hdc = hdc;
+
+        EnumFontFamiliesEx(hdc, &lf, (FONTENUMPROC) enum_font_proc,
+                           (LPARAM) &blob, 0);
+
+        ReleaseDC(d->hwnd, hdc);
+    }
+
+    if (update_effects) {
+        int i;
+        HDC hdc;
+        int pt_height;
+
+        if (d->lf_work.lfWeight >= FW_BOLD)
+            CheckDlgButton(d->hwnd, IDC_CFG_BOLD, BST_CHECKED);
+        else
+            CheckDlgButton(d->hwnd, IDC_CFG_BOLD, BST_UNCHECKED);
+
+        if (d->lf_work.lfItalic)
+            CheckDlgButton(d->hwnd, IDC_CFG_ITALICS, BST_CHECKED);
+        else
+            CheckDlgButton(d->hwnd, IDC_CFG_ITALICS, BST_UNCHECKED);
+
+        hdc = GetWindowDC(d->hwnd);
+
+        pt_height = MulDiv(d->lf_work.lfHeight, 72,
+                           GetDeviceCaps(hdc, LOGPIXELSY));
+
+
+        ReleaseDC(d->hwnd, hdc);
+
+        if (pt_height < 0)
+            pt_height = - pt_height;
+
+        for (i=0; i < ARRAYLENGTH(text_sizes); i++) {
+            if (text_sizes[i] >= pt_height)
+                break;
+        }
+
+        if (i >= ARRAYLENGTH(text_sizes))
+            i = ARRAYLENGTH(text_sizes) - 1;
+
+        SendDlgItemMessage(d->hwnd, IDC_CFG_SIZE, CB_SETCURSEL,
+                           d->size_idx[i], 0);
+    }
+}
+
+static int CALLBACK
+enum_font_families_proc(ENUMLOGFONTEXDV * plfe,
+                        ENUMTEXTMETRIC * pntm,
+                        DWORD font_type,
+                        LPARAM lParam) {
+
+    dlg_data * d = (dlg_data *) lParam;
+
+    SendDlgItemMessage(d->hwnd, IDC_CFG_FONTS,
+                       CB_ADDSTRING, 0,
+                       (LPARAM) plfe->elfEnumLogfontEx.elfLogFont.lfFaceName);
+
+    return TRUE;
+}
+
+INT_PTR CALLBACK
+khm_cfg_appearance_proc(HWND hwnd,
+                        UINT uMsg,
+                        WPARAM wParam,
+                        LPARAM lParam) {
+
+    dlg_data * d;
+
+    switch(uMsg) {
+    case WM_INITDIALOG:
+        {
+            HWND hw_cb;
+            LOGFONT lf;
+            HDC hdc;
+            int i;
+            wchar_t buf[4];
+
+            d = PMALLOC(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;
+            d->hwnd = hwnd;
+
+            read_params(hwnd, d);
+
+            hw_cb = GetDlgItem(hwnd, IDC_CFG_FONTS);
+#ifdef DEBUG
+            assert(hw_cb);
+#endif
+            SendMessage(hw_cb, CB_RESETCONTENT, 0, 0);
+
+            ZeroMemory(&lf, sizeof(lf));
+            lf.lfCharSet = ANSI_CHARSET;
+
+            hdc = GetWindowDC(hwnd);
+
+            EnumFontFamiliesEx(hdc, &lf, (FONTENUMPROC) enum_font_families_proc,
+                               (LPARAM) d, 0);
+
+            ReleaseDC(hwnd, hdc);
+
+
+            for (i=0; i < ARRAYLENGTH(text_sizes); i++) {
+                LRESULT idx;
+
+                StringCbPrintf(buf, sizeof(buf), L"%d", text_sizes[i]);
+
+                idx = SendDlgItemMessage(hwnd, IDC_CFG_SIZE,
+                                         CB_ADDSTRING, 0, (LPARAM) buf);
+
+                SendDlgItemMessage(hwnd, IDC_CFG_SIZE,
+                                   CB_SETITEMDATA, idx, text_sizes[i]);
+
+                d->size_idx[i] = (int) idx;
+            }
+
+            update_selection(d, TRUE, TRUE);
+
+            refresh_view(hwnd, d);
+        }
+        return FALSE;
+
+    case WM_COMMAND:
+        d = (dlg_data *) (LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER);
+
+        if (wParam == MAKEWPARAM(IDC_CFG_FONTS, CBN_SELCHANGE)) {
+            LRESULT idx;
+            wchar_t facename[LF_FACESIZE];
+
+            idx = SendDlgItemMessage(hwnd, IDC_CFG_FONTS,
+                                     CB_GETCURSEL,
+                                     0, 0);
+
+            if (idx == CB_ERR)
+                return TRUE;
+
+            if (SendDlgItemMessage(hwnd, IDC_CFG_FONTS,
+                                   CB_GETLBTEXTLEN, idx, 0)
+                >= ARRAYLENGTH(facename))
+                return TRUE;
+
+            SendDlgItemMessage(hwnd, IDC_CFG_FONTS,
+                               CB_GETLBTEXT, idx,
+                               (LPARAM) facename);
+
+            ZeroMemory(d->lf_work.lfFaceName,
+                       sizeof(d->lf_work.lfFaceName));
+
+            StringCbCopy(d->lf_work.lfFaceName,
+                         sizeof(d->lf_work.lfFaceName),
+                         facename);
+
+            update_selection(d, FALSE, FALSE);
+
+            refresh_view(hwnd, d);
+
+            check_for_modification(d);
+
+        } else if (wParam == MAKEWPARAM(IDC_CFG_BOLD, BN_CLICKED)) {
+
+            if (IsDlgButtonChecked(hwnd, IDC_CFG_BOLD) == BST_CHECKED) {
+                d->lf_work.lfWeight = FW_BOLD;
+            } else {
+                d->lf_work.lfWeight = 0;
+            }
+
+            refresh_view(hwnd, d);
+
+            check_for_modification(d);
+
+        } else if (wParam == MAKEWPARAM(IDC_CFG_ITALICS, BN_CLICKED)) {
+
+            d->lf_work.lfItalic =
+                (IsDlgButtonChecked(hwnd, IDC_CFG_ITALICS) == BST_CHECKED);
+
+            refresh_view(hwnd, d);
+
+            check_for_modification(d);
+
+        } else if (wParam == MAKEWPARAM(IDC_CFG_REVERT, BN_CLICKED)) {
+            HDC hdc;
+
+            hdc = GetWindowDC(hwnd);
+
+            khm_get_cw_element_font(hdc, NULL, TRUE, &d->lf_work);
+
+            ReleaseDC(hwnd, hdc);
+
+            update_selection(d, TRUE, TRUE);
+
+            refresh_view(hwnd, d);
+
+            check_for_modification(d);
+
+        } else if (wParam == MAKEWPARAM(IDC_CFG_SIZE, CBN_SELCHANGE)) {
+            HDC hdc;
+            LPARAM idx;
+            int points;
+
+            idx = SendDlgItemMessage(hwnd, IDC_CFG_SIZE,
+                                     CB_GETCURSEL, 0, 0);
+            if (idx == CB_ERR)
+                return TRUE;
+
+            points = (int) SendDlgItemMessage(hwnd, IDC_CFG_SIZE,
+                                              CB_GETITEMDATA, idx, 0);
+
+            hdc = GetWindowDC(hwnd);
+
+            d->lf_work.lfHeight = -MulDiv(points,
+                                          GetDeviceCaps(hdc, LOGPIXELSY),
+                                          72);
+
+            ReleaseDC(hwnd, hdc);
+
+            refresh_view(hwnd, d);
+
+            check_for_modification(d);
+        }
+
+        return TRUE;
+
+    case WM_DESTROY:
+        d = (dlg_data *) (LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER);
+
+        if (d) {
+            if (d->c_font_bold)
+                DeleteObject(d->c_font_bold);
+
+            if (d->c_font_normal)
+                DeleteObject(d->c_font_normal);
+
+            PFREE(d);
+        }
+        return TRUE;
+
+    case KHUI_WM_CFG_NOTIFY:
+        d = (dlg_data *) (LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER);
+
+        if (HIWORD(wParam) == WMCFG_APPLY) {
+            write_params(d);
+            khui_action_trigger(KHUI_ACTION_LAYOUT_RELOAD, NULL);
+        }
+
+        return TRUE;
+    }
+
+    return FALSE;
+}
index fa7ac6fd4bb95a9f292e6fa05bab54fcb1439787..1e92947f09cc9280b3969f1d919ead215f625fa4 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<shlwapi.h>\r
-#include<khmapp.h>\r
-#include<assert.h>\r
-\r
-typedef struct tag_cfg_data {\r
-    BOOL auto_init;\r
-    BOOL auto_start;\r
-    BOOL auto_import;\r
-    BOOL keep_running;\r
-    BOOL auto_detect_net;\r
-    BOOL log_to_file;\r
-    BOOL destroy_creds;\r
-    khm_int32 notif_action;\r
-} cfg_data;\r
-\r
-typedef struct tag_dlg_data {\r
-    khui_config_node node;\r
-    cfg_data saved;\r
-    cfg_data work;\r
-} dlg_data;\r
-\r
-static void\r
-read_params(dlg_data * dd) {\r
-    cfg_data * d;\r
-    khm_handle csp_cw;\r
-    khm_int32 t;\r
-\r
-    d = &dd->saved;\r
-\r
-    if (KHM_FAILED(khc_open_space(NULL, L"CredWindow", KHM_PERM_READ,\r
-                                  &csp_cw))) {\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-        return;\r
-    }\r
-\r
-    khc_read_int32(csp_cw, L"AutoInit", &t);\r
-    d->auto_init = !!t;\r
-\r
-    khc_read_int32(csp_cw, L"AutoStart", &t);\r
-    d->auto_start = !!t;\r
-\r
-    khc_read_int32(csp_cw, L"AutoImport", &t);\r
-    d->auto_import = !!t;\r
-\r
-    khc_read_int32(csp_cw, L"KeepRunning", &t);\r
-    d->keep_running = !!t;\r
-\r
-    khc_read_int32(csp_cw, L"AutoDetectNet", &t);\r
-    d->auto_detect_net = !!t;\r
-\r
-    khc_read_int32(csp_cw, L"LogToFile", &t);\r
-    d->log_to_file = !!t;\r
-\r
-    khc_read_int32(csp_cw, L"DestroyCredsOnExit", &t);\r
-    d->destroy_creds = !!t;\r
-\r
-    khc_read_int32(csp_cw, L"NotificationAction", &t);\r
-    d->notif_action = t;\r
-\r
-    khc_close_space(csp_cw);\r
-\r
-    dd->work = *d;\r
-}\r
-\r
-static void\r
-write_params(dlg_data * dd) {\r
-    cfg_data * d, * s;\r
-    khm_handle csp_cw;\r
-    BOOL applied = FALSE;\r
-\r
-    d = &dd->work;\r
-    s = &dd->saved;\r
-\r
-    if (KHM_FAILED(khc_open_space(NULL, L"CredWindow", KHM_PERM_WRITE,\r
-                                  &csp_cw))) {\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-        return;\r
-    }\r
-\r
-    if (!!d->auto_init != !!s->auto_init) {\r
-        khc_write_int32(csp_cw, L"AutoInit", d->auto_init);\r
-        applied = TRUE;\r
-    }\r
-\r
-    if (!!d->auto_start != !!s->auto_start) {\r
-        khc_write_int32(csp_cw, L"AutoStart", d->auto_start);\r
-        applied = TRUE;\r
-    }\r
-\r
-    if (!!d->auto_import != !!s->auto_import) {\r
-        khc_write_int32(csp_cw, L"AutoImport", d->auto_import);\r
-        applied = TRUE;\r
-    }\r
-\r
-    if (!!d->keep_running != !!s->keep_running) {\r
-        khc_write_int32(csp_cw, L"KeepRunning", d->keep_running);\r
-        applied = TRUE;\r
-    }\r
-\r
-    if (!!d->auto_detect_net != !!s->auto_detect_net) {\r
-        khc_write_int32(csp_cw, L"AutoDetectNet", d->auto_detect_net);\r
-        applied = TRUE;\r
-    }\r
-\r
-    if (!!d->log_to_file != !!s->log_to_file) {\r
-       khc_write_int32(csp_cw, L"LogToFile", d->log_to_file);\r
-       applied = TRUE;\r
-\r
-       if (d->log_to_file) {\r
-           khm_start_file_log();\r
-       } else {\r
-           khm_stop_file_log();\r
-       }\r
-    }\r
-\r
-    if (!!d->destroy_creds != !!s->destroy_creds) {\r
-        khc_write_int32(csp_cw, L"DestroyCredsOnExit", d->destroy_creds);\r
-        applied = TRUE;\r
-    }\r
-\r
-    if (d->notif_action != s->notif_action) {\r
-        khc_write_int32(csp_cw, L"NotificationAction", d->notif_action);\r
-        applied = TRUE;\r
-    }\r
-\r
-    khc_close_space(csp_cw);\r
-\r
-    khui_cfg_set_flags(dd->node,\r
-                       (applied) ? KHUI_CNFLAG_APPLIED : 0,\r
-                       KHUI_CNFLAG_APPLIED | KHUI_CNFLAG_MODIFIED);\r
-\r
-    *s = *d;\r
-}\r
-\r
-static void\r
-check_for_modification(dlg_data * dd) {\r
-    cfg_data * d, * s;\r
-    d = &dd->work;\r
-    s = &dd->saved;\r
-\r
-    if (!!d->auto_init != !!s->auto_init ||\r
-        !!d->auto_start != !!s->auto_start ||\r
-        !!d->auto_import != !!s->auto_import ||\r
-        !!d->keep_running != !!s->keep_running ||\r
-        !!d->auto_detect_net != !!s->auto_detect_net ||\r
-       !!d->log_to_file != !!s->log_to_file ||\r
-        !!d->destroy_creds != !!s->destroy_creds ||\r
-        d->notif_action != s->notif_action) {\r
-\r
-        khui_cfg_set_flags(dd->node,\r
-                           KHUI_CNFLAG_MODIFIED,\r
-                           KHUI_CNFLAG_MODIFIED);\r
-\r
-    } else {\r
-\r
-        khui_cfg_set_flags(dd->node,\r
-                           0,\r
-                           KHUI_CNFLAG_MODIFIED);\r
-\r
-    }\r
-}\r
-\r
-\r
-static void\r
-strip_ampersands(wchar_t * str) {\r
-    wchar_t *f, *t;\r
-\r
-    for(f = t = str; *f; f++)\r
-        if (*f != L'&')\r
-            *t++ = *f;\r
-\r
-    *t = L'\0';\r
-}\r
-\r
-static void\r
-refresh_view(HWND hwnd, dlg_data * d) {\r
-    wchar_t buf[512];\r
-    khm_size i;\r
-\r
-    CheckDlgButton(hwnd, IDC_CFG_AUTOINIT,\r
-                   (d->work.auto_init?BST_CHECKED:BST_UNCHECKED));\r
-    CheckDlgButton(hwnd, IDC_CFG_AUTOSTART,\r
-                   (d->work.auto_start?BST_CHECKED:BST_UNCHECKED));\r
-    CheckDlgButton(hwnd, IDC_CFG_AUTOIMPORT,\r
-                   (d->work.auto_import?BST_CHECKED:BST_UNCHECKED));\r
-    CheckDlgButton(hwnd, IDC_CFG_KEEPRUNNING,\r
-                   (d->work.keep_running?BST_CHECKED:BST_UNCHECKED));\r
-    CheckDlgButton(hwnd, IDC_CFG_NETDETECT,\r
-                   (d->work.auto_detect_net?BST_CHECKED:BST_UNCHECKED));\r
-    CheckDlgButton(hwnd, IDC_CFG_LOGTOFILE,\r
-                  (d->work.log_to_file?BST_CHECKED:BST_UNCHECKED));\r
-    CheckDlgButton(hwnd, IDC_CFG_DESTROYALL,\r
-                   (d->work.destroy_creds?BST_CHECKED:BST_UNCHECKED));\r
-\r
-    /* we need populate the notification action combo box control and\r
-       set the current selection to match the default action. */\r
-\r
-    if (n_khm_notifier_actions != SendDlgItemMessage(hwnd, IDC_CFG_NOTACTION,\r
-                                                     CB_GETCOUNT, 0, 0)) {\r
-\r
-        for (i=0; i < n_khm_notifier_actions; i++) {\r
-            int idx;\r
-\r
-            khm_get_action_caption(khm_notifier_actions[i],\r
-                                   buf, sizeof(buf));\r
-\r
-            strip_ampersands(buf);\r
-\r
-            idx = (int) SendDlgItemMessage(hwnd, IDC_CFG_NOTACTION,\r
-                                           CB_INSERTSTRING, i,\r
-                                           (LPARAM) buf);\r
-\r
-#ifdef DEBUG\r
-            if (idx != (int) i) {\r
-                assert(FALSE);\r
-            }\r
-#endif\r
-        }\r
-    }\r
-\r
-    for (i=0; i < n_khm_notifier_actions; i++) {\r
-        if (khm_notifier_actions[i] == d->work.notif_action)\r
-            break;\r
-    }\r
-\r
-    if (i >= n_khm_notifier_actions) {\r
-        d->work.notif_action = khm_notifier_actions[0];\r
-        i = 0;\r
-    }\r
-\r
-    SendDlgItemMessage(hwnd, IDC_CFG_NOTACTION, CB_SETCURSEL, i, 0);\r
-\r
-    /* in addition, we correct the label on the trace log control to\r
-       reflect the actual path that is going to get used */\r
-    if (GetDlgItemText(hwnd, IDC_CFG_LOGPATH, buf,\r
-                      ARRAYLENGTH(buf)) == 0) {\r
-\r
-       khm_get_file_log_path(sizeof(buf), buf);\r
-\r
-       SetDlgItemText(hwnd, IDC_CFG_LOGPATH, buf);\r
-    }\r
-}\r
-\r
-static void\r
-refresh_data(HWND hwnd, dlg_data * d) {\r
-    int idx;\r
-\r
-    d->work.auto_init = (IsDlgButtonChecked(hwnd, IDC_CFG_AUTOINIT)\r
-                         == BST_CHECKED);\r
-    d->work.auto_start = (IsDlgButtonChecked(hwnd, IDC_CFG_AUTOSTART)\r
-                          == BST_CHECKED);\r
-    d->work.auto_import = (IsDlgButtonChecked(hwnd, IDC_CFG_AUTOIMPORT)\r
-                           == BST_CHECKED);\r
-    d->work.keep_running = (IsDlgButtonChecked(hwnd, IDC_CFG_KEEPRUNNING)\r
-                            == BST_CHECKED);\r
-    d->work.auto_detect_net = (IsDlgButtonChecked(hwnd, IDC_CFG_NETDETECT)\r
-                               == BST_CHECKED);\r
-    d->work.log_to_file = (IsDlgButtonChecked(hwnd, IDC_CFG_LOGTOFILE)\r
-                          == BST_CHECKED);\r
-    d->work.destroy_creds = (IsDlgButtonChecked(hwnd, IDC_CFG_DESTROYALL)\r
-                             == BST_CHECKED);\r
-\r
-    idx = (int) SendDlgItemMessage(hwnd, IDC_CFG_NOTACTION, CB_GETCURSEL, 0, 0);\r
-    if (idx < 0)\r
-        idx = 0;\r
-    else if (idx >= (int) n_khm_notifier_actions)\r
-        idx = (int) n_khm_notifier_actions - 1;\r
-\r
-    d->work.notif_action = khm_notifier_actions[idx];\r
-}\r
-\r
-INT_PTR CALLBACK\r
-khm_cfg_general_proc(HWND hwnd,\r
-                     UINT uMsg,\r
-                     WPARAM wParam,\r
-                     LPARAM lParam) {\r
-    dlg_data * d;\r
-\r
-    switch(uMsg) {\r
-    case WM_INITDIALOG:\r
-        d = PMALLOC(sizeof(*d));\r
-#ifdef DEBUG\r
-        assert(d != NULL);\r
-#endif\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-        SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);\r
-#pragma warning(pop)\r
-\r
-        ZeroMemory(d, sizeof(*d));\r
-\r
-        d->node = (khui_config_node) lParam;\r
-\r
-        read_params(d);\r
-\r
-        refresh_view(hwnd, d);\r
-\r
-        return FALSE;\r
-\r
-    case WM_DESTROY:\r
-        d = (dlg_data *) (DWORD_PTR) GetWindowLongPtr(hwnd, DWLP_USER);\r
-        if (d) {\r
-            PFREE(d);\r
-        }\r
-        return TRUE;\r
-\r
-    case WM_COMMAND:\r
-        d = (dlg_data *) (DWORD_PTR) GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        if (HIWORD(wParam) == BN_CLICKED) {\r
-            if (LOWORD(wParam) == IDC_CFG_SHOWLOG) {\r
-                /* we need to display the logfile */\r
-                wchar_t buf[512];\r
-\r
-                buf[0] = L'\0';\r
-                khm_get_file_log_path(sizeof(buf), buf);\r
-\r
-                if (!buf[0] ||\r
-                    !PathFileExists(buf)) {\r
-\r
-                    wchar_t title[256];\r
-                    wchar_t msg[550];\r
-                    wchar_t fmt[256];\r
-\r
-                    LoadString(khm_hInstance, IDS_CFG_LOGF_CS,\r
-                               title, ARRAYLENGTH(title));\r
-                    LoadString(khm_hInstance, IDS_CFG_LOGF_CSR,\r
-                               fmt, ARRAYLENGTH(fmt));\r
-\r
-                    StringCbPrintf(msg, sizeof(msg), fmt, buf);\r
-\r
-                    MessageBox(hwnd, title, msg, MB_OK);\r
-                    \r
-                } else {\r
-                    wchar_t cmdline[550];\r
-                    STARTUPINFO si;\r
-                    PROCESS_INFORMATION pi;\r
-\r
-                    StringCbCopy(cmdline, sizeof(cmdline), L"notepad.exe ");\r
-                    StringCbCat(cmdline, sizeof(cmdline), L"\"");\r
-                    StringCbCat(cmdline, sizeof(cmdline), buf);\r
-                    StringCbCat(cmdline, sizeof(cmdline), L"\"");\r
-\r
-                    ZeroMemory(&si, sizeof(si));\r
-                    si.cb = sizeof(si);\r
-                    ZeroMemory(&pi, sizeof(pi));\r
-\r
-                    CreateProcess(NULL,\r
-                                  cmdline,\r
-                                  NULL, NULL,\r
-                                  FALSE,\r
-                                  0, NULL, NULL,\r
-                                  &si,\r
-                                  &pi);\r
-\r
-                    if (pi.hProcess)\r
-                        CloseHandle(pi.hProcess);\r
-                    if (pi.hThread)\r
-                        CloseHandle(pi.hThread);\r
-\r
-                }\r
-            } else {\r
-                refresh_data(hwnd, d);\r
-                check_for_modification(d);\r
-            }\r
-        } else if (HIWORD(wParam) == CBN_SELCHANGE) {\r
-            refresh_data(hwnd, d);\r
-            check_for_modification(d);\r
-        }\r
-\r
-        khm_set_dialog_result(hwnd, 0);\r
-\r
-        return TRUE;\r
-\r
-    case KHUI_WM_CFG_NOTIFY:\r
-        d = (dlg_data *) (DWORD_PTR) GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        if (HIWORD(wParam) == WMCFG_APPLY) {\r
-            write_params(d);\r
-        }\r
-\r
-        khm_set_dialog_result(hwnd, 0);\r
-\r
-        return TRUE;\r
-    }\r
-\r
-    return FALSE;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<shlwapi.h>
+#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;
+    BOOL log_to_file;
+    BOOL destroy_creds;
+    khm_int32 notif_action;
+} 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_read_int32(csp_cw, L"LogToFile", &t);
+    d->log_to_file = !!t;
+
+    khc_read_int32(csp_cw, L"DestroyCredsOnExit", &t);
+    d->destroy_creds = !!t;
+
+    khc_read_int32(csp_cw, L"NotificationAction", &t);
+    d->notif_action = t;
+
+    khc_close_space(csp_cw);
+
+    dd->work = *d;
+}
+
+static void
+write_params(dlg_data * dd) {
+    cfg_data * d, * s;
+    khm_handle csp_cw;
+    BOOL applied = FALSE;
+
+    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);
+        applied = TRUE;
+    }
+
+    if (!!d->auto_start != !!s->auto_start) {
+        khc_write_int32(csp_cw, L"AutoStart", d->auto_start);
+        applied = TRUE;
+    }
+
+    if (!!d->auto_import != !!s->auto_import) {
+        khc_write_int32(csp_cw, L"AutoImport", d->auto_import);
+        applied = TRUE;
+    }
+
+    if (!!d->keep_running != !!s->keep_running) {
+        khc_write_int32(csp_cw, L"KeepRunning", d->keep_running);
+        applied = TRUE;
+    }
+
+    if (!!d->auto_detect_net != !!s->auto_detect_net) {
+        khc_write_int32(csp_cw, L"AutoDetectNet", d->auto_detect_net);
+        applied = TRUE;
+    }
+
+    if (!!d->log_to_file != !!s->log_to_file) {
+       khc_write_int32(csp_cw, L"LogToFile", d->log_to_file);
+       applied = TRUE;
+
+       if (d->log_to_file) {
+           khm_start_file_log();
+       } else {
+           khm_stop_file_log();
+       }
+    }
+
+    if (!!d->destroy_creds != !!s->destroy_creds) {
+        khc_write_int32(csp_cw, L"DestroyCredsOnExit", d->destroy_creds);
+        applied = TRUE;
+    }
+
+    if (d->notif_action != s->notif_action) {
+        khc_write_int32(csp_cw, L"NotificationAction", d->notif_action);
+        applied = TRUE;
+    }
+
+    khc_close_space(csp_cw);
+
+    khui_cfg_set_flags(dd->node,
+                       (applied) ? KHUI_CNFLAG_APPLIED : 0,
+                       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 ||
+       !!d->log_to_file != !!s->log_to_file ||
+        !!d->destroy_creds != !!s->destroy_creds ||
+        d->notif_action != s->notif_action) {
+
+        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
+strip_ampersands(wchar_t * str) {
+    wchar_t *f, *t;
+
+    for(f = t = str; *f; f++)
+        if (*f != L'&')
+            *t++ = *f;
+
+    *t = L'\0';
+}
+
+static void
+refresh_view(HWND hwnd, dlg_data * d) {
+    wchar_t buf[512];
+    khm_size i;
+
+    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));
+    CheckDlgButton(hwnd, IDC_CFG_LOGTOFILE,
+                  (d->work.log_to_file?BST_CHECKED:BST_UNCHECKED));
+    CheckDlgButton(hwnd, IDC_CFG_DESTROYALL,
+                   (d->work.destroy_creds?BST_CHECKED:BST_UNCHECKED));
+
+    /* we need populate the notification action combo box control and
+       set the current selection to match the default action. */
+
+    if (n_khm_notifier_actions != SendDlgItemMessage(hwnd, IDC_CFG_NOTACTION,
+                                                     CB_GETCOUNT, 0, 0)) {
+
+        for (i=0; i < n_khm_notifier_actions; i++) {
+            int idx;
+
+            khm_get_action_caption(khm_notifier_actions[i],
+                                   buf, sizeof(buf));
+
+            strip_ampersands(buf);
+
+            idx = (int) SendDlgItemMessage(hwnd, IDC_CFG_NOTACTION,
+                                           CB_INSERTSTRING, i,
+                                           (LPARAM) buf);
+
+#ifdef DEBUG
+            if (idx != (int) i) {
+                assert(FALSE);
+            }
+#endif
+        }
+    }
+
+    for (i=0; i < n_khm_notifier_actions; i++) {
+        if (khm_notifier_actions[i] == d->work.notif_action)
+            break;
+    }
+
+    if (i >= n_khm_notifier_actions) {
+        d->work.notif_action = khm_notifier_actions[0];
+        i = 0;
+    }
+
+    SendDlgItemMessage(hwnd, IDC_CFG_NOTACTION, CB_SETCURSEL, i, 0);
+
+    /* in addition, we correct the label on the trace log control to
+       reflect the actual path that is going to get used */
+    if (GetDlgItemText(hwnd, IDC_CFG_LOGPATH, buf,
+                      ARRAYLENGTH(buf)) == 0) {
+
+       khm_get_file_log_path(sizeof(buf), buf);
+
+       SetDlgItemText(hwnd, IDC_CFG_LOGPATH, buf);
+    }
+}
+
+static void
+refresh_data(HWND hwnd, dlg_data * d) {
+    int idx;
+
+    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);
+    d->work.log_to_file = (IsDlgButtonChecked(hwnd, IDC_CFG_LOGTOFILE)
+                          == BST_CHECKED);
+    d->work.destroy_creds = (IsDlgButtonChecked(hwnd, IDC_CFG_DESTROYALL)
+                             == BST_CHECKED);
+
+    idx = (int) SendDlgItemMessage(hwnd, IDC_CFG_NOTACTION, CB_GETCURSEL, 0, 0);
+    if (idx < 0)
+        idx = 0;
+    else if (idx >= (int) n_khm_notifier_actions)
+        idx = (int) n_khm_notifier_actions - 1;
+
+    d->work.notif_action = khm_notifier_actions[idx];
+}
+
+INT_PTR CALLBACK
+khm_cfg_general_proc(HWND hwnd,
+                     UINT uMsg,
+                     WPARAM wParam,
+                     LPARAM lParam) {
+    dlg_data * d;
+
+    switch(uMsg) {
+    case WM_INITDIALOG:
+        d = PMALLOC(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_DESTROY:
+        d = (dlg_data *) (DWORD_PTR) GetWindowLongPtr(hwnd, DWLP_USER);
+        if (d) {
+            PFREE(d);
+        }
+        return TRUE;
+
+    case WM_COMMAND:
+        d = (dlg_data *) (DWORD_PTR) GetWindowLongPtr(hwnd, DWLP_USER);
+
+        if (HIWORD(wParam) == BN_CLICKED) {
+            if (LOWORD(wParam) == IDC_CFG_SHOWLOG) {
+                /* we need to display the logfile */
+                wchar_t buf[512];
+
+                buf[0] = L'\0';
+                khm_get_file_log_path(sizeof(buf), buf);
+
+                if (!buf[0] ||
+                    !PathFileExists(buf)) {
+
+                    wchar_t title[256];
+                    wchar_t msg[550];
+                    wchar_t fmt[256];
+
+                    LoadString(khm_hInstance, IDS_CFG_LOGF_CS,
+                               title, ARRAYLENGTH(title));
+                    LoadString(khm_hInstance, IDS_CFG_LOGF_CSR,
+                               fmt, ARRAYLENGTH(fmt));
+
+                    StringCbPrintf(msg, sizeof(msg), fmt, buf);
+
+                    MessageBox(hwnd, title, msg, MB_OK);
+                    
+                } else {
+                    wchar_t cmdline[550];
+                    STARTUPINFO si;
+                    PROCESS_INFORMATION pi;
+
+                    StringCbCopy(cmdline, sizeof(cmdline), L"notepad.exe ");
+                    StringCbCat(cmdline, sizeof(cmdline), L"\"");
+                    StringCbCat(cmdline, sizeof(cmdline), buf);
+                    StringCbCat(cmdline, sizeof(cmdline), L"\"");
+
+                    ZeroMemory(&si, sizeof(si));
+                    si.cb = sizeof(si);
+                    ZeroMemory(&pi, sizeof(pi));
+
+                    CreateProcess(NULL,
+                                  cmdline,
+                                  NULL, NULL,
+                                  FALSE,
+                                  0, NULL, NULL,
+                                  &si,
+                                  &pi);
+
+                    if (pi.hProcess)
+                        CloseHandle(pi.hProcess);
+                    if (pi.hThread)
+                        CloseHandle(pi.hThread);
+
+                }
+            } else {
+                refresh_data(hwnd, d);
+                check_for_modification(d);
+            }
+        } else if (HIWORD(wParam) == CBN_SELCHANGE) {
+            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;
+}
index a8813d1164726735e055d6b8af56d04aa8904b52..d45288a3a9d23ea2a0c49fcc07984eb41db5359f 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khmapp.h>\r
-#include<assert.h>\r
-\r
-static khui_config_node\r
-get_window_node(HWND hwnd) {\r
-    return (khui_config_node) (LONG_PTR)\r
-        GetWindowLongPtr(hwnd, DWLP_USER);\r
-}\r
-\r
-static void\r
-set_window_node(HWND hwnd, khui_config_node node) {\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-    SetWindowLongPtr(hwnd, DWLP_USER,\r
-                     (LONG_PTR) node);\r
-#pragma warning(pop)\r
-}\r
-\r
-static void\r
-add_subpanels(HWND hwnd, \r
-              khui_config_node ctx_node,\r
-              khui_config_node ref_node) {\r
-\r
-    HWND hw_tab;\r
-    HWND hw_target;\r
-    khui_config_node sub;\r
-    khui_config_node_reg reg;\r
-    khui_config_init_data idata;\r
-    int idx;\r
-\r
-    hw_tab = GetDlgItem(hwnd, IDC_CFG_TAB);\r
-    hw_target = GetDlgItem(hwnd, IDC_CFG_TARGET);\r
-#ifdef DEBUG\r
-    assert(hw_tab);\r
-    assert(hw_target);\r
-#endif\r
-\r
-    if (KHM_FAILED(khui_cfg_get_first_subpanel(ref_node, &sub))) {\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-        return;\r
-    }\r
-\r
-    idx = 0;\r
-    while(sub) {\r
-        HWND hwnd_panel;\r
-        TCITEM tci;\r
-        int iid;\r
-\r
-        khui_cfg_get_reg(sub, &reg);\r
-\r
-        if ((ctx_node == ref_node && (reg.flags & KHUI_CNFLAG_PLURAL)) ||\r
-            (ctx_node != ref_node && !(reg.flags & KHUI_CNFLAG_PLURAL)))\r
-            goto _next_node;\r
-\r
-        idata.ctx_node = ctx_node;\r
-        idata.this_node = sub;\r
-        idata.ref_node = ref_node;\r
-\r
-        hwnd_panel = CreateDialogParam(reg.h_module,\r
-                                       reg.dlg_template,\r
-                                       hwnd,\r
-                                       reg.dlg_proc,\r
-                                       (LPARAM) &idata);\r
-\r
-#ifdef DEBUG\r
-        assert(hwnd_panel);\r
-#endif\r
-\r
-        ShowWindow(hwnd_panel, SW_HIDE);\r
-\r
-        ZeroMemory(&tci, sizeof(tci));\r
-\r
-        tci.mask = TCIF_PARAM | TCIF_TEXT;\r
-        tci.lParam = (LPARAM) sub;\r
-        tci.pszText = (LPWSTR) reg.short_desc;\r
-\r
-        iid = TabCtrl_InsertItem(hw_tab, 0, &tci);\r
-        idx++;\r
-\r
-        if (reg.flags & KHUI_CNFLAG_PLURAL) {\r
-            khui_cfg_set_param_inst(sub, ctx_node, iid);\r
-            khui_cfg_set_hwnd_inst(sub, ctx_node, hwnd_panel);\r
-        } else {\r
-            khui_cfg_set_param(sub, iid);\r
-            khui_cfg_set_hwnd(sub, hwnd_panel);\r
-        }\r
-\r
-    _next_node:\r
-\r
-        khui_cfg_get_next_release(&sub);\r
-    }\r
-\r
-    TabCtrl_SetCurSel(hw_tab, 0);\r
-}\r
-\r
-static void\r
-apply_all(HWND hwnd, \r
-          HWND hw_tab,\r
-          khui_config_node noderef) {\r
-    TCITEM tci;\r
-    HWND hw;\r
-    khui_config_node_reg reg;\r
-    int idx;\r
-    int count;\r
-    BOOL cont = TRUE;\r
-\r
-    count = TabCtrl_GetItemCount(hw_tab);\r
-\r
-    for (idx = 0; idx < count && cont; idx++) {\r
-\r
-        ZeroMemory(&tci, sizeof(tci));\r
-\r
-        tci.mask = TCIF_PARAM;\r
-        TabCtrl_GetItem(hw_tab,\r
-                        idx,\r
-                        &tci);\r
-\r
-#ifdef DEBUG\r
-        assert(tci.lParam);\r
-#endif\r
-        khui_cfg_get_reg((khui_config_node) tci.lParam, &reg);\r
-        if (reg.flags & KHUI_CNFLAG_PLURAL)\r
-            hw = khui_cfg_get_hwnd_inst((khui_config_node) tci.lParam,\r
-                                        noderef);\r
-        else\r
-            hw = khui_cfg_get_hwnd((khui_config_node) tci.lParam);\r
-#ifdef DEBUG\r
-        assert(hw);\r
-#endif\r
-\r
-        SendMessage(hw, KHUI_WM_CFG_NOTIFY,\r
-                    MAKEWPARAM(0, WMCFG_APPLY), (LPARAM) &cont);\r
-    }\r
-}\r
-\r
-static void\r
-show_tab_panel(HWND hwnd,\r
-               khui_config_node node,\r
-               HWND hw_tab,\r
-               int idx,\r
-               BOOL show) {\r
-    TCITEM tci;\r
-    HWND hw;\r
-    HWND hw_target;\r
-    HWND hw_firstctl;\r
-    RECT r;\r
-    RECT rref;\r
-    khui_config_node_reg reg;\r
-\r
-    ZeroMemory(&tci, sizeof(tci));\r
-\r
-    tci.mask = TCIF_PARAM;\r
-    TabCtrl_GetItem(hw_tab,\r
-                    idx,\r
-                    &tci);\r
-\r
-#ifdef DEBUG\r
-    assert(tci.lParam);\r
-#endif\r
-    khui_cfg_get_reg((khui_config_node) tci.lParam, &reg);\r
-    if (reg.flags & KHUI_CNFLAG_PLURAL)\r
-        hw = khui_cfg_get_hwnd_inst((khui_config_node) tci.lParam,\r
-                                    node);\r
-    else\r
-        hw = khui_cfg_get_hwnd((khui_config_node) tci.lParam);\r
-#ifdef DEBUG\r
-    assert(hw);\r
-#endif\r
-\r
-    if (!show) {\r
-        ShowWindow(hw, SW_HIDE);\r
-        return;\r
-    }\r
-\r
-    hw_target = GetDlgItem(hwnd, IDC_CFG_TARGET);\r
-#ifdef DEBUG\r
-    assert(hw_target);\r
-#endif\r
-    GetWindowRect(hwnd, &rref);\r
-    GetWindowRect(hw_target, &r);\r
-\r
-    OffsetRect(&r, -rref.left, -rref.top);\r
-\r
-    SetWindowPos(hw,\r
-                 hw_tab,\r
-                 r.left, r.top,\r
-                 r.right - r.left, r.bottom - r.top,\r
-                 SWP_NOACTIVATE | SWP_NOOWNERZORDER |\r
-                 SWP_SHOWWINDOW);\r
-\r
-    hw_firstctl = GetNextDlgTabItem(hw, NULL, FALSE);\r
-    if (hw_firstctl) {\r
-        SetFocus(hw_firstctl);\r
-    }\r
-}\r
-\r
-static INT_PTR\r
-handle_cfg_notify(HWND hwnd,\r
-                  WPARAM wParam,\r
-                  LPARAM lParam) {\r
-    khui_config_node node;\r
-    HWND hw;\r
-\r
-    node = get_window_node(hwnd);\r
-\r
-    if (HIWORD(wParam) == WMCFG_APPLY) {\r
-\r
-        hw = GetDlgItem(hwnd, IDC_CFG_TAB);\r
-\r
-        apply_all(hwnd,\r
-                  hw,\r
-                  node);\r
-    }\r
-\r
-    return TRUE;\r
-}\r
-\r
-static INT_PTR\r
-handle_notify(HWND hwnd,\r
-              WPARAM wParam,\r
-              LPARAM lParam) {\r
-    LPNMHDR lpnm;\r
-    int i;\r
-\r
-\r
-    khui_config_node node;\r
-\r
-    lpnm = (LPNMHDR) lParam;\r
-    node = get_window_node(hwnd);\r
-\r
-    if (lpnm->idFrom == IDC_CFG_TAB) {\r
-        switch(lpnm->code) {\r
-        case TCN_SELCHANGING:\r
-            i = TabCtrl_GetCurSel(lpnm->hwndFrom);\r
-\r
-            show_tab_panel(hwnd, \r
-                           node,\r
-                           lpnm->hwndFrom,\r
-                           i,\r
-                           FALSE);\r
-            break;\r
-\r
-        case TCN_SELCHANGE:\r
-            i = TabCtrl_GetCurSel(lpnm->hwndFrom);\r
-\r
-            show_tab_panel(hwnd,\r
-                           node,\r
-                           lpnm->hwndFrom,\r
-                           i,\r
-                           TRUE);\r
-            break;\r
-        }\r
-        return TRUE;\r
-    } else {\r
-        return FALSE;\r
-    }\r
-}\r
-\r
-typedef struct tag_ident_props {\r
-    BOOL monitor;\r
-    BOOL auto_renew;\r
-    BOOL sticky;\r
-} ident_props;\r
-\r
-typedef struct tag_ident_data {\r
-    khm_handle ident;\r
-    wchar_t * idname;\r
-    int lv_idx;\r
-\r
-    BOOL removed;\r
-    BOOL applied;\r
-\r
-    khm_int32 flags;\r
-\r
-    ident_props saved;\r
-    ident_props work;\r
-\r
-    HWND hwnd;\r
-} ident_data;\r
-\r
-typedef struct tag_global_props {\r
-    BOOL monitor;\r
-    BOOL auto_renew;\r
-    BOOL sticky;\r
-} global_props;\r
-\r
-typedef struct tag_idents_data {\r
-    BOOL         valid;\r
-\r
-    ident_data * idents;\r
-    khm_size     n_idents;\r
-    khm_size     nc_idents;\r
-#define IDENTS_DATA_ALLOC_INCR 8\r
-\r
-    /* global options */\r
-    global_props saved;\r
-    global_props work;\r
-    BOOL         applied;\r
-\r
-    int          refcount;\r
-\r
-    HIMAGELIST   hi_status;\r
-    int          idx_id;\r
-    int          idx_default;\r
-    int          idx_modified;\r
-    int          idx_applied;\r
-    int          idx_deleted;\r
-\r
-    HWND         hwnd;\r
-    khui_config_init_data cfg;\r
-} idents_data;\r
-\r
-static idents_data cfg_idents = {FALSE, NULL, 0, 0,\r
-                                 {0, 0, 0},\r
-                                 {0, 0, 0},\r
-                                 FALSE,\r
-\r
-                                 0, NULL };\r
-\r
-static void\r
-read_params_ident(ident_data * d) {\r
-    khm_handle csp_ident;\r
-    khm_handle csp_cw;\r
-    khm_int32 t;\r
-\r
-    if (KHM_FAILED(kcdb_identity_get_config(d->ident,\r
-                                            KHM_PERM_READ,\r
-                                            &csp_ident))) {\r
-        csp_ident = NULL;\r
-    }\r
-\r
-    if (KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow", KHM_PERM_READ,\r
-                                     &csp_cw))) {\r
-        if (csp_ident) {\r
-            khc_shadow_space(csp_ident,\r
-                             csp_cw);\r
-            khc_close_space(csp_cw);\r
-        } else {\r
-            csp_ident = csp_cw;\r
-        }\r
-        csp_cw = NULL;\r
-    } else {\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-        d->saved.monitor = TRUE;\r
-        d->saved.auto_renew = TRUE;\r
-        d->saved.sticky = FALSE;\r
-        d->work = d->saved;\r
-\r
-        if (csp_ident)\r
-            khc_close_space(csp_ident);\r
-\r
-        return;\r
-    }\r
-\r
-    if (KHM_FAILED(khc_read_int32(csp_ident, L"Monitor", &t))) {\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-        d->saved.monitor = TRUE;\r
-    } else {\r
-        d->saved.monitor = !!t;\r
-    }\r
-\r
-    if (KHM_FAILED(khc_read_int32(csp_ident, L"AllowAutoRenew", &t))) {\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-        d->saved.auto_renew = TRUE;\r
-    } else {\r
-        d->saved.auto_renew = !!t;\r
-    }\r
-\r
-    if (KHM_FAILED(khc_read_int32(csp_ident, L"Sticky", &t))) {\r
-        d->saved.sticky = FALSE;\r
-    } else {\r
-        d->saved.sticky = !!t;\r
-    }\r
-\r
-    khc_close_space(csp_ident);\r
-\r
-    d->work = d->saved;\r
-    d->applied = FALSE;\r
-}\r
-\r
-static void\r
-write_params_ident(ident_data * d) {\r
-    khm_handle csp_ident;\r
-\r
-    if (d->saved.monitor == d->work.monitor &&\r
-        d->saved.auto_renew == d->work.auto_renew &&\r
-        d->saved.sticky == d->work.sticky &&\r
-        !d->removed)\r
-        return;\r
-\r
-    if (KHM_FAILED(kcdb_identity_get_config(d->ident, KHM_PERM_WRITE,\r
-                                            &csp_ident))) {\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-        return;\r
-    }\r
-\r
-    if (d->removed) {\r
-        khm_handle h = NULL;\r
-        khm_int32 flags = 0;\r
-\r
-        khc_remove_space(csp_ident);\r
-\r
-        /* calling kcdb_identity_get_config() will update the\r
-           KCDB_IDENT_FLAG_CONFIG flag for the identity to reflect the\r
-           fact that it nolonger has a configuration. */\r
-        kcdb_identity_get_config(d->ident, 0, &h);\r
-        if (h) {\r
-            /* what the ? */\r
-#ifdef DEBUG\r
-            assert(FALSE);\r
-#endif\r
-            khc_close_space(h);\r
-        }\r
-#ifdef DEBUG\r
-        kcdb_identity_get_flags(d->ident, &flags);\r
-        assert(!(flags & KCDB_IDENT_FLAG_CONFIG));\r
-#endif\r
-\r
-    } else {\r
-\r
-        if (d->saved.monitor != d->work.monitor)\r
-            khc_write_int32(csp_ident, L"Monitor", !!d->work.monitor);\r
-\r
-        if (d->saved.auto_renew != d->work.auto_renew)\r
-            khc_write_int32(csp_ident, L"AllowAutoRenew",\r
-                            !!d->work.auto_renew);\r
-\r
-        if (d->saved.sticky != d->work.sticky) {\r
-            kcdb_identity_set_flags(d->ident,\r
-                                    (d->work.sticky)?KCDB_IDENT_FLAG_STICKY:0,\r
-                                    KCDB_IDENT_FLAG_STICKY);\r
-        }\r
-    }\r
-\r
-    khc_close_space(csp_ident);\r
-\r
-    d->saved = d->work;\r
-\r
-    d->applied = TRUE;\r
-\r
-    if (d->hwnd)\r
-        PostMessage(d->hwnd, KHUI_WM_CFG_NOTIFY,\r
-                    MAKEWPARAM(0, WMCFG_UPDATE_STATE), 0);\r
-\r
-    khm_refresh_config();\r
-}\r
-\r
-static void\r
-write_params_idents(void) {\r
-    khm_handle csp_cw = NULL;\r
-\r
-    if (KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow",\r
-                                     KHM_FLAG_CREATE, &csp_cw))) {\r
-        if (cfg_idents.work.monitor != cfg_idents.saved.monitor) {\r
-            khc_write_int32(csp_cw, L"DefaultMonitor",\r
-                            !!cfg_idents.work.monitor);\r
-            cfg_idents.work.monitor = cfg_idents.saved.monitor;\r
-            cfg_idents.applied = TRUE;\r
-        }\r
-        if (cfg_idents.work.auto_renew != cfg_idents.saved.auto_renew) {\r
-            khc_write_int32(csp_cw, L"DefaultAllowAutoRenew",\r
-                            !!cfg_idents.work.auto_renew);\r
-            cfg_idents.work.auto_renew = cfg_idents.saved.auto_renew;\r
-            cfg_idents.applied = TRUE;\r
-        }\r
-        if (cfg_idents.work.sticky != cfg_idents.saved.sticky) {\r
-            khc_write_int32(csp_cw, L"DefaultSticky",\r
-                            !!cfg_idents.work.sticky);\r
-            cfg_idents.work.sticky = cfg_idents.saved.sticky;\r
-            cfg_idents.applied = TRUE;\r
-        }\r
-\r
-        khc_close_space(csp_cw);\r
-        csp_cw = NULL;\r
-    }\r
-\r
-#if 0\r
-    for (i=0; i < (int)cfg_idents.n_idents; i++) {\r
-        write_params_ident(&cfg_idents.idents[i]);\r
-    }\r
-#endif\r
-\r
-    if (cfg_idents.hwnd)\r
-        PostMessage(cfg_idents.hwnd, KHUI_WM_CFG_NOTIFY,\r
-                    MAKEWPARAM(0, WMCFG_UPDATE_STATE), 0);\r
-}\r
-\r
-static void\r
-init_idents_data(void) {\r
-    khm_int32 rv;\r
-    wchar_t * t;\r
-    wchar_t * widnames = NULL;\r
-    khm_size cb;\r
-    int n_tries = 0;\r
-    int i;\r
-    khm_handle csp_cw = NULL;\r
-\r
-    if (cfg_idents.valid)\r
-        return;\r
-\r
-#ifdef DEBUG\r
-    assert(cfg_idents.idents == NULL);\r
-    assert(cfg_idents.n_idents == 0);\r
-    assert(cfg_idents.nc_idents == 0);\r
-#endif\r
-\r
-    if (KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow", 0, &csp_cw))) {\r
-        khm_int32 t;\r
-\r
-        if (KHM_SUCCEEDED(khc_read_int32(csp_cw, L"DefaultMonitor", &t)))\r
-            cfg_idents.saved.monitor = !!t;\r
-        else\r
-            cfg_idents.saved.monitor = TRUE;\r
-\r
-        if (KHM_SUCCEEDED(khc_read_int32(csp_cw, L"DefaultAllowAutoRenew", &t)))\r
-            cfg_idents.saved.auto_renew = !!t;\r
-        else\r
-            cfg_idents.saved.auto_renew = TRUE;\r
-\r
-        if (KHM_SUCCEEDED(khc_read_int32(csp_cw, L"DefaultSticky", &t)))\r
-            cfg_idents.saved.sticky = !!t;\r
-        else\r
-            cfg_idents.saved.sticky = FALSE;\r
-\r
-        khc_close_space(csp_cw);\r
-        csp_cw = NULL;\r
-\r
-    } else {\r
-\r
-        cfg_idents.saved.monitor = TRUE;\r
-        cfg_idents.saved.auto_renew = TRUE;\r
-        cfg_idents.saved.sticky = FALSE;\r
-\r
-    }\r
-\r
-    cfg_idents.work = cfg_idents.saved;\r
-    cfg_idents.applied = FALSE;\r
-\r
-    do {\r
-        rv = kcdb_identity_enum(KCDB_IDENT_FLAG_CONFIG,\r
-                                KCDB_IDENT_FLAG_CONFIG,\r
-                                NULL,\r
-                                &cb,\r
-                                &cfg_idents.n_idents);\r
-\r
-        if (rv != KHM_ERROR_TOO_LONG ||\r
-            cfg_idents.n_idents == 0 ||\r
-            cb == 0)\r
-            break;\r
-\r
-        if (widnames)\r
-            PFREE(widnames);\r
-        widnames = PMALLOC(cb);\r
-#ifdef DEBUG\r
-        assert(widnames);\r
-#endif\r
-\r
-        rv = kcdb_identity_enum(KCDB_IDENT_FLAG_CONFIG,\r
-                                KCDB_IDENT_FLAG_CONFIG,\r
-                                widnames,\r
-                                &cb,\r
-                                &cfg_idents.n_idents);\r
-        n_tries++;\r
-    } while(KHM_FAILED(rv) &&\r
-            n_tries < 5);\r
-\r
-    if (KHM_FAILED(rv) ||\r
-        cfg_idents.n_idents == 0) {\r
-        cfg_idents.n_idents = 0;\r
-        goto _cleanup;\r
-    }\r
-\r
-    cfg_idents.idents = PMALLOC(sizeof(*cfg_idents.idents) * \r
-                               cfg_idents.n_idents);\r
-#ifdef DEBUG\r
-    assert(cfg_idents.idents);\r
-#endif\r
-    ZeroMemory(cfg_idents.idents, \r
-               sizeof(*cfg_idents.idents) * cfg_idents.n_idents);\r
-    cfg_idents.nc_idents = cfg_idents.n_idents;\r
-\r
-    i = 0;\r
-    for (t = widnames; t && *t; t = multi_string_next(t)) {\r
-        khm_handle ident;\r
-\r
-        if (KHM_FAILED(kcdb_identity_create(t, 0, &ident))) {\r
-            cfg_idents.n_idents--;\r
-            continue;\r
-        }\r
-\r
-        StringCbLength(t, KCDB_IDENT_MAXCB_NAME, &cb);\r
-        cb += sizeof(wchar_t);\r
-\r
-        cfg_idents.idents[i].idname = PMALLOC(cb);\r
-#ifdef DEBUG\r
-        assert(cfg_idents.idents[i].idname);\r
-#endif\r
-        StringCbCopy(cfg_idents.idents[i].idname, cb, t);\r
-\r
-        cfg_idents.idents[i].ident = ident;\r
-        cfg_idents.idents[i].removed = FALSE;\r
-\r
-        kcdb_identity_get_flags(ident, &cfg_idents.idents[i].flags);\r
-#ifdef DEBUG\r
-        assert(cfg_idents.idents[i].flags & KCDB_IDENT_FLAG_CONFIG);\r
-#endif\r
-\r
-        read_params_ident(&cfg_idents.idents[i]);\r
-\r
-        i++;\r
-        /* leave identity held */\r
-    }\r
-\r
- _cleanup:\r
-\r
-    cfg_idents.valid = TRUE;\r
-\r
-    if (widnames)\r
-        PFREE(widnames);\r
-}\r
-\r
-static void\r
-free_idents_data(void) {\r
-    int i;\r
-\r
-    if (!cfg_idents.valid)\r
-        return;\r
-\r
-    for (i=0; i< (int) cfg_idents.n_idents; i++) {\r
-        if (cfg_idents.idents[i].ident)\r
-            kcdb_identity_release(cfg_idents.idents[i].ident);\r
-        if (cfg_idents.idents[i].idname)\r
-            PFREE(cfg_idents.idents[i].idname);\r
-    }\r
-\r
-    if (cfg_idents.idents)\r
-        PFREE(cfg_idents.idents);\r
-\r
-    cfg_idents.idents = NULL;\r
-    cfg_idents.n_idents = 0;\r
-    cfg_idents.nc_idents = 0;\r
-    cfg_idents.valid = FALSE;\r
-}\r
-\r
-static void\r
-hold_idents_data(void) {\r
-    if (!cfg_idents.valid)\r
-        init_idents_data();\r
-#ifdef DEBUG\r
-    assert(cfg_idents.valid);\r
-#endif\r
-    cfg_idents.refcount++;\r
-}\r
-\r
-static void\r
-release_idents_data(void) {\r
-#ifdef DEBUG\r
-    assert(cfg_idents.valid);\r
-#endif\r
-    cfg_idents.refcount--;\r
-\r
-    if (cfg_idents.refcount == 0)\r
-        free_idents_data();\r
-}\r
-\r
-\r
-static void\r
-refresh_data_idents(HWND hwnd) {\r
-    cfg_idents.work.monitor =\r
-        (IsDlgButtonChecked(hwnd, IDC_CFG_MONITOR) == BST_CHECKED);\r
-    cfg_idents.work.auto_renew =\r
-        (IsDlgButtonChecked(hwnd, IDC_CFG_RENEW) == BST_CHECKED);\r
-    cfg_idents.work.sticky =\r
-        (IsDlgButtonChecked(hwnd, IDC_CFG_STICKY) == BST_CHECKED);\r
-}\r
-\r
-static void\r
-refresh_view_idents_state(HWND hwnd) {\r
-    BOOL modified;\r
-    BOOL applied;\r
-    khm_int32 flags = 0;\r
-\r
-    applied = cfg_idents.applied;\r
-    modified = (cfg_idents.work.monitor != cfg_idents.saved.monitor ||\r
-                cfg_idents.work.auto_renew != cfg_idents.saved.auto_renew ||\r
-                cfg_idents.work.sticky != cfg_idents.saved.sticky);\r
-\r
-    if (modified)\r
-        flags |= KHUI_CNFLAG_MODIFIED;\r
-    if (applied)\r
-        flags |= KHUI_CNFLAG_APPLIED;\r
-\r
-    khui_cfg_set_flags_inst(&cfg_idents.cfg, flags,\r
-                            KHUI_CNFLAG_APPLIED | KHUI_CNFLAG_MODIFIED);\r
-}\r
-\r
-struct ctrl_row_dimensions {\r
-    RECT enclosure;\r
-    RECT label;\r
-    RECT control;\r
-};\r
-\r
-typedef struct tag_add_ident_data {\r
-    khui_new_creds * nc;\r
-\r
-    struct ctrl_row_dimensions dim_small;\r
-    struct ctrl_row_dimensions dim_medium;\r
-    struct ctrl_row_dimensions dim_large;\r
-    int row_gap;\r
-\r
-    int current_y;\r
-    int current_x;\r
-\r
-    HWND hwnd_last_ctrl;\r
-} add_ident_data;\r
-\r
-void\r
-get_ctrl_row_metrics(struct ctrl_row_dimensions * dim, HWND hw_lbl, HWND hw_ctl) {\r
-\r
-    assert(hw_lbl);\r
-    assert(hw_ctl);\r
-\r
-    GetWindowRect(hw_lbl, &dim->label);\r
-    GetWindowRect(hw_ctl, &dim->control);\r
-\r
-    UnionRect(&dim->enclosure, &dim->label, &dim->control);\r
-    OffsetRect(&dim->label,\r
-               -dim->enclosure.left,\r
-               -dim->enclosure.top);\r
-    OffsetRect(&dim->control,\r
-               -dim->enclosure.left,\r
-               -dim->enclosure.top);\r
-    OffsetRect(&dim->enclosure,\r
-               -dim->enclosure.left,\r
-               -dim->enclosure.top);\r
-}\r
-\r
-/* dialog box procedure for the "Add new identity" dialog */\r
-INT_PTR CALLBACK\r
-khm_cfg_add_ident_proc(HWND hwnd,\r
-                       UINT umsg,\r
-                       WPARAM wParam,\r
-                       LPARAM lParam) {\r
-    add_ident_data * d;\r
-\r
-    switch(umsg) {\r
-    case WM_INITDIALOG:\r
-        /* we create a new credentials blob and pull in the identity\r
-           selectors from the identity provider. */\r
-        d = PMALLOC(sizeof(*d));\r
-        ZeroMemory(d, sizeof(*d));\r
-\r
-        khui_cw_create_cred_blob(&d->nc);\r
-#ifdef DEBUG\r
-        assert(d->nc != NULL);\r
-#endif\r
-        if (d->nc == NULL) {\r
-            PFREE(d);\r
-            break;\r
-        }\r
-\r
-        if (KHM_FAILED(kcdb_identpro_get_ui_cb(&d->nc->ident_cb))) {\r
-            /* this should have worked.  The only reason it would fail\r
-               is if there is no identity provider or if the identity\r
-               provider does not support providing idnetity\r
-               selectors. */\r
-            khui_cw_destroy_cred_blob(d->nc);\r
-            PFREE(d);\r
-            break;\r
-        }\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-        SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);\r
-#pragma warning(pop)\r
-\r
-        /* get metrics for dynamic controls */\r
-        get_ctrl_row_metrics(&d->dim_small,\r
-                             GetDlgItem(hwnd, IDC_SM_LBL),\r
-                             GetDlgItem(hwnd, IDC_SM_CTL));\r
-        get_ctrl_row_metrics(&d->dim_medium,\r
-                             GetDlgItem(hwnd, IDC_MED_LBL),\r
-                             GetDlgItem(hwnd, IDC_MED_CTL));\r
-        get_ctrl_row_metrics(&d->dim_large,\r
-                             GetDlgItem(hwnd, IDC_LG_LBL),\r
-                             GetDlgItem(hwnd, IDC_LG_CTL));\r
-\r
-        {\r
-            RECT rlbl;\r
-            RECT rctl;\r
-            RECT rwnd;\r
-\r
-            GetWindowRect(GetDlgItem(hwnd, IDC_SM_LBL),\r
-                          &rlbl);\r
-            GetWindowRect(GetDlgItem(hwnd, IDC_SM_CTL),\r
-                          &rctl);\r
-            GetWindowRect(hwnd, &rwnd);\r
-\r
-            OffsetRect(&rlbl, -rwnd.left, -rwnd.top);\r
-            OffsetRect(&rctl, -rwnd.left, -rwnd.top);\r
-\r
-            d->current_x = rlbl.left;\r
-            d->current_y = rctl.top - GetSystemMetrics(SM_CYCAPTION);\r
-\r
-            GetWindowRect(GetDlgItem(hwnd, IDC_MED_CTL),\r
-                          &rlbl);\r
-            OffsetRect(&rlbl, -rwnd.left, -rwnd.top);\r
-\r
-            d->row_gap = rlbl.top - rctl.bottom;\r
-        }\r
-\r
-        d->nc->hwnd = hwnd;\r
-\r
-        /* now call the UI callback and make it create the\r
-           controls. */\r
-        d->nc->ident_cb(d->nc, WMNC_IDENT_INIT, NULL, 0, 0,\r
-                        (LPARAM) hwnd);\r
-\r
-        break;\r
-\r
-    case WM_DESTROY:\r
-        d = (add_ident_data *)(LONG_PTR)\r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-        if (d == NULL)\r
-            break;\r
-\r
-        d->nc->ident_cb(d->nc, WMNC_IDENT_EXIT, NULL, 0, 0, 0);\r
-\r
-        khui_cw_destroy_cred_blob(d->nc);\r
-        PFREE(d);\r
-        break;\r
-\r
-    case KHUI_WM_NC_NOTIFY:\r
-        d = (add_ident_data *)(LONG_PTR)\r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        switch(HIWORD(wParam)) {\r
-        case WMNC_ADD_CONTROL_ROW:\r
-            {\r
-                khui_control_row * row;\r
-                RECT r_lbl, r_inp, r_enc;\r
-                struct ctrl_row_dimensions * dim;\r
-                HFONT hf;\r
-\r
-                row = (khui_control_row *) lParam;\r
-\r
-#ifdef DEBUG\r
-                assert(row->label);\r
-                assert(row->input);\r
-                assert(d);\r
-#endif\r
-\r
-                if (row->size == KHUI_CTRLSIZE_SMALL) {\r
-                    dim = &d->dim_small;\r
-                } else if (row->size == KHUI_CTRLSIZE_HALF) {\r
-                    dim = &d->dim_medium;\r
-                } else {\r
-                    dim = &d->dim_large;\r
-#ifdef DEBUG\r
-                    assert(row->size == KHUI_CTRLSIZE_FULL);\r
-#endif\r
-                }\r
-\r
-                CopyRect(&r_enc, &dim->enclosure);\r
-                CopyRect(&r_lbl, &dim->label);\r
-                CopyRect(&r_inp, &dim->control);\r
-\r
-                OffsetRect(&r_enc, d->current_x, d->current_y);\r
-                OffsetRect(&r_lbl, r_enc.left, r_enc.top);\r
-                OffsetRect(&r_inp, r_enc.left, r_enc.top);\r
-\r
-                d->current_y += r_enc.bottom - r_enc.top;\r
-\r
-                hf = (HFONT) SendDlgItemMessage(hwnd, IDOK, WM_GETFONT, 0, 0);\r
-\r
-                if (row->label) {\r
-                    SetWindowPos(row->label,\r
-                                 ((d->hwnd_last_ctrl != NULL)?\r
-                                  d->hwnd_last_ctrl :\r
-                                  HWND_TOP),\r
-                                 r_lbl.left, r_lbl.top,\r
-                                 r_lbl.right - r_lbl.left,\r
-                                 r_lbl.bottom - r_lbl.top,\r
-                                 SWP_DEFERERASE | SWP_NOACTIVATE |\r
-                                 SWP_NOOWNERZORDER);\r
-                    if (hf)\r
-                        SendMessage(row->label, WM_SETFONT,\r
-                                    (WPARAM) hf,\r
-                                    TRUE);\r
-                    d->hwnd_last_ctrl = row->label;\r
-                }\r
-\r
-                if (row->input) {\r
-                    SetWindowPos(row->input,\r
-                                 ((d->hwnd_last_ctrl != NULL)?\r
-                                  d->hwnd_last_ctrl :\r
-                                  HWND_TOP),\r
-                                 r_inp.left, r_inp.top,\r
-                                 r_inp.right - r_inp.left,\r
-                                 r_inp.bottom - r_inp.top,\r
-                                 SWP_DEFERERASE | SWP_NOACTIVATE |\r
-                                 SWP_NOOWNERZORDER);\r
-                    if (hf)\r
-                        SendMessage(row->input, WM_SETFONT,\r
-                                    (WPARAM) hf,\r
-                                    TRUE);\r
-                    d->hwnd_last_ctrl = row->input;\r
-                }\r
-            }\r
-            break;\r
-\r
-        case WMNC_IDENTITY_CHANGE:\r
-            break;\r
-        }\r
-        return TRUE;\r
-\r
-    case WM_COMMAND:\r
-        if (LOWORD(wParam) == IDOK) {\r
-            wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-            wchar_t err_msg[1024];\r
-            khm_handle ident = NULL;\r
-            khm_handle csp_ident = NULL;\r
-            khm_size cb;\r
-            khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-            d = (add_ident_data *)(LONG_PTR)\r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-            if (!d || !d->nc)\r
-                break;\r
-\r
-            /* check if there was an identity selected */\r
-            if (d->nc->n_identities == 0 ||\r
-                d->nc->identities[0] == NULL) {\r
-\r
-                StringCbCopy(idname, sizeof(idname), L"");\r
-\r
-                LoadString(khm_hInstance, IDS_CFG_IDNAME_NON,\r
-                           err_msg, ARRAYLENGTH(err_msg));\r
-\r
-                goto show_failure;\r
-            }\r
-\r
-            ident = d->nc->identities[0];\r
-            kcdb_identity_hold(ident);\r
-\r
-            cb = sizeof(idname);\r
-            kcdb_identity_get_name(ident, idname, &cb);\r
-\r
-            /* now we have to create the identity configuration. */\r
-            if (KHM_FAILED(rv = kcdb_identity_get_config(ident,\r
-                                                         KHM_FLAG_CREATE,\r
-                                                         &csp_ident))) {\r
-                wchar_t fmt[256];\r
-\r
-                LoadString(khm_hInstance, IDS_CFG_IDNAME_CCC,\r
-                           fmt, ARRAYLENGTH(fmt));\r
-                StringCbPrintf(err_msg, sizeof(err_msg), fmt, rv);\r
-\r
-                kcdb_identity_release(ident);\r
-\r
-                goto show_failure;\r
-            }\r
-\r
-            /* create a value so that the configuration space will\r
-               actually be created in the registry.  We don't want\r
-               this new identity to be sticky. */\r
-            khc_write_int32(csp_ident, L"Sticky", 0);\r
-\r
-            khm_refresh_config();\r
-\r
-            kcdb_identity_release(ident);\r
-            khc_close_space(csp_ident);\r
-\r
-            EndDialog(hwnd, 0);\r
-            break;\r
-\r
-        show_failure:\r
-            {\r
-                wchar_t title[512];\r
-                wchar_t fmt[256];\r
-\r
-                if (!err_msg[0])\r
-                    break;\r
-\r
-                LoadString(khm_hInstance, IDS_CFG_IDNAME_PRB,\r
-                           fmt, ARRAYLENGTH(fmt));\r
-                StringCbPrintf(title, sizeof(title), fmt, idname);\r
-\r
-                MessageBox(hwnd, err_msg, title, MB_OK | MB_ICONSTOP);\r
-\r
-                /* don't end the dialog yet */\r
-                break;\r
-            }\r
-            break;\r
-            \r
-        } else if (LOWORD(wParam) == IDCANCEL) {\r
-            EndDialog(hwnd, 1);\r
-        } else {\r
-            d = (add_ident_data *)(LONG_PTR)\r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-            if (d && d->nc && d->nc->ident_cb) {\r
-                return d->nc->ident_cb(d->nc, WMNC_IDENT_WMSG,\r
-                                       hwnd, umsg, wParam, lParam);\r
-            }\r
-        }\r
-        break;\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-/* dialog procedure for the "general" pane of the "identities"\r
-   configuration node. */\r
-INT_PTR CALLBACK\r
-khm_cfg_ids_tab_proc(HWND hwnd,\r
-                    UINT umsg,\r
-                    WPARAM wParam,\r
-                    LPARAM lParam) {\r
-\r
-    switch(umsg) {\r
-    case WM_INITDIALOG:\r
-        {\r
-            HICON hicon;\r
-\r
-            hold_idents_data();\r
-\r
-            cfg_idents.hwnd = hwnd;\r
-            cfg_idents.cfg = *((khui_config_init_data *) lParam);\r
-\r
-            /* add the status icons */\r
-            if (cfg_idents.hi_status)\r
-                goto _done_with_icons;\r
-\r
-            cfg_idents.hi_status = \r
-                ImageList_Create(GetSystemMetrics(SM_CXSMICON),\r
-                                 GetSystemMetrics(SM_CYSMICON), \r
-                                 ILC_COLOR8 | ILC_MASK,\r
-                                 4,4);\r
-\r
-            hicon =\r
-                LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_ID),\r
-                          IMAGE_ICON,\r
-                          GetSystemMetrics(SM_CXSMICON),\r
-                          GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);\r
-\r
-            cfg_idents.idx_id = ImageList_AddIcon(cfg_idents.hi_status,\r
-                                                  hicon);\r
-\r
-            DestroyIcon(hicon);\r
-\r
-            hicon = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_CFG_DEFAULT),\r
-                              IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), \r
-                              GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);\r
-\r
-            cfg_idents.idx_default = ImageList_AddIcon(cfg_idents.hi_status, \r
-                                                       hicon) + 1;\r
-\r
-            DestroyIcon(hicon);\r
-\r
-            hicon = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_CFG_MODIFIED),\r
-                              IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), \r
-                              GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);\r
-\r
-            cfg_idents.idx_modified = ImageList_AddIcon(cfg_idents.hi_status, \r
-                                                        hicon) + 1;\r
-\r
-            DestroyIcon(hicon);\r
-\r
-            hicon = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_CFG_APPLIED),\r
-                              IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), \r
-                              GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);\r
-\r
-            cfg_idents.idx_applied = ImageList_AddIcon(cfg_idents.hi_status, \r
-                                                       hicon) + 1;\r
-\r
-            DestroyIcon(hicon);\r
-\r
-            hicon = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_CFG_DELETED),\r
-                              IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), \r
-                              GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);\r
-\r
-            cfg_idents.idx_deleted = ImageList_AddIcon(cfg_idents.hi_status, \r
-                                                       hicon) + 1;\r
-\r
-            DestroyIcon(hicon);\r
-\r
-        _done_with_icons:\r
-\r
-            CheckDlgButton(hwnd, IDC_CFG_MONITOR,\r
-                           (cfg_idents.work.monitor)?BST_CHECKED:BST_UNCHECKED);\r
-            CheckDlgButton(hwnd, IDC_CFG_RENEW,\r
-                           (cfg_idents.work.auto_renew)?BST_CHECKED:BST_UNCHECKED);\r
-            CheckDlgButton(hwnd, IDC_CFG_STICKY,\r
-                           (cfg_idents.work.sticky)?BST_CHECKED:BST_UNCHECKED);\r
-\r
-        }\r
-        return FALSE;\r
-\r
-    case WM_COMMAND:\r
-\r
-        if (HIWORD(wParam) == BN_CLICKED) {\r
-            UINT ctrl = LOWORD(wParam);\r
-\r
-            switch(ctrl) {\r
-            case IDC_CFG_MONITOR:\r
-            case IDC_CFG_RENEW:\r
-            case IDC_CFG_STICKY:\r
-                refresh_data_idents(hwnd);\r
-                break;\r
-\r
-            case IDC_CFG_ADDIDENT:\r
-                DialogBoxParam(khm_hInstance,\r
-                               MAKEINTRESOURCE(IDD_CFG_ADDIDENT),\r
-                               hwnd,\r
-                               khm_cfg_add_ident_proc,\r
-                               (LPARAM) hwnd);\r
-                break;\r
-            }\r
-\r
-            refresh_view_idents_state(hwnd);\r
-        }\r
-\r
-        khm_set_dialog_result(hwnd, 0);\r
-        return TRUE;\r
-\r
-    case KHUI_WM_CFG_NOTIFY:\r
-        {\r
-            switch(HIWORD(wParam)) {\r
-            case WMCFG_APPLY:\r
-                write_params_idents();\r
-                break;\r
-\r
-            case WMCFG_UPDATE_STATE:\r
-                refresh_view_idents_state(hwnd);\r
-                break;\r
-            }\r
-        }\r
-        return TRUE;\r
-\r
-    case WM_DESTROY:\r
-        cfg_idents.hwnd = NULL;\r
-\r
-        if (cfg_idents.hi_status != NULL) {\r
-            ImageList_Destroy(cfg_idents.hi_status);\r
-            cfg_idents.hi_status = NULL;\r
-        }\r
-        release_idents_data();\r
-\r
-        khm_set_dialog_result(hwnd, 0);\r
-\r
-        return TRUE;\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-/* dialog procedure for the "Identities" configuration node */\r
-INT_PTR CALLBACK\r
-khm_cfg_identities_proc(HWND hwnd,\r
-                        UINT uMsg,\r
-                        WPARAM wParam,\r
-                        LPARAM lParam) {\r
-    HWND hw;\r
-    switch(uMsg) {\r
-    case WM_INITDIALOG:\r
-        set_window_node(hwnd, (khui_config_node) lParam);\r
-        add_subpanels(hwnd, (khui_config_node) lParam,\r
-                      (khui_config_node) lParam);\r
-        hw = GetDlgItem(hwnd, IDC_CFG_TAB);\r
-        show_tab_panel(hwnd,\r
-                       (khui_config_node) lParam,\r
-                       hw,\r
-                       TabCtrl_GetCurSel(hw),\r
-                       TRUE);\r
-        return FALSE;\r
-\r
-    case WM_DESTROY:\r
-        return 0;\r
-\r
-    case KHUI_WM_CFG_NOTIFY:\r
-        return handle_cfg_notify(hwnd, wParam, lParam);\r
-\r
-    case WM_NOTIFY:\r
-        return handle_notify(hwnd, wParam, lParam);\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-static ident_data *\r
-find_ident_by_node(khui_config_node node) {\r
-    khm_size cb;\r
-    wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-    int i;\r
-    khm_handle ident = NULL;\r
-\r
-    cb = sizeof(idname);\r
-    khui_cfg_get_name(node, idname, &cb);\r
-\r
-    for (i=0; i < (int)cfg_idents.n_idents; i++) {\r
-        if (!wcscmp(cfg_idents.idents[i].idname, idname))\r
-            break;\r
-    }\r
-\r
-    if (i < (int)cfg_idents.n_idents)\r
-        return &cfg_idents.idents[i];\r
-\r
-    /* there is no identity data for this configuration node.  We try\r
-       to create it. */\r
-    if (KHM_FAILED(kcdb_identity_create(idname, 0, &ident)))\r
-        return NULL;\r
-\r
-    if (cfg_idents.n_idents >= cfg_idents.nc_idents) {\r
-        cfg_idents.nc_idents = UBOUNDSS(cfg_idents.n_idents + 1,\r
-                                        IDENTS_DATA_ALLOC_INCR,\r
-                                        IDENTS_DATA_ALLOC_INCR);\r
-#ifdef DEBUG\r
-        assert(cfg_idents.nc_idents > cfg_idents.n_idents);\r
-#endif\r
-        cfg_idents.idents = PREALLOC(cfg_idents.idents,\r
-                                     sizeof(*cfg_idents.idents) *\r
-                                     cfg_idents.nc_idents);\r
-#ifdef DEBUG\r
-        assert(cfg_idents.idents);\r
-#endif\r
-        ZeroMemory(&(cfg_idents.idents[cfg_idents.n_idents]),\r
-                   sizeof(*cfg_idents.idents) *\r
-                   (cfg_idents.nc_idents - cfg_idents.n_idents));\r
-    }\r
-\r
-    i = (int) cfg_idents.n_idents;\r
-\r
-    StringCbLength(idname, KCDB_IDENT_MAXCB_NAME, &cb);\r
-    cb += sizeof(wchar_t);\r
-\r
-    cfg_idents.idents[i].idname = PMALLOC(cb);\r
-#ifdef DEBUG\r
-    assert(cfg_idents.idents[i].idname);\r
-#endif\r
-    StringCbCopy(cfg_idents.idents[i].idname, cb, idname);\r
-\r
-    cfg_idents.idents[i].ident = ident;\r
-    cfg_idents.idents[i].removed = FALSE;\r
-\r
-    kcdb_identity_get_flags(ident, &cfg_idents.idents[i].flags);\r
-#ifdef DEBUG\r
-    assert(cfg_idents.idents[i].flags & KCDB_IDENT_FLAG_CONFIG);\r
-#endif\r
-\r
-    read_params_ident(&cfg_idents.idents[i]);\r
-\r
-    cfg_idents.n_idents++;\r
-\r
-    /* leave ident held. */\r
-\r
-    return &cfg_idents.idents[i];\r
-}\r
-\r
-static void\r
-refresh_view_ident(HWND hwnd, khui_config_node node) {\r
-    ident_data * d;\r
-\r
-    d = find_ident_by_node(node);\r
-#ifdef DEBUG\r
-    assert(d);\r
-#endif\r
-\r
-    CheckDlgButton(hwnd, IDC_CFG_MONITOR,\r
-                   (d->work.monitor? BST_CHECKED: BST_UNCHECKED));\r
-    CheckDlgButton(hwnd, IDC_CFG_RENEW,\r
-                   (d->work.auto_renew? BST_CHECKED: BST_UNCHECKED));\r
-    CheckDlgButton(hwnd, IDC_CFG_STICKY,\r
-                   (d->work.sticky? BST_CHECKED: BST_UNCHECKED));\r
-}\r
-\r
-static void\r
-mark_remove_ident(HWND hwnd, khui_config_init_data * idata) {\r
-    ident_data * d;\r
-\r
-    d = find_ident_by_node(idata->ctx_node);\r
-#ifdef DEBUG\r
-    assert(d);\r
-#endif\r
-\r
-    if (d->removed)\r
-        return;\r
-\r
-    d->removed = TRUE;\r
-\r
-    khui_cfg_set_flags_inst(idata, KHUI_CNFLAG_MODIFIED,\r
-                            KHUI_CNFLAG_MODIFIED);\r
-\r
-    EnableWindow(GetDlgItem(hwnd, IDC_CFG_REMOVE), FALSE);\r
-}\r
-\r
-static void\r
-refresh_data_ident(HWND hwnd, khui_config_init_data * idata) {\r
-    ident_data * d;\r
-\r
-    d = find_ident_by_node(idata->ctx_node);\r
-#ifdef DEBUG\r
-    assert(d);\r
-#endif\r
-\r
-    if (IsDlgButtonChecked(hwnd, IDC_CFG_MONITOR) == BST_CHECKED)\r
-        d->work.monitor = TRUE;\r
-    else\r
-        d->work.monitor = FALSE;\r
-\r
-    if (IsDlgButtonChecked(hwnd, IDC_CFG_RENEW) == BST_CHECKED)\r
-        d->work.auto_renew = TRUE;\r
-    else\r
-        d->work.auto_renew = FALSE;\r
-\r
-    if (IsDlgButtonChecked(hwnd, IDC_CFG_STICKY) == BST_CHECKED)\r
-        d->work.sticky = TRUE;\r
-    else\r
-        d->work.sticky = FALSE;\r
-\r
-    if (d->work.monitor != d->saved.monitor ||\r
-        d->work.auto_renew != d->saved.auto_renew ||\r
-        d->work.sticky != d->saved.sticky) {\r
-\r
-        khui_cfg_set_flags_inst(idata, KHUI_CNFLAG_MODIFIED,\r
-                                KHUI_CNFLAG_MODIFIED);\r
-\r
-    } else {\r
-        khui_cfg_set_flags_inst(idata, 0,\r
-                                KHUI_CNFLAG_MODIFIED);\r
-    }\r
-}\r
-\r
-/* dialog procedure for the "general" pane of individual identity\r
-   configuration nodes. */\r
-INT_PTR CALLBACK\r
-khm_cfg_id_tab_proc(HWND hwnd,\r
-                    UINT umsg,\r
-                    WPARAM wParam,\r
-                    LPARAM lParam) {\r
-\r
-    khui_config_init_data * idata;\r
-\r
-    switch(umsg) {\r
-    case WM_INITDIALOG:\r
-        {\r
-            ident_data * d;\r
-\r
-            hold_idents_data();\r
-\r
-            idata = (khui_config_init_data *) lParam;\r
-\r
-            khui_cfg_init_dialog_data(hwnd, idata, 0, NULL, NULL);\r
-\r
-            refresh_view_ident(hwnd, idata->ctx_node);\r
-\r
-            d = find_ident_by_node(idata->ctx_node);\r
-            if (d)\r
-                d->hwnd = hwnd;\r
-#ifdef DEBUG\r
-            else\r
-                assert(FALSE);\r
-#endif\r
-        }\r
-        return FALSE;\r
-\r
-    case WM_COMMAND:\r
-        khui_cfg_get_dialog_data(hwnd, &idata, NULL);\r
-\r
-        if (HIWORD(wParam) == BN_CLICKED) {\r
-            switch(LOWORD(wParam)) {\r
-            case IDC_CFG_MONITOR:\r
-            case IDC_CFG_RENEW:\r
-            case IDC_CFG_STICKY:\r
-\r
-                refresh_data_ident(hwnd, idata);\r
-                if (cfg_idents.hwnd)\r
-                    PostMessage(cfg_idents.hwnd, KHUI_WM_CFG_NOTIFY,\r
-                                MAKEWPARAM(1, WMCFG_UPDATE_STATE), 0);\r
-                break;\r
-\r
-            case IDC_CFG_REMOVE:\r
-                mark_remove_ident(hwnd, idata);\r
-                if (cfg_idents.hwnd)\r
-                    PostMessage(cfg_idents.hwnd, KHUI_WM_CFG_NOTIFY,\r
-                                MAKEWPARAM(1, WMCFG_UPDATE_STATE), 0);\r
-                break;\r
-            }\r
-        }\r
-\r
-        khm_set_dialog_result(hwnd, 0);\r
-        return TRUE;\r
-\r
-    case WM_DESTROY:\r
-        {\r
-            ident_data * d;\r
-\r
-            khui_cfg_get_dialog_data(hwnd, &idata, NULL);\r
-\r
-            d = find_ident_by_node(idata->ctx_node);\r
-            if (d)\r
-                d->hwnd = NULL;\r
-\r
-            release_idents_data();\r
-            khui_cfg_free_dialog_data(hwnd);\r
-            khm_set_dialog_result(hwnd, 0);\r
-        }\r
-        return TRUE;\r
-\r
-    case KHUI_WM_CFG_NOTIFY:\r
-        {\r
-            ident_data * d;\r
-            BOOL * cont;\r
-\r
-            khui_cfg_get_dialog_data(hwnd, &idata, NULL);\r
-\r
-            switch (HIWORD(wParam)) {\r
-            case WMCFG_APPLY:\r
-                cont = (BOOL *) lParam;\r
-                d = find_ident_by_node(idata->ctx_node);\r
-                write_params_ident(d);\r
-                if (d->removed && cont)\r
-                    *cont = FALSE;\r
-                khui_cfg_set_flags_inst(idata, KHUI_CNFLAG_APPLIED,\r
-                                        KHUI_CNFLAG_APPLIED | \r
-                                        KHUI_CNFLAG_MODIFIED);\r
-                break;\r
-\r
-            case WMCFG_UPDATE_STATE:\r
-                refresh_view_ident(hwnd, idata->ctx_node);\r
-                refresh_data_ident(hwnd, idata);\r
-                break;\r
-            }\r
-        }\r
-        return TRUE;\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-/* dialog procedure for individual identity configuration nodes */\r
-INT_PTR CALLBACK\r
-khm_cfg_identity_proc(HWND hwnd,\r
-                      UINT uMsg,\r
-                      WPARAM wParam,\r
-                      LPARAM lParam) {\r
-    HWND hw;\r
-\r
-    switch(uMsg) {\r
-    case WM_INITDIALOG:\r
-        {\r
-            khui_config_node refnode = NULL;\r
-\r
-            set_window_node(hwnd, (khui_config_node) lParam);\r
-\r
-            khui_cfg_open(NULL, L"KhmIdentities", &refnode);\r
-#ifdef DEBUG\r
-            assert(refnode != NULL);\r
-#endif\r
-            add_subpanels(hwnd,\r
-                          (khui_config_node) lParam,\r
-                          refnode);\r
-\r
-            hw = GetDlgItem(hwnd, IDC_CFG_TAB);\r
-\r
-            show_tab_panel(hwnd,\r
-                           (khui_config_node) lParam,\r
-                           hw,\r
-                           TabCtrl_GetCurSel(hw),\r
-                           TRUE);\r
-\r
-            khui_cfg_release(refnode);\r
-        }\r
-        return FALSE;\r
-\r
-    case WM_DESTROY:\r
-        return 0;\r
-\r
-    case KHUI_WM_CFG_NOTIFY:\r
-        return handle_cfg_notify(hwnd, wParam, lParam);\r
-\r
-    case WM_NOTIFY:\r
-        return handle_notify(hwnd, wParam, lParam);\r
-    }\r
-    return FALSE;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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;
+    BOOL cont = TRUE;
+
+    count = TabCtrl_GetItemCount(hw_tab);
+
+    for (idx = 0; idx < count && cont; 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
+
+        SendMessage(hw, KHUI_WM_CFG_NOTIFY,
+                    MAKEWPARAM(0, WMCFG_APPLY), (LPARAM) &cont);
+    }
+}
+
+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;
+    HWND hw_firstctl;
+    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);
+
+    hw_firstctl = GetNextDlgTabItem(hw, NULL, FALSE);
+    if (hw_firstctl) {
+        SetFocus(hw_firstctl);
+    }
+}
+
+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_global_props {
+    BOOL monitor;
+    BOOL auto_renew;
+    BOOL sticky;
+} global_props;
+
+typedef struct tag_idents_data {
+    BOOL         valid;
+
+    ident_data * idents;
+    khm_size     n_idents;
+    khm_size     nc_idents;
+#define IDENTS_DATA_ALLOC_INCR 8
+
+    /* global options */
+    global_props saved;
+    global_props work;
+    BOOL         applied;
+
+    int          refcount;
+
+    HIMAGELIST   hi_status;
+    int          idx_id;
+    int          idx_default;
+    int          idx_modified;
+    int          idx_applied;
+    int          idx_deleted;
+
+    HWND         hwnd;
+    khui_config_init_data cfg;
+} idents_data;
+
+static idents_data cfg_idents = {FALSE, NULL, 0, 0,
+                                 {0, 0, 0},
+                                 {0, 0, 0},
+                                 FALSE,
+
+                                 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->removed) {
+        khm_handle h = NULL;
+        khm_int32 flags = 0;
+
+        khc_remove_space(csp_ident);
+
+        /* calling kcdb_identity_get_config() will update the
+           KCDB_IDENT_FLAG_CONFIG flag for the identity to reflect the
+           fact that it nolonger has a configuration. */
+        kcdb_identity_get_config(d->ident, 0, &h);
+        if (h) {
+            /* what the ? */
+#ifdef DEBUG
+            assert(FALSE);
+#endif
+            khc_close_space(h);
+        }
+#ifdef DEBUG
+        kcdb_identity_get_flags(d->ident, &flags);
+        assert(!(flags & KCDB_IDENT_FLAG_CONFIG));
+#endif
+
+    } else {
+
+        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) {
+            kcdb_identity_set_flags(d->ident,
+                                    (d->work.sticky)?KCDB_IDENT_FLAG_STICKY:0,
+                                    KCDB_IDENT_FLAG_STICKY);
+        }
+    }
+
+    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);
+
+    khm_refresh_config();
+}
+
+static void
+write_params_idents(void) {
+    khm_handle csp_cw = NULL;
+
+    if (KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow",
+                                     KHM_FLAG_CREATE, &csp_cw))) {
+        if (cfg_idents.work.monitor != cfg_idents.saved.monitor) {
+            khc_write_int32(csp_cw, L"DefaultMonitor",
+                            !!cfg_idents.work.monitor);
+            cfg_idents.work.monitor = cfg_idents.saved.monitor;
+            cfg_idents.applied = TRUE;
+        }
+        if (cfg_idents.work.auto_renew != cfg_idents.saved.auto_renew) {
+            khc_write_int32(csp_cw, L"DefaultAllowAutoRenew",
+                            !!cfg_idents.work.auto_renew);
+            cfg_idents.work.auto_renew = cfg_idents.saved.auto_renew;
+            cfg_idents.applied = TRUE;
+        }
+        if (cfg_idents.work.sticky != cfg_idents.saved.sticky) {
+            khc_write_int32(csp_cw, L"DefaultSticky",
+                            !!cfg_idents.work.sticky);
+            cfg_idents.work.sticky = cfg_idents.saved.sticky;
+            cfg_idents.applied = TRUE;
+        }
+
+        khc_close_space(csp_cw);
+        csp_cw = NULL;
+    }
+
+#if 0
+    for (i=0; i < (int)cfg_idents.n_idents; i++) {
+        write_params_ident(&cfg_idents.idents[i]);
+    }
+#endif
+
+    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;
+    khm_handle csp_cw = NULL;
+
+    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
+
+    if (KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow", 0, &csp_cw))) {
+        khm_int32 t;
+
+        if (KHM_SUCCEEDED(khc_read_int32(csp_cw, L"DefaultMonitor", &t)))
+            cfg_idents.saved.monitor = !!t;
+        else
+            cfg_idents.saved.monitor = TRUE;
+
+        if (KHM_SUCCEEDED(khc_read_int32(csp_cw, L"DefaultAllowAutoRenew", &t)))
+            cfg_idents.saved.auto_renew = !!t;
+        else
+            cfg_idents.saved.auto_renew = TRUE;
+
+        if (KHM_SUCCEEDED(khc_read_int32(csp_cw, L"DefaultSticky", &t)))
+            cfg_idents.saved.sticky = !!t;
+        else
+            cfg_idents.saved.sticky = FALSE;
+
+        khc_close_space(csp_cw);
+        csp_cw = NULL;
+
+    } else {
+
+        cfg_idents.saved.monitor = TRUE;
+        cfg_idents.saved.auto_renew = TRUE;
+        cfg_idents.saved.sticky = FALSE;
+
+    }
+
+    cfg_idents.work = cfg_idents.saved;
+    cfg_idents.applied = FALSE;
+
+    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)
+            PFREE(widnames);
+        widnames = PMALLOC(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 = PMALLOC(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 = PMALLOC(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);
+#ifdef DEBUG
+        assert(cfg_idents.idents[i].flags & KCDB_IDENT_FLAG_CONFIG);
+#endif
+
+        read_params_ident(&cfg_idents.idents[i]);
+
+        i++;
+        /* leave identity held */
+    }
+
+ _cleanup:
+
+    cfg_idents.valid = TRUE;
+
+    if (widnames)
+        PFREE(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)
+            PFREE(cfg_idents.idents[i].idname);
+    }
+
+    if (cfg_idents.idents)
+        PFREE(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();
+}
+
+
+static void
+refresh_data_idents(HWND hwnd) {
+    cfg_idents.work.monitor =
+        (IsDlgButtonChecked(hwnd, IDC_CFG_MONITOR) == BST_CHECKED);
+    cfg_idents.work.auto_renew =
+        (IsDlgButtonChecked(hwnd, IDC_CFG_RENEW) == BST_CHECKED);
+    cfg_idents.work.sticky =
+        (IsDlgButtonChecked(hwnd, IDC_CFG_STICKY) == BST_CHECKED);
+}
+
+static void
+refresh_view_idents_state(HWND hwnd) {
+    BOOL modified;
+    BOOL applied;
+    khm_int32 flags = 0;
+
+    applied = cfg_idents.applied;
+    modified = (cfg_idents.work.monitor != cfg_idents.saved.monitor ||
+                cfg_idents.work.auto_renew != cfg_idents.saved.auto_renew ||
+                cfg_idents.work.sticky != cfg_idents.saved.sticky);
+
+    if (modified)
+        flags |= KHUI_CNFLAG_MODIFIED;
+    if (applied)
+        flags |= KHUI_CNFLAG_APPLIED;
+
+    khui_cfg_set_flags_inst(&cfg_idents.cfg, flags,
+                            KHUI_CNFLAG_APPLIED | KHUI_CNFLAG_MODIFIED);
+}
+
+struct ctrl_row_dimensions {
+    RECT enclosure;
+    RECT label;
+    RECT control;
+};
+
+typedef struct tag_add_ident_data {
+    khui_new_creds * nc;
+
+    struct ctrl_row_dimensions dim_small;
+    struct ctrl_row_dimensions dim_medium;
+    struct ctrl_row_dimensions dim_large;
+    int row_gap;
+
+    int current_y;
+    int current_x;
+
+    HWND hwnd_last_ctrl;
+} add_ident_data;
+
+void
+get_ctrl_row_metrics(struct ctrl_row_dimensions * dim, HWND hw_lbl, HWND hw_ctl) {
+
+    assert(hw_lbl);
+    assert(hw_ctl);
+
+    GetWindowRect(hw_lbl, &dim->label);
+    GetWindowRect(hw_ctl, &dim->control);
+
+    UnionRect(&dim->enclosure, &dim->label, &dim->control);
+    OffsetRect(&dim->label,
+               -dim->enclosure.left,
+               -dim->enclosure.top);
+    OffsetRect(&dim->control,
+               -dim->enclosure.left,
+               -dim->enclosure.top);
+    OffsetRect(&dim->enclosure,
+               -dim->enclosure.left,
+               -dim->enclosure.top);
+}
+
+/* dialog box procedure for the "Add new identity" dialog */
+INT_PTR CALLBACK
+khm_cfg_add_ident_proc(HWND hwnd,
+                       UINT umsg,
+                       WPARAM wParam,
+                       LPARAM lParam) {
+    add_ident_data * d;
+
+    switch(umsg) {
+    case WM_INITDIALOG:
+        /* we create a new credentials blob and pull in the identity
+           selectors from the identity provider. */
+        d = PMALLOC(sizeof(*d));
+        ZeroMemory(d, sizeof(*d));
+
+        khui_cw_create_cred_blob(&d->nc);
+#ifdef DEBUG
+        assert(d->nc != NULL);
+#endif
+        if (d->nc == NULL) {
+            PFREE(d);
+            break;
+        }
+
+        if (KHM_FAILED(kcdb_identpro_get_ui_cb(&d->nc->ident_cb))) {
+            /* this should have worked.  The only reason it would fail
+               is if there is no identity provider or if the identity
+               provider does not support providing idnetity
+               selectors. */
+            khui_cw_destroy_cred_blob(d->nc);
+            PFREE(d);
+            break;
+        }
+
+#pragma warning(push)
+#pragma warning(disable: 4244)
+        SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);
+#pragma warning(pop)
+
+        /* get metrics for dynamic controls */
+        get_ctrl_row_metrics(&d->dim_small,
+                             GetDlgItem(hwnd, IDC_SM_LBL),
+                             GetDlgItem(hwnd, IDC_SM_CTL));
+        get_ctrl_row_metrics(&d->dim_medium,
+                             GetDlgItem(hwnd, IDC_MED_LBL),
+                             GetDlgItem(hwnd, IDC_MED_CTL));
+        get_ctrl_row_metrics(&d->dim_large,
+                             GetDlgItem(hwnd, IDC_LG_LBL),
+                             GetDlgItem(hwnd, IDC_LG_CTL));
+
+        {
+            RECT rlbl;
+            RECT rctl;
+            RECT rwnd;
+
+            GetWindowRect(GetDlgItem(hwnd, IDC_SM_LBL),
+                          &rlbl);
+            GetWindowRect(GetDlgItem(hwnd, IDC_SM_CTL),
+                          &rctl);
+            GetWindowRect(hwnd, &rwnd);
+
+            OffsetRect(&rlbl, -rwnd.left, -rwnd.top);
+            OffsetRect(&rctl, -rwnd.left, -rwnd.top);
+
+            d->current_x = rlbl.left;
+            d->current_y = rctl.top - GetSystemMetrics(SM_CYCAPTION);
+
+            GetWindowRect(GetDlgItem(hwnd, IDC_MED_CTL),
+                          &rlbl);
+            OffsetRect(&rlbl, -rwnd.left, -rwnd.top);
+
+            d->row_gap = rlbl.top - rctl.bottom;
+        }
+
+        d->nc->hwnd = hwnd;
+
+        /* now call the UI callback and make it create the
+           controls. */
+        d->nc->ident_cb(d->nc, WMNC_IDENT_INIT, NULL, 0, 0,
+                        (LPARAM) hwnd);
+
+        break;
+
+    case WM_DESTROY:
+        d = (add_ident_data *)(LONG_PTR)
+            GetWindowLongPtr(hwnd, DWLP_USER);
+        if (d == NULL)
+            break;
+
+        d->nc->ident_cb(d->nc, WMNC_IDENT_EXIT, NULL, 0, 0, 0);
+
+        khui_cw_destroy_cred_blob(d->nc);
+        PFREE(d);
+        break;
+
+    case KHUI_WM_NC_NOTIFY:
+        d = (add_ident_data *)(LONG_PTR)
+            GetWindowLongPtr(hwnd, DWLP_USER);
+
+        switch(HIWORD(wParam)) {
+        case WMNC_ADD_CONTROL_ROW:
+            {
+                khui_control_row * row;
+                RECT r_lbl, r_inp, r_enc;
+                struct ctrl_row_dimensions * dim;
+                HFONT hf;
+
+                row = (khui_control_row *) lParam;
+
+#ifdef DEBUG
+                assert(row->label);
+                assert(row->input);
+                assert(d);
+#endif
+
+                if (row->size == KHUI_CTRLSIZE_SMALL) {
+                    dim = &d->dim_small;
+                } else if (row->size == KHUI_CTRLSIZE_HALF) {
+                    dim = &d->dim_medium;
+                } else {
+                    dim = &d->dim_large;
+#ifdef DEBUG
+                    assert(row->size == KHUI_CTRLSIZE_FULL);
+#endif
+                }
+
+                CopyRect(&r_enc, &dim->enclosure);
+                CopyRect(&r_lbl, &dim->label);
+                CopyRect(&r_inp, &dim->control);
+
+                OffsetRect(&r_enc, d->current_x, d->current_y);
+                OffsetRect(&r_lbl, r_enc.left, r_enc.top);
+                OffsetRect(&r_inp, r_enc.left, r_enc.top);
+
+                d->current_y += r_enc.bottom - r_enc.top;
+
+                hf = (HFONT) SendDlgItemMessage(hwnd, IDOK, WM_GETFONT, 0, 0);
+
+                if (row->label) {
+                    SetWindowPos(row->label,
+                                 ((d->hwnd_last_ctrl != NULL)?
+                                  d->hwnd_last_ctrl :
+                                  HWND_TOP),
+                                 r_lbl.left, r_lbl.top,
+                                 r_lbl.right - r_lbl.left,
+                                 r_lbl.bottom - r_lbl.top,
+                                 SWP_DEFERERASE | SWP_NOACTIVATE |
+                                 SWP_NOOWNERZORDER);
+                    if (hf)
+                        SendMessage(row->label, WM_SETFONT,
+                                    (WPARAM) hf,
+                                    TRUE);
+                    d->hwnd_last_ctrl = row->label;
+                }
+
+                if (row->input) {
+                    SetWindowPos(row->input,
+                                 ((d->hwnd_last_ctrl != NULL)?
+                                  d->hwnd_last_ctrl :
+                                  HWND_TOP),
+                                 r_inp.left, r_inp.top,
+                                 r_inp.right - r_inp.left,
+                                 r_inp.bottom - r_inp.top,
+                                 SWP_DEFERERASE | SWP_NOACTIVATE |
+                                 SWP_NOOWNERZORDER);
+                    if (hf)
+                        SendMessage(row->input, WM_SETFONT,
+                                    (WPARAM) hf,
+                                    TRUE);
+                    d->hwnd_last_ctrl = row->input;
+                }
+            }
+            break;
+
+        case WMNC_IDENTITY_CHANGE:
+            break;
+        }
+        return TRUE;
+
+    case WM_COMMAND:
+        if (LOWORD(wParam) == IDOK) {
+            wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
+            wchar_t err_msg[1024];
+            khm_handle ident = NULL;
+            khm_handle csp_ident = NULL;
+            khm_size cb;
+            khm_int32 rv = KHM_ERROR_SUCCESS;
+
+            d = (add_ident_data *)(LONG_PTR)
+                GetWindowLongPtr(hwnd, DWLP_USER);
+
+            if (!d || !d->nc)
+                break;
+
+            /* check if there was an identity selected */
+            if (d->nc->n_identities == 0 ||
+                d->nc->identities[0] == NULL) {
+
+                StringCbCopy(idname, sizeof(idname), L"");
+
+                LoadString(khm_hInstance, IDS_CFG_IDNAME_NON,
+                           err_msg, ARRAYLENGTH(err_msg));
+
+                goto show_failure;
+            }
+
+            ident = d->nc->identities[0];
+            kcdb_identity_hold(ident);
+
+            cb = sizeof(idname);
+            kcdb_identity_get_name(ident, idname, &cb);
+
+            /* now we have to create the identity configuration. */
+            if (KHM_FAILED(rv = kcdb_identity_get_config(ident,
+                                                         KHM_FLAG_CREATE,
+                                                         &csp_ident))) {
+                wchar_t fmt[256];
+
+                LoadString(khm_hInstance, IDS_CFG_IDNAME_CCC,
+                           fmt, ARRAYLENGTH(fmt));
+                StringCbPrintf(err_msg, sizeof(err_msg), fmt, rv);
+
+                kcdb_identity_release(ident);
+
+                goto show_failure;
+            }
+
+            /* create a value so that the configuration space will
+               actually be created in the registry.  We don't want
+               this new identity to be sticky. */
+            khc_write_int32(csp_ident, L"Sticky", 0);
+
+            khm_refresh_config();
+
+            kcdb_identity_release(ident);
+            khc_close_space(csp_ident);
+
+            EndDialog(hwnd, 0);
+            break;
+
+        show_failure:
+            {
+                wchar_t title[512];
+                wchar_t fmt[256];
+
+                if (!err_msg[0])
+                    break;
+
+                LoadString(khm_hInstance, IDS_CFG_IDNAME_PRB,
+                           fmt, ARRAYLENGTH(fmt));
+                StringCbPrintf(title, sizeof(title), fmt, idname);
+
+                MessageBox(hwnd, err_msg, title, MB_OK | MB_ICONSTOP);
+
+                /* don't end the dialog yet */
+                break;
+            }
+            break;
+            
+        } else if (LOWORD(wParam) == IDCANCEL) {
+            EndDialog(hwnd, 1);
+        } else {
+            d = (add_ident_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);
+            }
+        }
+        break;
+    }
+
+    return FALSE;
+}
+
+/* dialog procedure for the "general" pane of the "identities"
+   configuration node. */
+INT_PTR CALLBACK
+khm_cfg_ids_tab_proc(HWND hwnd,
+                    UINT umsg,
+                    WPARAM wParam,
+                    LPARAM lParam) {
+
+    switch(umsg) {
+    case WM_INITDIALOG:
+        {
+            HICON hicon;
+
+            hold_idents_data();
+
+            cfg_idents.hwnd = hwnd;
+            cfg_idents.cfg = *((khui_config_init_data *) lParam);
+
+            /* add the status icons */
+            if (cfg_idents.hi_status)
+                goto _done_with_icons;
+
+            cfg_idents.hi_status = 
+                ImageList_Create(GetSystemMetrics(SM_CXSMICON),
+                                 GetSystemMetrics(SM_CYSMICON), 
+                                 ILC_COLOR8 | ILC_MASK,
+                                 4,4);
+
+            hicon =
+                LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_ID),
+                          IMAGE_ICON,
+                          GetSystemMetrics(SM_CXSMICON),
+                          GetSystemMetrics(SM_CYSMICON), 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, GetSystemMetrics(SM_CXSMICON), 
+                              GetSystemMetrics(SM_CYSMICON), 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, GetSystemMetrics(SM_CXSMICON), 
+                              GetSystemMetrics(SM_CYSMICON), 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, GetSystemMetrics(SM_CXSMICON), 
+                              GetSystemMetrics(SM_CYSMICON), 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, GetSystemMetrics(SM_CXSMICON), 
+                              GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);
+
+            cfg_idents.idx_deleted = ImageList_AddIcon(cfg_idents.hi_status, 
+                                                       hicon) + 1;
+
+            DestroyIcon(hicon);
+
+        _done_with_icons:
+
+            CheckDlgButton(hwnd, IDC_CFG_MONITOR,
+                           (cfg_idents.work.monitor)?BST_CHECKED:BST_UNCHECKED);
+            CheckDlgButton(hwnd, IDC_CFG_RENEW,
+                           (cfg_idents.work.auto_renew)?BST_CHECKED:BST_UNCHECKED);
+            CheckDlgButton(hwnd, IDC_CFG_STICKY,
+                           (cfg_idents.work.sticky)?BST_CHECKED:BST_UNCHECKED);
+
+        }
+        return FALSE;
+
+    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:
+                refresh_data_idents(hwnd);
+                break;
+
+            case IDC_CFG_ADDIDENT:
+                DialogBoxParam(khm_hInstance,
+                               MAKEINTRESOURCE(IDD_CFG_ADDIDENT),
+                               hwnd,
+                               khm_cfg_add_ident_proc,
+                               (LPARAM) 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);
+                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;
+}
+
+/* dialog procedure for the "Identities" configuration node */
+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;
+    khm_handle ident = NULL;
+
+    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];
+
+    /* there is no identity data for this configuration node.  We try
+       to create it. */
+    if (KHM_FAILED(kcdb_identity_create(idname, 0, &ident)))
+        return NULL;
+
+    if (cfg_idents.n_idents >= cfg_idents.nc_idents) {
+        cfg_idents.nc_idents = UBOUNDSS(cfg_idents.n_idents + 1,
+                                        IDENTS_DATA_ALLOC_INCR,
+                                        IDENTS_DATA_ALLOC_INCR);
+#ifdef DEBUG
+        assert(cfg_idents.nc_idents > cfg_idents.n_idents);
+#endif
+        cfg_idents.idents = PREALLOC(cfg_idents.idents,
+                                     sizeof(*cfg_idents.idents) *
+                                     cfg_idents.nc_idents);
+#ifdef DEBUG
+        assert(cfg_idents.idents);
+#endif
+        ZeroMemory(&(cfg_idents.idents[cfg_idents.n_idents]),
+                   sizeof(*cfg_idents.idents) *
+                   (cfg_idents.nc_idents - cfg_idents.n_idents));
+    }
+
+    i = (int) cfg_idents.n_idents;
+
+    StringCbLength(idname, KCDB_IDENT_MAXCB_NAME, &cb);
+    cb += sizeof(wchar_t);
+
+    cfg_idents.idents[i].idname = PMALLOC(cb);
+#ifdef DEBUG
+    assert(cfg_idents.idents[i].idname);
+#endif
+    StringCbCopy(cfg_idents.idents[i].idname, cb, idname);
+
+    cfg_idents.idents[i].ident = ident;
+    cfg_idents.idents[i].removed = FALSE;
+
+    kcdb_identity_get_flags(ident, &cfg_idents.idents[i].flags);
+#ifdef DEBUG
+    assert(cfg_idents.idents[i].flags & KCDB_IDENT_FLAG_CONFIG);
+#endif
+
+    read_params_ident(&cfg_idents.idents[i]);
+
+    cfg_idents.n_idents++;
+
+    /* leave ident held. */
+
+    return &cfg_idents.idents[i];
+}
+
+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
+mark_remove_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 (d->removed)
+        return;
+
+    d->removed = TRUE;
+
+    khui_cfg_set_flags_inst(idata, KHUI_CNFLAG_MODIFIED,
+                            KHUI_CNFLAG_MODIFIED);
+
+    EnableWindow(GetDlgItem(hwnd, IDC_CFG_REMOVE), FALSE);
+}
+
+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);
+    }
+}
+
+/* dialog procedure for the "general" pane of individual identity
+   configuration nodes. */
+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;
+
+            case IDC_CFG_REMOVE:
+                mark_remove_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;
+            BOOL * cont;
+
+            khui_cfg_get_dialog_data(hwnd, &idata, NULL);
+
+            switch (HIWORD(wParam)) {
+            case WMCFG_APPLY:
+                cont = (BOOL *) lParam;
+                d = find_ident_by_node(idata->ctx_node);
+                write_params_ident(d);
+                if (d->removed && cont)
+                    *cont = FALSE;
+                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;
+}
+
+/* dialog procedure for individual identity configuration nodes */
+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;
+}
index ac5f930283c04a45085dd1c8c7be0bd77b0c4a4e..91650adb8c17259fb206d5b36f210ccc6286e312 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khmapp.h>\r
-#include<assert.h>\r
-\r
-typedef struct tag_notif_data {\r
-    khui_config_node node;\r
-\r
-    BOOL modified;\r
-\r
-    BOOL monitor;\r
-    BOOL renew;\r
-    BOOL halflife;\r
-    BOOL warn1;\r
-    BOOL warn2;\r
-\r
-    khui_tracker tc_renew;\r
-    khui_tracker tc_warn1;\r
-    khui_tracker tc_warn2;\r
-} notif_data;\r
-\r
-static void \r
-read_params(notif_data * d) {\r
-    khm_handle csp_cw;\r
-    khm_int32 rv;\r
-    khm_int32 t;\r
-\r
-    rv = khc_open_space(NULL, L"CredWindow", KHM_PERM_READ, &csp_cw);\r
-    assert(KHM_SUCCEEDED(rv));\r
-\r
-    rv = khc_read_int32(csp_cw, L"Monitor", &t);\r
-    assert(KHM_SUCCEEDED(rv));\r
-    d->monitor = !!t;\r
-\r
-    rv = khc_read_int32(csp_cw, L"AllowAutoRenew", &t);\r
-    assert(KHM_SUCCEEDED(rv));\r
-    d->renew = !!t;\r
-\r
-    rv = khc_read_int32(csp_cw, L"RenewAtHalfLife", &t);\r
-    assert(KHM_SUCCEEDED(rv));\r
-    d->halflife = !!t;\r
-\r
-    rv = khc_read_int32(csp_cw, L"AllowWarn", &t);\r
-    assert(KHM_SUCCEEDED(rv));\r
-    d->warn1 = !!t;\r
-\r
-    rv = khc_read_int32(csp_cw, L"AllowCritical", &t);\r
-    assert(KHM_SUCCEEDED(rv));\r
-    d->warn2 = !!t;\r
-\r
-    rv = khc_read_int32(csp_cw, L"AutoRenewThreshold", &t);\r
-    assert(KHM_SUCCEEDED(rv));\r
-    d->tc_renew.current = t;\r
-\r
-    rv = khc_read_int32(csp_cw, L"WarnThreshold", &t);\r
-    assert(KHM_SUCCEEDED(rv));\r
-    d->tc_warn1.current = t;\r
-\r
-    rv = khc_read_int32(csp_cw, L"CriticalThreshold", &t);\r
-    assert(KHM_SUCCEEDED(rv));\r
-    d->tc_warn2.current = t;\r
-\r
-    rv = khc_read_int32(csp_cw, L"MaxThreshold", &t);\r
-    assert(KHM_SUCCEEDED(rv));\r
-    d->tc_renew.max = t;\r
-    d->tc_warn1.max = t;\r
-    d->tc_warn2.max = t;\r
-\r
-    rv = khc_read_int32(csp_cw, L"MinThreshold", &t);\r
-    assert(KHM_SUCCEEDED(rv));\r
-    d->tc_renew.min = t;\r
-    d->tc_warn1.min = t;\r
-    d->tc_warn2.min = t;\r
-\r
-    khc_close_space(csp_cw);\r
-\r
-    d->modified = FALSE;\r
-}\r
-\r
-static void\r
-check_for_modification(notif_data * d) {\r
-    notif_data t;\r
-\r
-    ZeroMemory(&t, sizeof(t));\r
-\r
-    read_params(&t);\r
-\r
-    if ((!!d->monitor) != (!!t.monitor) ||\r
-        (!!d->renew) != (!!t.renew) ||\r
-        (!!d->halflife) != (!!t.halflife) ||\r
-        (!!d->warn1) != (!!t.warn1) ||\r
-        (!!d->warn2) != (!!t.warn2) ||\r
-        d->tc_renew.current != t.tc_renew.current ||\r
-        d->tc_warn1.current != t.tc_warn1.current ||\r
-        d->tc_warn2.current != t.tc_warn2.current) {\r
-\r
-        khui_cfg_set_flags(d->node, \r
-                           KHUI_CNFLAG_MODIFIED,\r
-                           KHUI_CNFLAG_MODIFIED);\r
-\r
-        d->modified = TRUE;\r
-\r
-    } else {\r
-        khui_cfg_set_flags(d->node,\r
-                           0,\r
-                           KHUI_CNFLAG_MODIFIED);\r
-\r
-        d->modified = FALSE;\r
-    }\r
-}\r
-\r
-static void\r
-write_params(notif_data * d) {\r
-    khm_handle csp_cw;\r
-    khm_int32 rv;\r
-\r
-    if (!d->modified)\r
-        return;\r
-\r
-    rv = khc_open_space(NULL, L"CredWindow", KHM_PERM_WRITE, &csp_cw);\r
-    assert(KHM_SUCCEEDED(rv));\r
-\r
-    rv = khc_write_int32(csp_cw, L"Monitor", d->monitor);\r
-    assert(KHM_SUCCEEDED(rv));\r
-\r
-    rv = khc_write_int32(csp_cw, L"AllowAutoRenew", d->renew);\r
-    assert(KHM_SUCCEEDED(rv));\r
-\r
-    rv = khc_write_int32(csp_cw, L"RenewAtHalfLife", d->halflife);\r
-    assert(KHM_SUCCEEDED(rv));\r
-\r
-    rv = khc_write_int32(csp_cw, L"AllowWarn", d->warn1);\r
-    assert(KHM_SUCCEEDED(rv));\r
-\r
-    rv = khc_write_int32(csp_cw, L"AllowCritical", d->warn2);\r
-    assert(KHM_SUCCEEDED(rv));\r
-\r
-\r
-    rv = khc_write_int32(csp_cw, L"AutoRenewThreshold", \r
-                         (khm_int32) d->tc_renew.current);\r
-    assert(KHM_SUCCEEDED(rv));\r
-\r
-    rv = khc_write_int32(csp_cw, L"WarnThreshold", \r
-                         (khm_int32) d->tc_warn1.current);\r
-    assert(KHM_SUCCEEDED(rv));\r
-\r
-    rv = khc_write_int32(csp_cw, L"CriticalThreshold", \r
-                         (khm_int32) d->tc_warn2.current);\r
-    assert(KHM_SUCCEEDED(rv));\r
-\r
-    khc_close_space(csp_cw);\r
-\r
-    khui_cfg_set_flags(d->node,\r
-                       KHUI_CNFLAG_APPLIED,\r
-                       KHUI_CNFLAG_APPLIED | KHUI_CNFLAG_MODIFIED);\r
-\r
-    khm_timer_refresh(hwnd_notifier);\r
-}\r
-\r
-static void\r
-refresh_view(HWND hwnd, notif_data * d) {\r
-    CheckDlgButton(hwnd, IDC_NOTIF_MONITOR, \r
-                   (d->monitor?BST_CHECKED:BST_UNCHECKED));\r
-    CheckDlgButton(hwnd, IDC_NOTIF_RENEW, \r
-                   (d->renew?BST_CHECKED:BST_UNCHECKED));\r
-    CheckDlgButton(hwnd, IDC_NOTIF_HALFLIFE,\r
-                   (d->halflife?BST_CHECKED:BST_UNCHECKED));\r
-    CheckDlgButton(hwnd, IDC_NOTIF_WARN1, \r
-                   (d->warn1?BST_CHECKED:BST_UNCHECKED));\r
-    CheckDlgButton(hwnd, IDC_NOTIF_WARN2, \r
-                   (d->warn2?BST_CHECKED:BST_UNCHECKED));\r
-    khui_tracker_refresh(&d->tc_renew);\r
-    khui_tracker_refresh(&d->tc_warn1);\r
-    khui_tracker_refresh(&d->tc_warn2);\r
-    if (!d->monitor) {\r
-        EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_RENEW), FALSE);\r
-        EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_HALFLIFE), FALSE);\r
-        EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_WARN1), FALSE);\r
-        EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_WARN2), FALSE);\r
-        EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_RENEW_THR), FALSE);\r
-        EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_WARN1_THR), FALSE);\r
-        EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_WARN2_THR), FALSE);\r
-    } else {\r
-        EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_RENEW), TRUE);\r
-        EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_HALFLIFE), TRUE);\r
-        EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_WARN1), TRUE);\r
-        EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_WARN2), TRUE);\r
-        EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_RENEW_THR), !!(d->renew));\r
-        EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_WARN1_THR), !!(d->warn1));\r
-        EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_WARN2_THR), !!(d->warn2));\r
-    }\r
-}\r
-\r
-static void\r
-refresh_data(HWND hwnd, notif_data * d) {\r
-    d->monitor = (IsDlgButtonChecked(hwnd, IDC_NOTIF_MONITOR)\r
-                  == BST_CHECKED);\r
-    d->renew   = (IsDlgButtonChecked(hwnd, IDC_NOTIF_RENEW)\r
-                  == BST_CHECKED);\r
-    d->halflife = (IsDlgButtonChecked(hwnd, IDC_NOTIF_HALFLIFE)\r
-                   == BST_CHECKED);\r
-    d->warn1   = (IsDlgButtonChecked(hwnd, IDC_NOTIF_WARN1)\r
-                  == BST_CHECKED);\r
-    d->warn2   = (IsDlgButtonChecked(hwnd, IDC_NOTIF_WARN2)\r
-                  == BST_CHECKED);\r
-\r
-    check_for_modification(d);\r
-}\r
-\r
-INT_PTR CALLBACK\r
-khm_cfg_notifications_proc(HWND hwnd,\r
-                           UINT uMsg,\r
-                           WPARAM wParam,\r
-                           LPARAM lParam) {\r
-\r
-    notif_data * d;\r
-\r
-    switch(uMsg) {\r
-    case WM_INITDIALOG: {\r
-        HWND hw;\r
-\r
-        d = PMALLOC(sizeof(*d));\r
-#ifdef DEBUG\r
-        assert(d != NULL);\r
-#endif\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-        SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);\r
-#pragma warning(pop)\r
-\r
-        ZeroMemory(d, sizeof(*d));\r
-\r
-        d->node = (khui_config_node) lParam;\r
-\r
-        khui_tracker_initialize(&d->tc_renew);\r
-        khui_tracker_initialize(&d->tc_warn1);\r
-        khui_tracker_initialize(&d->tc_warn2);\r
-\r
-        read_params(d);\r
-\r
-        hw = GetDlgItem(hwnd, IDC_NOTIF_RENEW_THR);\r
-        khui_tracker_install(hw, &d->tc_renew);\r
-\r
-        hw = GetDlgItem(hwnd, IDC_NOTIF_WARN1_THR);\r
-        khui_tracker_install(hw, &d->tc_warn1);\r
-\r
-        hw = GetDlgItem(hwnd, IDC_NOTIF_WARN2_THR);\r
-        khui_tracker_install(hw, &d->tc_warn2);\r
-\r
-        refresh_view(hwnd, d);\r
-\r
-        /* normally we should return TRUE, but in this case we return\r
-           FALSE since we don't want to inadvertently steal the focus\r
-           from the treeview. */\r
-        return FALSE;\r
-    }\r
-\r
-    case WM_COMMAND: {\r
-        d = (notif_data *) (DWORD_PTR) GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        if (HIWORD(wParam) == BN_CLICKED) {\r
-            refresh_data(hwnd, d);\r
-            refresh_view(hwnd, d);\r
-\r
-            check_for_modification(d);\r
-        } else if (HIWORD(wParam) == EN_CHANGE) {\r
-            SetTimer(hwnd, 1, 500, NULL);\r
-        }\r
-\r
-        khm_set_dialog_result(hwnd, 0);\r
-\r
-        return TRUE;\r
-    }\r
-\r
-    case WM_TIMER: {\r
-        d = (notif_data *) (DWORD_PTR) GetWindowLongPtr(hwnd, DWLP_USER);\r
-        KillTimer(hwnd, 1);\r
-        check_for_modification(d);\r
-\r
-        khm_set_dialog_result(hwnd, 0);\r
-\r
-        return TRUE;\r
-    }\r
-\r
-    case WM_DESTROY: {\r
-        d = (notif_data *) (DWORD_PTR) GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        khui_tracker_kill_controls(&d->tc_renew);\r
-        khui_tracker_kill_controls(&d->tc_warn1);\r
-        khui_tracker_kill_controls(&d->tc_warn2);\r
-\r
-        PFREE(d);\r
-\r
-        SetWindowLongPtr(hwnd, DWLP_USER, 0);\r
-\r
-        khm_set_dialog_result(hwnd, 0);\r
-\r
-        return TRUE;\r
-    }\r
-\r
-    case KHUI_WM_CFG_NOTIFY: {\r
-        d = (notif_data *) (DWORD_PTR) GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        if (HIWORD(wParam) == WMCFG_APPLY) {\r
-            write_params(d);\r
-        }\r
-\r
-        khm_set_dialog_result(hwnd, 0);\r
-\r
-        return TRUE;\r
-    }\r
-\r
-    }\r
-\r
-    return FALSE;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 modified;
+
+    BOOL monitor;
+    BOOL renew;
+    BOOL halflife;
+    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"RenewAtHalfLife", &t);
+    assert(KHM_SUCCEEDED(rv));
+    d->halflife = !!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);
+
+    d->modified = FALSE;
+}
+
+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->halflife) != (!!t.halflife) ||
+        (!!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);
+
+        d->modified = TRUE;
+
+    } else {
+        khui_cfg_set_flags(d->node,
+                           0,
+                           KHUI_CNFLAG_MODIFIED);
+
+        d->modified = FALSE;
+    }
+}
+
+static void
+write_params(notif_data * d) {
+    khm_handle csp_cw;
+    khm_int32 rv;
+
+    if (!d->modified)
+        return;
+
+    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"RenewAtHalfLife", d->halflife);
+    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);
+
+    khm_timer_refresh(hwnd_notifier);
+}
+
+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_HALFLIFE,
+                   (d->halflife?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_HALFLIFE), 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_HALFLIFE), 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->halflife = (IsDlgButtonChecked(hwnd, IDC_NOTIF_HALFLIFE)
+                   == 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 = PMALLOC(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);
+
+        PFREE(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;
+}
index cab71c4ea281c4d9c59e85c6ddd5364f7d907b97..6290d370328e937ffb769b77d278b32772f296e1 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khmapp.h>\r
-#include<assert.h>\r
-\r
-#define MAX_PLUGINS 256\r
-\r
-typedef struct tag_plugin_data {\r
-    kmm_plugin_info plugin;\r
-    kmm_module_info module;\r
-} plugin_data;\r
-\r
-typedef struct tag_plugin_dlg_data {\r
-    plugin_data * info[MAX_PLUGINS];\r
-    khm_size n_info;\r
-\r
-    plugin_data * selected;\r
-    HICON plugin_ico;\r
-} plugin_dlg_data;\r
-\r
-void update_dialog_fields(HWND hwnd,\r
-                          plugin_dlg_data * d,\r
-                          plugin_data * info) {\r
-    wchar_t buf[256];\r
-    UINT resid;\r
-    wchar_t * t;\r
-    khm_handle csp_module = NULL;\r
-\r
-    d->selected = info;\r
-\r
-    if (info->plugin.reg.description)\r
-        SetDlgItemText(hwnd, IDC_CFG_DESC, info->plugin.reg.description);\r
-    else {\r
-        wchar_t fmt[128];\r
-\r
-        LoadString(khm_hInstance, IDS_CFG_NODESC, fmt, ARRAYLENGTH(fmt));\r
-        StringCbPrintf(buf, sizeof(buf), fmt, info->plugin.reg.name);\r
-        SetDlgItemText(hwnd, IDC_CFG_DESC, buf);\r
-    }\r
-    \r
-    switch(info->plugin.state) {\r
-    case KMM_PLUGIN_STATE_FAIL_INIT:\r
-        resid = IDS_PISTATE_FAILINIT;\r
-        break;\r
-\r
-    case KMM_PLUGIN_STATE_FAIL_UNKNOWN:\r
-        resid = IDS_PISTATE_FAILUNK;\r
-        break;\r
-\r
-    case KMM_PLUGIN_STATE_FAIL_MAX_FAILURE:\r
-        resid = IDS_PISTATE_FAILMAX;\r
-        break;\r
-\r
-    case KMM_PLUGIN_STATE_FAIL_NOT_REGISTERED:\r
-        resid = IDS_PISTATE_FAILREG;\r
-        break;\r
-\r
-    case KMM_PLUGIN_STATE_FAIL_DISABLED:\r
-        resid = IDS_PISTATE_FAILDIS;\r
-        break;\r
-\r
-    case KMM_PLUGIN_STATE_FAIL_LOAD:\r
-        resid = IDS_PISTATE_FAILLOD;\r
-        break;\r
-\r
-    case KMM_PLUGIN_STATE_NONE:\r
-    case KMM_PLUGIN_STATE_PLACEHOLDER:\r
-        resid = IDS_PISTATE_PLACEHOLD;\r
-        break;\r
-\r
-    case KMM_PLUGIN_STATE_REG:\r
-    case KMM_PLUGIN_STATE_PREINIT:\r
-        resid = IDS_PISTATE_REG;\r
-        break;\r
-\r
-    case KMM_PLUGIN_STATE_HOLD:\r
-        resid = IDS_PISTATE_HOLD;\r
-        break;\r
-\r
-    case KMM_PLUGIN_STATE_INIT:\r
-        resid = IDS_PISTATE_INIT;\r
-        break;\r
-\r
-    case KMM_PLUGIN_STATE_RUNNING:\r
-        resid = IDS_PISTATE_RUN;\r
-        break;\r
-\r
-    case KMM_PLUGIN_STATE_EXITED:\r
-        resid = IDS_PISTATE_EXIT;\r
-        break;\r
-\r
-    default:\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-        resid = IDS_PISTATE_FAILUNK;\r
-    }\r
-\r
-    LoadString(khm_hInstance, resid,\r
-               buf, ARRAYLENGTH(buf));\r
-\r
-    SetDlgItemText(hwnd, IDC_CFG_STATE, buf);\r
-\r
-    SendDlgItemMessage(hwnd, IDC_CFG_DEPS,\r
-                       LB_RESETCONTENT, 0, 0);\r
-\r
-    for (t = info->plugin.reg.dependencies; t && *t;\r
-         t = multi_string_next(t)) {\r
-        SendDlgItemMessage(hwnd, IDC_CFG_DEPS,\r
-                           LB_INSERTSTRING, -1, (LPARAM) t);\r
-    }\r
-\r
-    if (info->plugin.reg.module)\r
-        SetDlgItemText(hwnd, IDC_CFG_MODULE,\r
-                       info->plugin.reg.module);\r
-    else\r
-        SetDlgItemText(hwnd, IDC_CFG_MODULE,\r
-                       L"");\r
-\r
-    if (info->module.reg.vendor)\r
-        SetDlgItemText(hwnd, IDC_CFG_VENDOR,\r
-                       info->module.reg.vendor);\r
-    else\r
-        SetDlgItemText(hwnd, IDC_CFG_VENDOR,\r
-                       L"");\r
-\r
-    StringCbPrintf(buf, sizeof(buf), L"%u.%u.%u.%u",\r
-                   (unsigned int) info->module.product_version.major,\r
-                   (unsigned int) info->module.product_version.minor,\r
-                   (unsigned int) info->module.product_version.patch,\r
-                   (unsigned int) info->module.product_version.aux);\r
-\r
-    SetDlgItemText(hwnd, IDC_CFG_VERSION, buf);\r
-\r
-    if (info->plugin.reg.icon) {\r
-        SendDlgItemMessage(hwnd, IDC_CFG_ICON,\r
-                           STM_SETICON,\r
-                           (WPARAM) info->plugin.reg.icon,\r
-                           0);\r
-    } else {\r
-        SendDlgItemMessage(hwnd, IDC_CFG_ICON,\r
-                           STM_SETICON,\r
-                           (WPARAM) d->plugin_ico,\r
-                           0);\r
-    }\r
-\r
-    if (KHM_SUCCEEDED(kmm_get_module_config(info->module.reg.name,\r
-                                            0, &csp_module)) &&\r
-        (khc_value_exists(csp_module, L"ImagePath") &\r
-         (KCONF_FLAG_MACHINE | KCONF_FLAG_USER))) {\r
-\r
-        EnableWindow(GetDlgItem(hwnd, IDC_CFG_UNREGISTER), TRUE);\r
-    } else {\r
-        EnableWindow(GetDlgItem(hwnd, IDC_CFG_UNREGISTER), FALSE);\r
-    }\r
-\r
-    if (csp_module)\r
-        khc_close_space(csp_module);\r
-\r
-    if (info->plugin.flags & KMM_PLUGIN_FLAG_DISABLED) {\r
-        EnableWindow(GetDlgItem(hwnd, IDC_CFG_ENABLE), TRUE);\r
-        EnableWindow(GetDlgItem(hwnd, IDC_CFG_DISABLE), FALSE);\r
-    } else {\r
-        EnableWindow(GetDlgItem(hwnd, IDC_CFG_ENABLE), FALSE);\r
-        EnableWindow(GetDlgItem(hwnd, IDC_CFG_DISABLE), TRUE);\r
-    }\r
-}\r
-\r
-#define IDX_PLUGIN_NORMAL   1\r
-#define IDX_PLUGIN_DISABLED 2\r
-#define IDX_PLUGIN_ERROR    3\r
-\r
-INT_PTR CALLBACK\r
-khm_cfg_plugins_proc(HWND hwnd,\r
-                     UINT uMsg,\r
-                     WPARAM wParam,\r
-                     LPARAM lParam) {\r
-\r
-    plugin_dlg_data * d;\r
-\r
-    switch(uMsg) {\r
-    case WM_INITDIALOG:\r
-        {\r
-            kmm_plugin p;\r
-            kmm_plugin pn;\r
-            kmm_module m;\r
-            khm_size i;\r
-            LVCOLUMN lvc;\r
-            RECT r;\r
-            HWND hw;\r
-            wchar_t buf[256];\r
-            HIMAGELIST h_ilist;\r
-            HICON h_icon;\r
-\r
-            d = PMALLOC(sizeof(*d));\r
-#ifdef DEBUG\r
-            assert(d);\r
-#endif\r
-            ZeroMemory(d, sizeof(*d));\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-            SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);\r
-#pragma warning(pop)\r
-\r
-            p = NULL;\r
-            i = 0;\r
-            do {\r
-                if (KHM_FAILED(kmm_get_next_plugin(p, &pn)))\r
-                    break;\r
-\r
-                if (p)\r
-                    kmm_release_plugin(p);\r
-                p = pn;\r
-\r
-#ifdef DEBUG\r
-                assert(d->info[i] == NULL);\r
-#endif\r
-                d->info[i] = PMALLOC(sizeof(*(d->info[i])));\r
-#ifdef DEBUG\r
-                assert(d->info[i]);\r
-#endif\r
-                ZeroMemory(&d->info[i]->plugin,\r
-                           sizeof(d->info[i]->plugin));\r
-\r
-                if (KHM_FAILED(kmm_get_plugin_info_i(p, &d->info[i]->plugin))) {\r
-                    PFREE(d->info[i]);\r
-                    d->info[i] = NULL;\r
-                    break;\r
-                }\r
-\r
-                ZeroMemory(&d->info[i]->module,\r
-                           sizeof(d->info[i]->module));\r
-\r
-                if (KHM_SUCCEEDED(kmm_load_module(d->info[i]->plugin.reg.module,\r
-                                                  KMM_LM_FLAG_NOLOAD,\r
-                                                  &m))) {\r
-                    kmm_get_module_info_i(m, &d->info[i]->module);\r
-                    kmm_release_module(m);\r
-                }\r
-\r
-                i ++;\r
-\r
-                if (i == MAX_PLUGINS)\r
-                    break;\r
-            } while(p);\r
-\r
-            if (p)\r
-                kmm_release_plugin(p);\r
-\r
-            d->n_info = i;\r
-\r
-            /* now populate the list view */\r
-            hw = GetDlgItem(hwnd, IDC_CFG_PLUGINS);\r
-#ifdef DEBUG\r
-            assert(hw);\r
-#endif\r
-\r
-            h_ilist = ImageList_Create(GetSystemMetrics(SM_CXSMICON),\r
-                                       GetSystemMetrics(SM_CYSMICON),\r
-                                       ILC_COLOR8,\r
-                                       4, 4);\r
-\r
-            h_icon = LoadImage(khm_hInstance,\r
-                               MAKEINTRESOURCE(IDI_CFG_PLUGIN),\r
-                               IMAGE_ICON,\r
-                               GetSystemMetrics(SM_CXSMICON),\r
-                               GetSystemMetrics(SM_CYSMICON),\r
-                               LR_DEFAULTCOLOR);\r
-#ifdef DEBUG\r
-            assert(h_icon);\r
-#endif\r
-            ImageList_AddIcon(h_ilist, h_icon);\r
-            DestroyIcon(h_icon);\r
-\r
-            h_icon = LoadImage(khm_hInstance,\r
-                               MAKEINTRESOURCE(IDI_CFG_PLUGIN_DIS),\r
-                               IMAGE_ICON,\r
-                               GetSystemMetrics(SM_CXSMICON),\r
-                               GetSystemMetrics(SM_CYSMICON),\r
-                               LR_DEFAULTCOLOR);\r
-#ifdef DEBUG\r
-            assert(h_icon);\r
-#endif\r
-            ImageList_AddIcon(h_ilist, h_icon);\r
-            DestroyIcon(h_icon);\r
-\r
-            h_icon = LoadImage(khm_hInstance,\r
-                               MAKEINTRESOURCE(IDI_CFG_PLUGIN_ERR),\r
-                               IMAGE_ICON,\r
-                               GetSystemMetrics(SM_CXSMICON),\r
-                               GetSystemMetrics(SM_CYSMICON),\r
-                               LR_DEFAULTCOLOR);\r
-#ifdef DEBUG\r
-            assert(h_icon);\r
-#endif\r
-            ImageList_AddIcon(h_ilist, h_icon);\r
-            DestroyIcon(h_icon);\r
-\r
-            ListView_SetImageList(hw, h_ilist, LVSIL_STATE);\r
-\r
-            ZeroMemory(&lvc, sizeof(lvc));\r
-\r
-            lvc.mask = LVCF_TEXT | LVCF_WIDTH;\r
-            GetWindowRect(hw, &r);\r
-            lvc.cx = ((r.right - r.left) * 95) / 100;\r
-            lvc.pszText = buf;\r
-\r
-            LoadString(khm_hInstance, IDS_CFG_PI_COL_PLUGINS,\r
-                       buf, ARRAYLENGTH(buf));\r
-\r
-            ListView_InsertColumn(hw, 0, &lvc);\r
-\r
-            for(i=0; i<d->n_info; i++) {\r
-                LVITEM lvi;\r
-\r
-                ZeroMemory(&lvi, sizeof(lvi));\r
-\r
-                lvi.mask = LVIF_PARAM | LVIF_TEXT | LVIF_STATE;\r
-                lvi.lParam = (LPARAM) d->info[i];\r
-                lvi.pszText = d->info[i]->plugin.reg.name;\r
-\r
-                if (d->info[i]->plugin.flags & KMM_PLUGIN_FLAG_DISABLED) {\r
-                    lvi.state = INDEXTOSTATEIMAGEMASK(IDX_PLUGIN_DISABLED);\r
-                } else if (d->info[i]->plugin.state < 0) {\r
-                    lvi.state = INDEXTOSTATEIMAGEMASK(IDX_PLUGIN_ERROR);\r
-                } else {\r
-                    lvi.state = INDEXTOSTATEIMAGEMASK(IDX_PLUGIN_NORMAL);\r
-                }\r
-\r
-                ListView_InsertItem(hw, &lvi);\r
-            }\r
-\r
-            d->plugin_ico =\r
-                (HICON) LoadImage(khm_hInstance,\r
-                                  MAKEINTRESOURCE(IDI_CFG_PLUGIN),\r
-                                  IMAGE_ICON,\r
-                                  GetSystemMetrics(SM_CXICON),\r
-                                  GetSystemMetrics(SM_CYICON),\r
-                                  LR_DEFAULTCOLOR);\r
-        }\r
-        return FALSE;\r
-\r
-    case WM_NOTIFY:\r
-        {\r
-            LPNMHDR lpnm;\r
-            HWND hw;\r
-\r
-            d = (plugin_dlg_data *) (LONG_PTR) \r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-            if (wParam == IDC_CFG_PLUGINS &&\r
-                (lpnm = (LPNMHDR) lParam) &&\r
-                lpnm->code == LVN_ITEMCHANGED) {\r
-\r
-                LVITEM lvi;\r
-\r
-                hw = GetDlgItem(hwnd, IDC_CFG_PLUGINS);\r
-#ifdef DEBUG\r
-                assert(hw);\r
-#endif\r
-                if (ListView_GetSelectedCount(hw) != 1) {\r
-                    SetDlgItemText(hwnd, IDC_CFG_DESC, L"");\r
-                    SetDlgItemText(hwnd, IDC_CFG_STATE, L"");\r
-                    SetDlgItemText(hwnd, IDC_CFG_MODULE, L"");\r
-                    SetDlgItemText(hwnd, IDC_CFG_VENDOR, L"");\r
-                    SetDlgItemText(hwnd, IDC_CFG_VERSION, L"");\r
-                    EnableWindow(GetDlgItem(hwnd, IDC_CFG_ENABLE), FALSE);\r
-                    EnableWindow(GetDlgItem(hwnd, IDC_CFG_DISABLE), FALSE);\r
-                    EnableWindow(GetDlgItem(hwnd, IDC_CFG_UNREGISTER), FALSE);\r
-                    SendDlgItemMessage(hwnd, IDC_CFG_DEPS, \r
-                                       LB_RESETCONTENT, 0, 0);\r
-                    SendDlgItemMessage(hwnd, IDC_CFG_ICON, STM_SETICON,\r
-                                       (WPARAM) d->plugin_ico, 0);\r
-                    d->selected = NULL;\r
-                } else {\r
-                    int idx;\r
-                    plugin_data * info;\r
-\r
-                    idx = ListView_GetNextItem(hw, -1, LVNI_SELECTED);\r
-#ifdef DEBUG\r
-                    assert(idx != -1);\r
-#endif\r
-                    ZeroMemory(&lvi, sizeof(lvi));\r
-                    lvi.iItem = idx;\r
-                    lvi.iSubItem = 0;\r
-                    lvi.mask = LVIF_PARAM;\r
-\r
-                    ListView_GetItem(hw, &lvi);\r
-#ifdef DEBUG\r
-                    assert(lvi.lParam != 0);\r
-#endif\r
-                    info = (plugin_data *) lvi.lParam;\r
-\r
-                    update_dialog_fields(hwnd, d, info);\r
-                }\r
-            }\r
-        }\r
-        return TRUE;\r
-\r
-    case WM_COMMAND:\r
-        {\r
-\r
-            d = (plugin_dlg_data *) (LONG_PTR)\r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-            switch (wParam) {\r
-            case MAKEWPARAM(IDC_CFG_ENABLE, BN_CLICKED):\r
-                if (d->selected != NULL) {\r
-                    khui_alert * alert = NULL;\r
-                    wchar_t buf[KHUI_MAXCCH_MESSAGE];\r
-                    wchar_t fmt[KHUI_MAXCCH_MESSAGE];\r
-                    kmm_plugin p;\r
-\r
-                    khui_alert_create_empty(&alert);\r
-\r
-                    LoadString(khm_hInstance, IDS_CFG_P_ENBCNFT,\r
-                               fmt, ARRAYLENGTH(fmt));\r
-                    StringCbPrintf(buf, sizeof(buf), fmt, d->selected->plugin.reg.name);\r
-                    khui_alert_set_title(alert, buf);\r
-\r
-                    LoadString(khm_hInstance, IDS_CFG_P_ENBCNFM,\r
-                               fmt, ARRAYLENGTH(fmt));\r
-                    StringCbPrintf(buf, sizeof(buf), fmt, d->selected->plugin.reg.name);\r
-                    khui_alert_set_message(alert, buf);\r
-\r
-                    khui_alert_set_severity(alert, KHERR_INFO);\r
-\r
-                    khui_alert_show_modal(alert);\r
-\r
-                    kmm_enable_plugin(d->selected->plugin.h_plugin, TRUE);\r
-\r
-                    khui_alert_release(alert);\r
-\r
-                    p = d->selected->plugin.h_plugin;\r
-                    kmm_hold_plugin(p);\r
-                    kmm_release_plugin_info_i(&d->selected->plugin);\r
-                    kmm_get_plugin_info_i(p, &d->selected->plugin);\r
-                    kmm_release_plugin(p);\r
-\r
-                    update_dialog_fields(hwnd, d, d->selected);\r
-                }\r
-                break;\r
-\r
-            case MAKEWPARAM(IDC_CFG_DISABLE, BN_CLICKED):\r
-                if (d->selected != NULL) {\r
-                    khui_alert * alert = NULL;\r
-                    wchar_t buf[KHUI_MAXCCH_MESSAGE];\r
-                    wchar_t fmt[KHUI_MAXCCH_MESSAGE];\r
-                    wchar_t depends[KHUI_MAXCCH_MESSAGE];\r
-                    khm_size i;\r
-                    kmm_plugin p;\r
-\r
-                    khui_alert_create_empty(&alert);\r
-#ifdef DEBUG\r
-                    assert(alert);\r
-#endif\r
-                    if (alert == NULL)\r
-                        break;\r
-\r
-                    LoadString(khm_hInstance, IDS_CFG_P_DELCNFT,\r
-                               fmt, ARRAYLENGTH(fmt));\r
-                    StringCbPrintf(buf, sizeof(buf), fmt, d->selected->plugin.reg.name);\r
-                    khui_alert_set_title(alert, buf);\r
-\r
-                    LoadString(khm_hInstance, IDS_CFG_P_DELCNFM,\r
-                               fmt, ARRAYLENGTH(fmt));\r
-                    StringCbPrintf(buf, sizeof(buf), fmt, d->selected->plugin.reg.name);\r
-                    khui_alert_set_message(alert, buf);\r
-\r
-                    depends[0] = L'\0';\r
-\r
-                    for (i=0; i<d->n_info; i++) {\r
-                        wchar_t * t;\r
-\r
-                        t = d->info[i]->plugin.reg.dependencies;\r
-\r
-                        while(t) {\r
-                            if (!wcscmp(t, d->selected->plugin.reg.name)) {\r
-                                if (depends[0])\r
-                                    StringCbCat(depends, sizeof(depends), L", ");\r
-                                StringCbCat(depends, sizeof(depends),\r
-                                            d->info[i]->plugin.reg.name);\r
-                                break;\r
-                            }\r
-                            t = multi_string_next(t);\r
-                        }\r
-                    }\r
-\r
-                    if (depends[0]) {\r
-                        LoadString(khm_hInstance, IDS_CFG_P_DELCNFS,\r
-                                   fmt, ARRAYLENGTH(fmt));\r
-                        StringCbPrintf(buf, sizeof(buf), fmt, depends);\r
-                        khui_alert_set_suggestion(alert, buf);\r
-                    } else {\r
-                        LoadString(khm_hInstance, IDS_CFG_P_DELNDEP,\r
-                                   buf, ARRAYLENGTH(buf));\r
-                        khui_alert_set_suggestion(alert, buf);\r
-                    }\r
-\r
-                    khui_alert_add_command(alert, KHUI_PACTION_YES);\r
-                    khui_alert_add_command(alert, KHUI_PACTION_NO);\r
-\r
-                    khui_alert_set_severity(alert, KHERR_WARNING);\r
-\r
-                    if (KHM_SUCCEEDED(khui_alert_show_modal(alert)) &&\r
-                        alert->response == KHUI_PACTION_YES) {\r
-                        kmm_enable_plugin(d->selected->plugin.h_plugin, FALSE);\r
-                    }\r
-\r
-                    khui_alert_release(alert);\r
-\r
-                    p = d->selected->plugin.h_plugin;\r
-                    kmm_hold_plugin(p);\r
-                    kmm_release_plugin_info_i(&d->selected->plugin);\r
-                    kmm_get_plugin_info_i(p, &d->selected->plugin);\r
-                    kmm_release_plugin(p);\r
-\r
-                    update_dialog_fields(hwnd, d, d->selected);\r
-                }\r
-                break;\r
-\r
-            case MAKEWPARAM(IDC_CFG_UNREGISTER, BN_CLICKED):\r
-                {\r
-                    khui_alert * alert = NULL;\r
-                    wchar_t buf[KHUI_MAXCCH_MESSAGE];\r
-                    wchar_t fmt[KHUI_MAXCCH_MESSAGE];\r
-                    wchar_t plist[KHUI_MAXCCH_MESSAGE];\r
-                    khm_size i;\r
-\r
-                    if (d->selected == NULL) {\r
-#ifdef DEBUG\r
-                        assert(FALSE);\r
-#endif\r
-                        break;\r
-                    }\r
-\r
-                    khui_alert_create_empty(&alert);\r
-\r
-                    LoadString(khm_hInstance, IDS_CFG_P_UNRCNFT,\r
-                               fmt, ARRAYLENGTH(fmt));\r
-                    StringCbPrintf(buf, sizeof(buf), fmt,\r
-                                   d->selected->plugin.reg.name);\r
-\r
-                    khui_alert_set_title(alert, buf);\r
-\r
-                    LoadString(khm_hInstance, IDS_CFG_P_UNRCNFM,\r
-                               fmt, ARRAYLENGTH(fmt));\r
-                    StringCbPrintf(buf, sizeof(buf), fmt,\r
-                                   d->selected->plugin.reg.name);\r
-\r
-                    khui_alert_set_message(alert, buf);\r
-\r
-                    plist[0] = L'\0';\r
-                    for (i=0; i < d->n_info; i++) {\r
-                        if (!wcscmp(d->info[i]->module.reg.name,\r
-                                    d->selected->module.reg.name)) {\r
-                            if (plist[0])\r
-                                StringCbCat(plist, sizeof(plist), L", ");\r
-                            StringCbCat(plist, sizeof(plist),\r
-                                        d->info[i]->plugin.reg.name);\r
-                        }\r
-                    }\r
-\r
-#ifdef DEBUG\r
-                    /* there should have been at least one plugin */\r
-                    assert(plist[0]);\r
-#endif\r
-\r
-                    LoadString(khm_hInstance, IDS_CFG_P_UNRCNFS,\r
-                               fmt, ARRAYLENGTH(fmt));\r
-                    StringCbPrintf(buf, sizeof(buf), fmt, plist);\r
-                    khui_alert_set_suggestion(alert, buf);\r
-\r
-                    khui_alert_add_command(alert, KHUI_PACTION_YES);\r
-                    khui_alert_add_command(alert, KHUI_PACTION_NO);\r
-\r
-                    khui_alert_set_severity(alert, KHERR_WARNING);\r
-\r
-                    if (KHM_SUCCEEDED(khui_alert_show_modal(alert)) &&\r
-                        alert->response == KHUI_PACTION_YES) {\r
-                        kmm_unregister_module(d->selected->module.reg.name, 0);\r
-\r
-                        update_dialog_fields(hwnd, d, d->selected);\r
-                    }\r
-                }\r
-                break;\r
-\r
-            case MAKEWPARAM(IDC_CFG_REGISTER, BN_CLICKED):\r
-                {\r
-                    \r
-                }\r
-                break;\r
-            }\r
-        }\r
-        return TRUE;\r
-\r
-    case WM_DESTROY:\r
-        {\r
-            khm_size i;\r
-\r
-            d = (plugin_dlg_data *) (LONG_PTR) \r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-#ifdef DEBUG\r
-            assert(d);\r
-#endif\r
-            for (i=0; i<d->n_info; i++) {\r
-#ifdef DEBUG\r
-                assert(d->info[i]);\r
-#endif\r
-                kmm_release_plugin_info_i(&d->info[i]->plugin);\r
-                kmm_release_module_info_i(&d->info[i]->module);\r
-                PFREE(d->info[i]);\r
-            }\r
-\r
-            PFREE(d);\r
-\r
-            khm_set_dialog_result(hwnd, 0);\r
-        }\r
-        return TRUE;\r
-    }\r
-    return FALSE;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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_data * selected;
+    HICON plugin_ico;
+} plugin_dlg_data;
+
+void update_dialog_fields(HWND hwnd,
+                          plugin_dlg_data * d,
+                          plugin_data * info) {
+    wchar_t buf[256];
+    UINT resid;
+    wchar_t * t;
+    khm_handle csp_module = NULL;
+
+    d->selected = info;
+
+    if (info->plugin.reg.description)
+        SetDlgItemText(hwnd, IDC_CFG_DESC, info->plugin.reg.description);
+    else {
+        wchar_t fmt[128];
+
+        LoadString(khm_hInstance, IDS_CFG_NODESC, fmt, ARRAYLENGTH(fmt));
+        StringCbPrintf(buf, sizeof(buf), fmt, info->plugin.reg.name);
+        SetDlgItemText(hwnd, IDC_CFG_DESC, buf);
+    }
+    
+    switch(info->plugin.state) {
+    case KMM_PLUGIN_STATE_FAIL_INIT:
+        resid = IDS_PISTATE_FAILINIT;
+        break;
+
+    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"");
+
+    StringCbPrintf(buf, sizeof(buf), L"%u.%u.%u.%u",
+                   (unsigned int) info->module.product_version.major,
+                   (unsigned int) info->module.product_version.minor,
+                   (unsigned int) info->module.product_version.patch,
+                   (unsigned int) info->module.product_version.aux);
+
+    SetDlgItemText(hwnd, IDC_CFG_VERSION, buf);
+
+    if (info->plugin.reg.icon) {
+        SendDlgItemMessage(hwnd, IDC_CFG_ICON,
+                           STM_SETICON,
+                           (WPARAM) info->plugin.reg.icon,
+                           0);
+    } else {
+        SendDlgItemMessage(hwnd, IDC_CFG_ICON,
+                           STM_SETICON,
+                           (WPARAM) d->plugin_ico,
+                           0);
+    }
+
+    if (KHM_SUCCEEDED(kmm_get_module_config(info->module.reg.name,
+                                            0, &csp_module)) &&
+        (khc_value_exists(csp_module, L"ImagePath") &
+         (KCONF_FLAG_MACHINE | KCONF_FLAG_USER))) {
+
+        EnableWindow(GetDlgItem(hwnd, IDC_CFG_UNREGISTER), TRUE);
+    } else {
+        EnableWindow(GetDlgItem(hwnd, IDC_CFG_UNREGISTER), FALSE);
+    }
+
+    if (csp_module)
+        khc_close_space(csp_module);
+
+    if (info->plugin.flags & KMM_PLUGIN_FLAG_DISABLED) {
+        EnableWindow(GetDlgItem(hwnd, IDC_CFG_ENABLE), TRUE);
+        EnableWindow(GetDlgItem(hwnd, IDC_CFG_DISABLE), FALSE);
+    } else {
+        EnableWindow(GetDlgItem(hwnd, IDC_CFG_ENABLE), FALSE);
+        EnableWindow(GetDlgItem(hwnd, IDC_CFG_DISABLE), TRUE);
+    }
+}
+
+#define IDX_PLUGIN_NORMAL   1
+#define IDX_PLUGIN_DISABLED 2
+#define IDX_PLUGIN_ERROR    3
+
+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];
+            HIMAGELIST h_ilist;
+            HICON h_icon;
+
+            d = PMALLOC(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] = PMALLOC(sizeof(*(d->info[i])));
+#ifdef DEBUG
+                assert(d->info[i]);
+#endif
+                ZeroMemory(&d->info[i]->plugin,
+                           sizeof(d->info[i]->plugin));
+
+                if (KHM_FAILED(kmm_get_plugin_info_i(p, &d->info[i]->plugin))) {
+                    PFREE(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
+
+            h_ilist = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
+                                       GetSystemMetrics(SM_CYSMICON),
+                                       ILC_COLOR8,
+                                       4, 4);
+
+            h_icon = LoadImage(khm_hInstance,
+                               MAKEINTRESOURCE(IDI_CFG_PLUGIN),
+                               IMAGE_ICON,
+                               GetSystemMetrics(SM_CXSMICON),
+                               GetSystemMetrics(SM_CYSMICON),
+                               LR_DEFAULTCOLOR);
+#ifdef DEBUG
+            assert(h_icon);
+#endif
+            ImageList_AddIcon(h_ilist, h_icon);
+            DestroyIcon(h_icon);
+
+            h_icon = LoadImage(khm_hInstance,
+                               MAKEINTRESOURCE(IDI_CFG_PLUGIN_DIS),
+                               IMAGE_ICON,
+                               GetSystemMetrics(SM_CXSMICON),
+                               GetSystemMetrics(SM_CYSMICON),
+                               LR_DEFAULTCOLOR);
+#ifdef DEBUG
+            assert(h_icon);
+#endif
+            ImageList_AddIcon(h_ilist, h_icon);
+            DestroyIcon(h_icon);
+
+            h_icon = LoadImage(khm_hInstance,
+                               MAKEINTRESOURCE(IDI_CFG_PLUGIN_ERR),
+                               IMAGE_ICON,
+                               GetSystemMetrics(SM_CXSMICON),
+                               GetSystemMetrics(SM_CYSMICON),
+                               LR_DEFAULTCOLOR);
+#ifdef DEBUG
+            assert(h_icon);
+#endif
+            ImageList_AddIcon(h_ilist, h_icon);
+            DestroyIcon(h_icon);
+
+            ListView_SetImageList(hw, h_ilist, LVSIL_STATE);
+
+            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 | LVIF_STATE;
+                lvi.lParam = (LPARAM) d->info[i];
+                lvi.pszText = d->info[i]->plugin.reg.name;
+
+                if (d->info[i]->plugin.flags & KMM_PLUGIN_FLAG_DISABLED) {
+                    lvi.state = INDEXTOSTATEIMAGEMASK(IDX_PLUGIN_DISABLED);
+                } else if (d->info[i]->plugin.state < 0) {
+                    lvi.state = INDEXTOSTATEIMAGEMASK(IDX_PLUGIN_ERROR);
+                } else {
+                    lvi.state = INDEXTOSTATEIMAGEMASK(IDX_PLUGIN_NORMAL);
+                }
+
+                ListView_InsertItem(hw, &lvi);
+            }
+
+            d->plugin_ico =
+                (HICON) LoadImage(khm_hInstance,
+                                  MAKEINTRESOURCE(IDI_CFG_PLUGIN),
+                                  IMAGE_ICON,
+                                  GetSystemMetrics(SM_CXICON),
+                                  GetSystemMetrics(SM_CYICON),
+                                  LR_DEFAULTCOLOR);
+        }
+        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"");
+                    SetDlgItemText(hwnd, IDC_CFG_VERSION, L"");
+                    EnableWindow(GetDlgItem(hwnd, IDC_CFG_ENABLE), FALSE);
+                    EnableWindow(GetDlgItem(hwnd, IDC_CFG_DISABLE), FALSE);
+                    EnableWindow(GetDlgItem(hwnd, IDC_CFG_UNREGISTER), FALSE);
+                    SendDlgItemMessage(hwnd, IDC_CFG_DEPS, 
+                                       LB_RESETCONTENT, 0, 0);
+                    SendDlgItemMessage(hwnd, IDC_CFG_ICON, STM_SETICON,
+                                       (WPARAM) d->plugin_ico, 0);
+                    d->selected = NULL;
+                } else {
+                    int idx;
+                    plugin_data * info;
+
+                    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;
+
+                    update_dialog_fields(hwnd, d, info);
+                }
+            }
+        }
+        return TRUE;
+
+    case WM_COMMAND:
+        {
+
+            d = (plugin_dlg_data *) (LONG_PTR)
+                GetWindowLongPtr(hwnd, DWLP_USER);
+
+            switch (wParam) {
+            case MAKEWPARAM(IDC_CFG_ENABLE, BN_CLICKED):
+                if (d->selected != NULL) {
+                    khui_alert * alert = NULL;
+                    wchar_t buf[KHUI_MAXCCH_MESSAGE];
+                    wchar_t fmt[KHUI_MAXCCH_MESSAGE];
+                    kmm_plugin p;
+
+                    khui_alert_create_empty(&alert);
+
+                    LoadString(khm_hInstance, IDS_CFG_P_ENBCNFT,
+                               fmt, ARRAYLENGTH(fmt));
+                    StringCbPrintf(buf, sizeof(buf), fmt, d->selected->plugin.reg.name);
+                    khui_alert_set_title(alert, buf);
+
+                    LoadString(khm_hInstance, IDS_CFG_P_ENBCNFM,
+                               fmt, ARRAYLENGTH(fmt));
+                    StringCbPrintf(buf, sizeof(buf), fmt, d->selected->plugin.reg.name);
+                    khui_alert_set_message(alert, buf);
+
+                    khui_alert_set_severity(alert, KHERR_INFO);
+
+                    khui_alert_show_modal(alert);
+
+                    kmm_enable_plugin(d->selected->plugin.h_plugin, TRUE);
+
+                    khui_alert_release(alert);
+
+                    p = d->selected->plugin.h_plugin;
+                    kmm_hold_plugin(p);
+                    kmm_release_plugin_info_i(&d->selected->plugin);
+                    kmm_get_plugin_info_i(p, &d->selected->plugin);
+                    kmm_release_plugin(p);
+
+                    update_dialog_fields(hwnd, d, d->selected);
+                }
+                break;
+
+            case MAKEWPARAM(IDC_CFG_DISABLE, BN_CLICKED):
+                if (d->selected != NULL) {
+                    khui_alert * alert = NULL;
+                    wchar_t buf[KHUI_MAXCCH_MESSAGE];
+                    wchar_t fmt[KHUI_MAXCCH_MESSAGE];
+                    wchar_t depends[KHUI_MAXCCH_MESSAGE];
+                    khm_size i;
+                    kmm_plugin p;
+
+                    khui_alert_create_empty(&alert);
+#ifdef DEBUG
+                    assert(alert);
+#endif
+                    if (alert == NULL)
+                        break;
+
+                    LoadString(khm_hInstance, IDS_CFG_P_DELCNFT,
+                               fmt, ARRAYLENGTH(fmt));
+                    StringCbPrintf(buf, sizeof(buf), fmt, d->selected->plugin.reg.name);
+                    khui_alert_set_title(alert, buf);
+
+                    LoadString(khm_hInstance, IDS_CFG_P_DELCNFM,
+                               fmt, ARRAYLENGTH(fmt));
+                    StringCbPrintf(buf, sizeof(buf), fmt, d->selected->plugin.reg.name);
+                    khui_alert_set_message(alert, buf);
+
+                    depends[0] = L'\0';
+
+                    for (i=0; i<d->n_info; i++) {
+                        wchar_t * t;
+
+                        t = d->info[i]->plugin.reg.dependencies;
+
+                        while(t) {
+                            if (!wcscmp(t, d->selected->plugin.reg.name)) {
+                                if (depends[0])
+                                    StringCbCat(depends, sizeof(depends), L", ");
+                                StringCbCat(depends, sizeof(depends),
+                                            d->info[i]->plugin.reg.name);
+                                break;
+                            }
+                            t = multi_string_next(t);
+                        }
+                    }
+
+                    if (depends[0]) {
+                        LoadString(khm_hInstance, IDS_CFG_P_DELCNFS,
+                                   fmt, ARRAYLENGTH(fmt));
+                        StringCbPrintf(buf, sizeof(buf), fmt, depends);
+                        khui_alert_set_suggestion(alert, buf);
+                    } else {
+                        LoadString(khm_hInstance, IDS_CFG_P_DELNDEP,
+                                   buf, ARRAYLENGTH(buf));
+                        khui_alert_set_suggestion(alert, buf);
+                    }
+
+                    khui_alert_add_command(alert, KHUI_PACTION_YES);
+                    khui_alert_add_command(alert, KHUI_PACTION_NO);
+
+                    khui_alert_set_severity(alert, KHERR_WARNING);
+
+                    if (KHM_SUCCEEDED(khui_alert_show_modal(alert)) &&
+                        alert->response == KHUI_PACTION_YES) {
+                        kmm_enable_plugin(d->selected->plugin.h_plugin, FALSE);
+                    }
+
+                    khui_alert_release(alert);
+
+                    p = d->selected->plugin.h_plugin;
+                    kmm_hold_plugin(p);
+                    kmm_release_plugin_info_i(&d->selected->plugin);
+                    kmm_get_plugin_info_i(p, &d->selected->plugin);
+                    kmm_release_plugin(p);
+
+                    update_dialog_fields(hwnd, d, d->selected);
+                }
+                break;
+
+            case MAKEWPARAM(IDC_CFG_UNREGISTER, BN_CLICKED):
+                {
+                    khui_alert * alert = NULL;
+                    wchar_t buf[KHUI_MAXCCH_MESSAGE];
+                    wchar_t fmt[KHUI_MAXCCH_MESSAGE];
+                    wchar_t plist[KHUI_MAXCCH_MESSAGE];
+                    khm_size i;
+
+                    if (d->selected == NULL) {
+#ifdef DEBUG
+                        assert(FALSE);
+#endif
+                        break;
+                    }
+
+                    khui_alert_create_empty(&alert);
+
+                    LoadString(khm_hInstance, IDS_CFG_P_UNRCNFT,
+                               fmt, ARRAYLENGTH(fmt));
+                    StringCbPrintf(buf, sizeof(buf), fmt,
+                                   d->selected->plugin.reg.name);
+
+                    khui_alert_set_title(alert, buf);
+
+                    LoadString(khm_hInstance, IDS_CFG_P_UNRCNFM,
+                               fmt, ARRAYLENGTH(fmt));
+                    StringCbPrintf(buf, sizeof(buf), fmt,
+                                   d->selected->plugin.reg.name);
+
+                    khui_alert_set_message(alert, buf);
+
+                    plist[0] = L'\0';
+                    for (i=0; i < d->n_info; i++) {
+                        if (!wcscmp(d->info[i]->module.reg.name,
+                                    d->selected->module.reg.name)) {
+                            if (plist[0])
+                                StringCbCat(plist, sizeof(plist), L", ");
+                            StringCbCat(plist, sizeof(plist),
+                                        d->info[i]->plugin.reg.name);
+                        }
+                    }
+
+#ifdef DEBUG
+                    /* there should have been at least one plugin */
+                    assert(plist[0]);
+#endif
+
+                    LoadString(khm_hInstance, IDS_CFG_P_UNRCNFS,
+                               fmt, ARRAYLENGTH(fmt));
+                    StringCbPrintf(buf, sizeof(buf), fmt, plist);
+                    khui_alert_set_suggestion(alert, buf);
+
+                    khui_alert_add_command(alert, KHUI_PACTION_YES);
+                    khui_alert_add_command(alert, KHUI_PACTION_NO);
+
+                    khui_alert_set_severity(alert, KHERR_WARNING);
+
+                    if (KHM_SUCCEEDED(khui_alert_show_modal(alert)) &&
+                        alert->response == KHUI_PACTION_YES) {
+                        kmm_unregister_module(d->selected->module.reg.name, 0);
+
+                        update_dialog_fields(hwnd, d, d->selected);
+                    }
+                }
+                break;
+
+            case MAKEWPARAM(IDC_CFG_REGISTER, BN_CLICKED):
+                {
+                    
+                }
+                break;
+            }
+        }
+        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);
+                PFREE(d->info[i]);
+            }
+
+            PFREE(d);
+
+            khm_set_dialog_result(hwnd, 0);
+        }
+        return TRUE;
+    }
+    return FALSE;
+}
index 03eec9ad5a62201cf48723508f4e689e4d687724..49ebfe48eca3acceb5546060dcd240010c69454a 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khmapp.h>\r
-#include<assert.h>\r
-\r
-static HWND cfgui_hwnd = NULL;\r
-\r
-typedef struct tag_cfgui_wnd_data {\r
-    khui_config_node current;\r
-    HWND hw_current;\r
-    HWND hw_generic_pane;\r
-    HBRUSH hbr_white;\r
-    HFONT hf_title;\r
-    khui_bitmap kbmp_logo;\r
-    HIMAGELIST hi_status;\r
-    BOOL modified;\r
-    int idx_default;\r
-    int idx_modified;\r
-    int idx_applied;\r
-} cfgui_wnd_data;\r
-\r
-static cfgui_wnd_data *\r
-cfgui_get_wnd_data(HWND hwnd) {\r
-    return (cfgui_wnd_data *)(LONG_PTR) \r
-        GetWindowLongPtr(hwnd, DWLP_USER);\r
-}\r
-\r
-static void\r
-cfgui_set_wnd_data(HWND hwnd, cfgui_wnd_data * d) {\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-    SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d);\r
-#pragma warning(pop)\r
-}\r
-\r
-static void\r
-cfgui_add_node(cfgui_wnd_data * d,\r
-               HWND hwtv,\r
-               khui_config_node node,\r
-               khui_config_node parent,\r
-               BOOL sorted) {\r
-\r
-    khui_config_node_reg reg;\r
-    khui_config_node c;\r
-    wchar_t wbuf[256];\r
-    const wchar_t * short_desc;\r
-    TVINSERTSTRUCT s;\r
-    HTREEITEM hItem;\r
-\r
-    if (node) {\r
-        khui_cfg_get_reg(node, &reg);\r
-        short_desc = reg.short_desc;\r
-    } else {\r
-        short_desc = wbuf;\r
-        LoadString(khm_hInstance, IDS_CFG_ROOT_NAME,\r
-                   wbuf, ARRAYLENGTH(wbuf));\r
-        reg.flags = 0;\r
-    }\r
-\r
-    ZeroMemory(&s, sizeof(s));\r
-\r
-    s.hParent = (node)?\r
-        (HTREEITEM) khui_cfg_get_param(parent):\r
-        TVI_ROOT;\r
-\r
-    s.hInsertAfter = (sorted)? TVI_SORT: TVI_FIRST;\r
-\r
-    s.itemex.mask =\r
-        TVIF_CHILDREN |\r
-        TVIF_PARAM |\r
-        TVIF_TEXT |\r
-        TVIF_STATE;\r
-\r
-    {\r
-        khui_config_node n;\r
-\r
-        if (KHM_SUCCEEDED(khui_cfg_get_first_child(node,\r
-                                                   &n))) {\r
-            s.itemex.cChildren = 1;\r
-            s.itemex.state = TVIS_EXPANDED;\r
-            s.itemex.stateMask = TVIS_EXPANDED;\r
-            khui_cfg_release(n);\r
-        } else {\r
-            s.itemex.cChildren = 0;\r
-            s.itemex.state = 0;\r
-            s.itemex.stateMask = TVIS_EXPANDED;\r
-        }\r
-\r
-        s.itemex.state |= INDEXTOSTATEIMAGEMASK(d->idx_default);\r
-        s.itemex.stateMask |= TVIS_STATEIMAGEMASK;\r
-    }\r
-\r
-    s.itemex.lParam = (LPARAM) node;\r
-    khui_cfg_hold(node);\r
-\r
-    s.itemex.pszText = (LPWSTR) short_desc;\r
-\r
-    hItem = TreeView_InsertItem(hwtv, &s);\r
-\r
-    khui_cfg_set_param(node, (LPARAM) hItem);\r
-\r
-    if (KHM_SUCCEEDED(khui_cfg_get_first_child(node,\r
-                                             &c))) {\r
-        do {\r
-            cfgui_add_node(d, hwtv, c, node,\r
-                           !!(reg.flags & KHUI_CNFLAG_SORT_CHILDREN));\r
-        } while (KHM_SUCCEEDED(khui_cfg_get_next_release(&c)));\r
-    }\r
-}\r
-\r
-static void \r
-cfgui_initialize_dialog(HWND hwnd) {\r
-    cfgui_wnd_data * d;\r
-    HWND hwtv;\r
-    HWND hwtitle;\r
-    HFONT hf;\r
-    HDC hdc;\r
-    HICON hicon;\r
-\r
-    d = cfgui_get_wnd_data(hwnd);\r
-\r
-    /* create and fill the image list for the treeview */\r
-\r
-    d->hi_status = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), \r
-                                    ILC_COLOR8 | ILC_MASK,\r
-                                    4,4);\r
-\r
-    hicon = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_CFG_DEFAULT),\r
-                      IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);\r
-\r
-    /* note that we can't use index 0 because that is used to indicate\r
-       that there is no state image for the node */\r
-    do {\r
-        d->idx_default = ImageList_AddIcon(d->hi_status, hicon);\r
-    } while(d->idx_default == 0);\r
-\r
-    DestroyIcon(hicon);\r
-\r
-    hicon = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_CFG_MODIFIED),\r
-                      IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);\r
-\r
-    d->idx_modified = ImageList_AddIcon(d->hi_status, hicon);\r
-\r
-    DestroyIcon(hicon);\r
-\r
-    hicon = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_CFG_APPLIED),\r
-                      IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);\r
-\r
-    d->idx_applied = ImageList_AddIcon(d->hi_status, hicon);\r
-\r
-    DestroyIcon(hicon);\r
-\r
-    /* now for the treeview */\r
-    hwtv = GetDlgItem(hwnd, IDC_CFG_NODELIST);\r
-\r
-    TreeView_SetImageList(hwtv, d->hi_status, TVSIL_STATE);\r
-\r
-    cfgui_add_node(d, hwtv, NULL, NULL, FALSE);\r
-\r
-    hdc = GetDC(hwnd);\r
-    hf = CreateFont(-MulDiv(12, \r
-                            GetDeviceCaps(hdc, LOGPIXELSY), \r
-                            72),\r
-                    0,          /* nWidth */\r
-                    0,          /* nEscapement */\r
-                    0,          /* nOrientation */\r
-                    FW_BOLD,    /* fnWeight */\r
-                    TRUE,       /* fdwItalic */\r
-                    FALSE,      /* fdwUnderline */\r
-                    FALSE,      /* fdwStrikeOut */\r
-                    DEFAULT_CHARSET, /* fdwCharSet */\r
-                    OUT_DEFAULT_PRECIS, /* fdwOutputPrecision */\r
-                    CLIP_DEFAULT_PRECIS, /* fdwClipPrecision */\r
-                    DEFAULT_QUALITY, /* fdwQuality */\r
-                    FF_SWISS | DEFAULT_PITCH, /* pitch&family */\r
-                    NULL);      /* face */\r
-    ReleaseDC(hwnd, hdc);\r
-\r
-    d->hf_title = hf;\r
-\r
-    hwtitle = GetDlgItem(hwnd, IDC_CFG_TITLE);\r
-\r
-    SendMessage(hwtitle,\r
-                WM_SETFONT,\r
-                (WPARAM) hf,\r
-                (LPARAM) FALSE);\r
-}\r
-\r
-static void\r
-cfgui_free_node(HWND hwtv, HTREEITEM hItem) {\r
-    TVITEMEX iex;\r
-    HTREEITEM hChItem;\r
-\r
-    ZeroMemory(&iex, sizeof(iex));\r
-\r
-    iex.mask = TVIF_PARAM;\r
-    iex.hItem = hItem;\r
-\r
-    if (TreeView_GetItem(hwtv, &iex)) {\r
-        khui_config_node node;\r
-\r
-        node = (khui_config_node) iex.lParam;\r
-        khui_cfg_release(node);\r
-    }\r
-\r
-    hChItem = TreeView_GetChild(hwtv, hItem);\r
-    while(hChItem) {\r
-        cfgui_free_node(hwtv, hChItem);\r
-\r
-        hChItem = TreeView_GetNextSibling(hwtv, hChItem);\r
-    }\r
-}\r
-\r
-static void\r
-cfgui_uninitialize_dialog(HWND hwnd) {\r
-    cfgui_wnd_data * d;\r
-    HWND hwtv;\r
-\r
-    d = cfgui_get_wnd_data(hwnd);\r
-\r
-    hwtv = GetDlgItem(hwnd, IDC_CFG_NODELIST);\r
-\r
-    cfgui_free_node(hwtv, TreeView_GetRoot(hwtv));\r
-\r
-    if (d->hf_title)\r
-        DeleteObject(d->hf_title);\r
-\r
-    if (d->hi_status)\r
-        ImageList_Destroy(d->hi_status);\r
-}\r
-\r
-static HWND\r
-cfgui_create_config_node_window(HWND hwnd, khui_config_node node) {\r
-    khui_config_node_reg reg;\r
-    khm_int32 rv;\r
-    HWND hw_new;\r
-\r
-    khui_config_node parent;\r
-\r
-    if (KHM_SUCCEEDED(khui_cfg_get_parent(node, &parent))) {\r
-        HWND hwp;\r
-\r
-        hwp = khui_cfg_get_hwnd(parent);\r
-\r
-        if (hwp == NULL)\r
-            cfgui_create_config_node_window(hwnd, parent);\r
-\r
-        khui_cfg_release(parent);\r
-    }\r
-\r
-    rv = khui_cfg_get_reg(node, &reg);\r
-#ifdef DEBUG\r
-    assert(KHM_SUCCEEDED(rv));\r
-#endif\r
-    hw_new = CreateDialogParam(reg.h_module,\r
-                               reg.dlg_template,\r
-                               hwnd,\r
-                               reg.dlg_proc,\r
-                               (LPARAM) node);\r
-#ifdef DEBUG\r
-    assert(hw_new);\r
-#endif\r
-    khui_cfg_set_hwnd(node, hw_new);\r
-\r
-    return hw_new;\r
-}\r
-\r
-static void\r
-cfgui_activate_node(HWND hwnd, khui_config_node node) {\r
-\r
-    cfgui_wnd_data * d;\r
-    HTREEITEM hItem;\r
-    HWND hw_new;\r
-    HWND hwtv;\r
-\r
-    d = cfgui_get_wnd_data(hwnd);\r
-    hwtv = GetDlgItem(hwnd, IDC_CFG_NODELIST);\r
-    hItem = (HTREEITEM) khui_cfg_get_param(node);\r
-\r
-#ifdef DEBUG\r
-    assert(hItem);\r
-    assert(hwtv);\r
-#endif\r
-\r
-    if (node == NULL) {\r
-        hw_new = d->hw_generic_pane;\r
-    } else {\r
-\r
-        hw_new = khui_cfg_get_hwnd(node);\r
-\r
-        if (hw_new == NULL) {\r
-            hw_new = cfgui_create_config_node_window(hwnd, node);\r
-        }\r
-    }\r
-\r
-    if (hw_new == d->hw_current)\r
-        return;                 /* nothing to do */\r
-\r
-    {\r
-        RECT r_title;\r
-        RECT r_pane;\r
-        HWND hw;\r
-\r
-        if (d->hw_current)\r
-            ShowWindow(d->hw_current, SW_HIDE);\r
-\r
-        hw = GetDlgItem(hwnd, IDC_CFG_TITLE);\r
-#ifdef DEBUG\r
-        assert(hw);\r
-#endif\r
-        GetWindowRect(hw, &r_title);\r
-\r
-        hw = GetDlgItem(hwnd, IDC_CFG_PANE);\r
-#ifdef DEBUG\r
-        assert(hw);\r
-#endif\r
-        GetWindowRect(hw, &r_pane);\r
-\r
-        OffsetRect(&r_pane, -r_title.left, -r_title.top);\r
-\r
-        SetWindowPos(hw_new,\r
-                     hwtv,\r
-                     r_pane.left, r_pane.top,\r
-                     r_pane.right - r_pane.left,\r
-                     r_pane.bottom - r_pane.top,\r
-                     SWP_NOOWNERZORDER |\r
-                     SWP_SHOWWINDOW |\r
-                     SWP_NOACTIVATE);\r
-    }\r
-\r
-    if (node == NULL) {\r
-        wchar_t wbuf[256];\r
-\r
-        LoadString(khm_hInstance, IDS_CFG_ROOT_TITLE,\r
-                   wbuf, ARRAYLENGTH(wbuf));\r
-\r
-        SetDlgItemText(hwnd, IDC_CFG_TITLE, wbuf);\r
-    } else {\r
-        khm_int32 rv;\r
-        khui_config_node_reg reg;\r
-\r
-        rv = khui_cfg_get_reg(node, &reg);\r
-#ifdef DEBUG\r
-        assert(KHM_SUCCEEDED(rv));\r
-#endif\r
-        SetDlgItemText(hwnd, IDC_CFG_TITLE, reg.long_desc);\r
-    }\r
-\r
-    d->hw_current = hw_new;\r
-    d->current = node;\r
-\r
-    TreeView_SelectItem(hwtv, hItem);\r
-}\r
-\r
-static BOOL\r
-cfgui_check_mod_state(khui_config_node node) {\r
-    khm_int32 flags;\r
-    khui_config_node c = NULL;\r
-    BOOL rv = FALSE;\r
-\r
-    flags = khui_cfg_get_flags(node);\r
-\r
-    if (flags & KHUI_CNFLAG_MODIFIED)\r
-        return TRUE;\r
-\r
-    if (KHM_FAILED(khui_cfg_get_first_child(node, &c)))\r
-        return FALSE;\r
-\r
-    while(c) {\r
-        rv = (rv || cfgui_check_mod_state(c));\r
-        khui_cfg_get_next_release(&c);\r
-    }\r
-\r
-    return rv;\r
-}\r
-\r
-static void\r
-cfgui_apply_settings(khui_config_node node) {\r
-    HWND hwnd;\r
-    khui_config_node c;\r
-    khm_int32 flags;\r
-\r
-    hwnd = khui_cfg_get_hwnd(node);\r
-    flags = khui_cfg_get_flags(node);\r
-\r
-    if (hwnd && (flags & KHUI_CNFLAG_MODIFIED)) {\r
-        SendMessage(hwnd, KHUI_WM_CFG_NOTIFY,\r
-                    MAKEWPARAM(0, WMCFG_APPLY),\r
-                    (LPARAM) node);\r
-    }\r
-\r
-    if (KHM_FAILED(khui_cfg_get_first_child(node, &c)))\r
-        return;\r
-\r
-    while (c) {\r
-        cfgui_apply_settings(c);\r
-        khui_cfg_get_next_release(&c);\r
-    }\r
-}\r
-\r
-static void\r
-cfgui_remove_item(HWND hwtv,\r
-                  HTREEITEM hItem) {\r
-    khui_config_node node;\r
-    HTREEITEM hChild;\r
-    TVITEMEX itemex;\r
-\r
-    for (hChild = TreeView_GetChild(hwtv, hItem);\r
-         hChild;\r
-         hChild = TreeView_GetChild(hwtv, hItem)) {\r
-\r
-        cfgui_remove_item(hwtv, hChild);\r
-\r
-    }\r
-\r
-    ZeroMemory(&itemex, sizeof(itemex));\r
-\r
-    itemex.mask = TVIF_PARAM;\r
-    itemex.hItem = hItem;\r
-\r
-    TreeView_GetChild(hwtv, &itemex);\r
-\r
-    node = (khui_config_node) itemex.lParam;\r
-\r
-    if (node) {\r
-        HWND hw;\r
-        hw = khui_cfg_get_hwnd(node);\r
-\r
-        if (hw)\r
-            DestroyWindow(hw);\r
-\r
-        khui_cfg_release(node);\r
-    }\r
-\r
-    TreeView_DeleteItem(hwtv, hItem);\r
-}\r
-\r
-struct cfgui_child_info {\r
-    HTREEITEM hItem;\r
-    khui_config_node node;\r
-    BOOL checked;\r
-};\r
-\r
-#define CI_ALLOC_INCR 8\r
-\r
-static void\r
-cfgui_sync_node(cfgui_wnd_data * d,\r
-                HWND hwtv,\r
-                khui_config_node c,\r
-                HTREEITEM hItem) {\r
-    khui_config_node child;\r
-    HTREEITEM hChild;\r
-    struct cfgui_child_info * childinfo = NULL;\r
-    khm_size n_childinfo = 0;\r
-    khm_size nc_childinfo = 0;\r
-    khm_size i;\r
-\r
-    /* first, get the list of children from the treeview control */\r
-    for (hChild = TreeView_GetChild(hwtv, hItem);\r
-         hChild;\r
-         hChild = TreeView_GetNextSibling(hwtv, hChild)) {\r
-\r
-        if (n_childinfo >= nc_childinfo) {\r
-            nc_childinfo = UBOUNDSS(n_childinfo + 1,\r
-                                    CI_ALLOC_INCR, CI_ALLOC_INCR);\r
-#ifdef DEBUG\r
-            assert(nc_childinfo > n_childinfo);\r
-#endif\r
-            childinfo = PREALLOC(childinfo,\r
-                                 sizeof(*childinfo) * nc_childinfo);\r
-#ifdef DEBUG\r
-            assert(childinfo);\r
-#endif\r
-        }\r
-\r
-        ZeroMemory(&childinfo[n_childinfo],\r
-                   sizeof(childinfo[n_childinfo]));\r
-\r
-        childinfo[n_childinfo].hItem = hChild;\r
-        childinfo[n_childinfo].checked = FALSE;\r
-        n_childinfo++;\r
-    }\r
-\r
-    /* now, go through the list of actual nodes and make sure they\r
-       match up */\r
-    child = NULL;\r
-    for (khui_cfg_get_first_child(c, &child);\r
-         child;\r
-         khui_cfg_get_next_release(&child)) {\r
-\r
-        hChild = (HTREEITEM) khui_cfg_get_param(child);\r
-\r
-        for (i=0; i < n_childinfo; i++) {\r
-            if (childinfo[i].hItem == hChild)\r
-                break;\r
-        }\r
-\r
-        if (i < n_childinfo) {\r
-            childinfo[i].checked = TRUE;\r
-        } else {\r
-            /* add it to the list, so we can create the node in the\r
-               tree view control later. */\r
-            if (n_childinfo >= nc_childinfo) {\r
-                nc_childinfo = UBOUNDSS(n_childinfo + 1,\r
-                                        CI_ALLOC_INCR, CI_ALLOC_INCR);\r
-#ifdef DEBUG\r
-                assert(nc_childinfo > n_childinfo);\r
-#endif\r
-                childinfo = PREALLOC(childinfo,\r
-                                     sizeof(*childinfo) * nc_childinfo);\r
-#ifdef DEBUG\r
-                assert(childinfo);\r
-#endif\r
-            }\r
-\r
-            ZeroMemory(&childinfo[n_childinfo],\r
-                       sizeof(childinfo[n_childinfo]));\r
-\r
-            childinfo[n_childinfo].node = child;\r
-            khui_cfg_hold(child);\r
-            n_childinfo++;\r
-        }\r
-    }\r
-\r
-    /* by this point, the childinfo list contains items of the\r
-       following forms:\r
-\r
-       1. childinfo[i].hItem != NULL && childinfo[i].checked == TRUE\r
-\r
-          Corresponds to a tree view item that has a matching\r
-          configuration node.  Nothing to do here.\r
-\r
-       2. childinfo[i].hItem != NULL && childinfo[i].checked == FALSE\r
-\r
-          Corresponds to a tree view item that has no matching\r
-          configuration node.  These should be removed.\r
-\r
-       3. childinfo[i].hItem == NULL && childinfo[i].node != NULL\r
-\r
-          Corresponds to a configuration node that has no matching\r
-          tree view item.  These nodes should be added.\r
-    */\r
-\r
-    /* first do the removals */\r
-    for (i=0; i < n_childinfo; i++) {\r
-        if (childinfo[i].hItem == NULL)\r
-            break;              /* nothing more to see from this point\r
-                                   on */\r
-        if (!childinfo[i].checked) {\r
-            /* remove! */\r
-            cfgui_remove_item(hwtv, childinfo[i].hItem);\r
-        }\r
-    }\r
-\r
-    /* continue from where the previous loop left off */\r
-    for (; i < n_childinfo; i++) {\r
-#ifdef DEBUG\r
-        assert(childinfo[i].hItem == NULL);\r
-        assert(childinfo[i].node != NULL);\r
-#endif\r
-\r
-        cfgui_add_node(d, hwtv, childinfo[i].node, c, FALSE);\r
-\r
-        khui_cfg_release(childinfo[i].node);\r
-        childinfo[i].node = NULL;\r
-    }\r
-\r
-    if (childinfo)\r
-        PFREE(childinfo);\r
-\r
-    /* finally recurse through to the next level */\r
-    for (hChild = TreeView_GetChild(hwtv, hItem);\r
-         hChild;\r
-         hChild = TreeView_GetNextSibling(hwtv, hChild)) {\r
-\r
-        TVITEMEX itemex;\r
-\r
-        ZeroMemory(&itemex, sizeof(itemex));\r
-\r
-        itemex.mask = TVIF_PARAM;\r
-        itemex.hItem = hChild;\r
-\r
-        TreeView_GetItem(hwtv, &itemex);\r
-\r
-        if (itemex.lParam) {\r
-            child = (khui_config_node) itemex.lParam;\r
-\r
-            cfgui_sync_node(d, hwtv, child, hChild);\r
-        }\r
-    }\r
-}\r
-\r
-static void\r
-cfgui_sync_node_list(cfgui_wnd_data * d, HWND hwnd) {\r
-    HWND hwtv;\r
-    HTREEITEM hItem;\r
-\r
-    hwtv = GetDlgItem(hwnd, IDC_CFG_NODELIST);\r
-    hItem = TreeView_GetRoot(hwtv);\r
-\r
-    cfgui_sync_node(d, hwtv, NULL, hItem);\r
-}\r
-\r
-static void\r
-cfgui_update_state(HWND hwnd, \r
-                   khm_int32 flags,\r
-                   khui_config_node node) {\r
-    cfgui_wnd_data * d;\r
-    HWND hwtv;\r
-    HTREEITEM hItem;\r
-    TVITEMEX itx;\r
-    int idx;\r
-\r
-    d = cfgui_get_wnd_data(hwnd);\r
-    hwtv = GetDlgItem(hwnd, IDC_CFG_NODELIST);\r
-    hItem = (HTREEITEM) khui_cfg_get_param(node);\r
-\r
-    ZeroMemory(&itx, sizeof(itx));\r
-\r
-    if (flags & KHUI_CNFLAG_MODIFIED)\r
-        idx = d->idx_modified;\r
-    else if (flags & KHUI_CNFLAG_APPLIED)\r
-        idx = d->idx_applied;\r
-    else\r
-        idx = d->idx_default;\r
-\r
-    itx.hItem = hItem;\r
-    itx.mask = TVIF_STATE;\r
-    itx.state = INDEXTOSTATEIMAGEMASK(idx);\r
-    itx.stateMask = TVIS_STATEIMAGEMASK;\r
-\r
-    TreeView_SetItem(hwtv, &itx);\r
-\r
-    if(cfgui_check_mod_state(NULL)) {\r
-        EnableWindow(GetDlgItem(hwnd, IDAPPLY), TRUE);\r
-    } else {\r
-        EnableWindow(GetDlgItem(hwnd, IDAPPLY), FALSE);\r
-    }\r
-}\r
-\r
-\r
-/* dialog procedure for the generic dialog */\r
-static INT_PTR CALLBACK\r
-cfgui_dlgproc_generic(HWND hwnd,\r
-                      UINT uMsg,\r
-                      WPARAM wParam,\r
-                      LPARAM lParam) {\r
-    cfgui_wnd_data * d;\r
-\r
-    switch(uMsg) {\r
-    case WM_INITDIALOG:\r
-        d = (cfgui_wnd_data *) lParam;\r
-        cfgui_set_wnd_data(hwnd, d);\r
-        return TRUE;\r
-\r
-    case WM_CTLCOLORSTATIC:\r
-        d = cfgui_get_wnd_data(hwnd);\r
-        return (BOOL)(DWORD_PTR) d->hbr_white;\r
-\r
-    case WM_ERASEBKGND:\r
-        {\r
-            HDC hdc = (HDC) wParam;\r
-            RECT r_client;\r
-            RECT r_logo;\r
-            RECT r_fill;\r
-\r
-            d = cfgui_get_wnd_data(hwnd);\r
-\r
-            GetClientRect(hwnd, &r_client);\r
-            SetRectEmpty(&r_logo);\r
-\r
-            r_logo.right = d->kbmp_logo.cx;\r
-            r_logo.bottom = d->kbmp_logo.cy;\r
-\r
-            OffsetRect(&r_logo,\r
-                       r_client.right - r_logo.right,\r
-                       r_client.bottom - r_logo.bottom);\r
-\r
-            khui_draw_bitmap(hdc,\r
-                             r_logo.left,\r
-                             r_logo.top,\r
-                             &d->kbmp_logo);\r
-\r
-            r_fill.left = 0;\r
-            r_fill.top = 0;\r
-            r_fill.right = r_logo.left;\r
-            r_fill.bottom = r_client.bottom;\r
-            FillRect(hdc, &r_fill, d->hbr_white);\r
-\r
-            r_fill.left = r_logo.left;\r
-            r_fill.right = r_client.right;\r
-            r_fill.bottom = r_logo.top;\r
-            FillRect(hdc, &r_fill, d->hbr_white);\r
-\r
-            SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LONG) TRUE);\r
-        }\r
-        return TRUE;\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-static INT_PTR CALLBACK \r
-cfgui_dlgproc(HWND hwnd,\r
-              UINT uMsg,\r
-              WPARAM wParam,\r
-              LPARAM lParam) {\r
-\r
-    khui_config_node node;\r
-    cfgui_wnd_data * d;\r
-\r
-    switch(uMsg) {\r
-    case WM_INITDIALOG:\r
-        node = (khui_config_node) lParam;\r
-\r
-        khui_cfg_clear_params();\r
-\r
-        khui_cfg_set_configui_handle(hwnd);\r
-\r
-        d = PMALLOC(sizeof(*d));\r
-        ZeroMemory(d, sizeof(*d));\r
-\r
-        d->hbr_white = CreateSolidBrush(RGB(255,255,255));\r
-\r
-        d->hw_generic_pane = \r
-            CreateDialogParam(khm_hInstance,\r
-                              MAKEINTRESOURCE(IDD_CFG_GENERIC),\r
-                              hwnd,\r
-                              cfgui_dlgproc_generic,\r
-                              (LPARAM) d);\r
-\r
-        khui_bitmap_from_hbmp(&d->kbmp_logo,\r
-                              LoadImage(\r
-                                        khm_hInstance,\r
-                                        MAKEINTRESOURCE(IDB_LOGO_OPAQUE),\r
-                                        IMAGE_BITMAP,\r
-                                        0,\r
-                                        0,\r
-                                        LR_DEFAULTCOLOR));\r
-\r
-        cfgui_set_wnd_data(hwnd, d);\r
-\r
-        cfgui_initialize_dialog(hwnd);\r
-\r
-        cfgui_activate_node(hwnd, node);\r
-\r
-        khm_add_dialog(hwnd);\r
-        khm_enter_modal(hwnd);\r
-\r
-        return TRUE;\r
-\r
-    case WM_DESTROY:\r
-        cfgui_hwnd = NULL;\r
-\r
-        khui_cfg_set_configui_handle(NULL);\r
-\r
-        cfgui_uninitialize_dialog(hwnd);\r
-\r
-        d = cfgui_get_wnd_data(hwnd);\r
-        khui_delete_bitmap(&d->kbmp_logo);\r
-        DeleteObject(d->hbr_white);\r
-\r
-        cfgui_set_wnd_data(hwnd, NULL);\r
-\r
-        khm_del_dialog(hwnd);\r
-\r
-        SetForegroundWindow(khm_hwnd_main);\r
-\r
-        PFREE(d);\r
-\r
-        return FALSE;\r
-\r
-    case WM_NOTIFY:\r
-        {\r
-            LPNMHDR lpnm;\r
-            LPNMTREEVIEW lptv;\r
-            LPNMTVGETINFOTIP lpgi;\r
-            khui_config_node node;\r
-\r
-            lpnm = (LPNMHDR) lParam;\r
-\r
-            switch (lpnm->code) {\r
-            case TVN_SELCHANGED:\r
-                lptv = (LPNMTREEVIEW) lParam;\r
-                cfgui_activate_node(hwnd,\r
-                                    (khui_config_node) \r
-                                    lptv->itemNew.lParam);\r
-                return TRUE;\r
-\r
-            case TVN_GETINFOTIP:\r
-                lpgi = (LPNMTVGETINFOTIP) lParam;\r
-                node = (khui_config_node) lpgi->lParam;\r
-\r
-                if (node) {\r
-                    khm_int32 flags = 0;\r
-\r
-                    flags = khui_cfg_get_flags(node);\r
-\r
-                    if (flags & KHUI_CNFLAG_MODIFIED) {\r
-                        LoadString(khm_hInstance, IDS_CFG_IT_MOD,\r
-                                   lpgi->pszText, lpgi->cchTextMax);\r
-                    } else if (flags & KHUI_CNFLAG_APPLIED) {\r
-                        LoadString(khm_hInstance, IDS_CFG_IT_APP,\r
-                                   lpgi->pszText, lpgi->cchTextMax);\r
-                    } else {\r
-                        LoadString(khm_hInstance, IDS_CFG_IT_NONE,\r
-                                   lpgi->pszText, lpgi->cchTextMax);\r
-                    }\r
-                } else {\r
-                    StringCchCopy(lpgi->pszText, lpgi->cchTextMax, L"");\r
-                }\r
-\r
-                return TRUE;\r
-            }\r
-        }\r
-        return TRUE;\r
-\r
-    case WM_CTLCOLORSTATIC:\r
-        {\r
-            d = cfgui_get_wnd_data(hwnd);\r
-            return (BOOL)(DWORD_PTR) d->hbr_white;\r
-        }\r
-        /* implicit break */\r
-\r
-    case WM_COMMAND:\r
-        switch(wParam) {\r
-        case MAKEWPARAM(IDCANCEL, BN_CLICKED):\r
-            khm_leave_modal();\r
-            DestroyWindow(hwnd);\r
-            break;\r
-\r
-        case MAKEWPARAM(IDAPPLY, BN_CLICKED):\r
-            cfgui_apply_settings(NULL);\r
-            break;\r
-\r
-        case MAKEWPARAM(IDOK, BN_CLICKED):\r
-            cfgui_apply_settings(NULL);\r
-            khm_leave_modal();\r
-            DestroyWindow(hwnd);\r
-            break;\r
-        }\r
-        return TRUE;\r
-\r
-    case KHUI_WM_CFG_NOTIFY:\r
-        switch(HIWORD(wParam)) {\r
-        case WMCFG_SHOW_NODE:\r
-            cfgui_activate_node(hwnd, (khui_config_node) lParam);\r
-            break;\r
-\r
-        case WMCFG_UPDATE_STATE:\r
-            cfgui_update_state(hwnd, LOWORD(wParam), \r
-                               (khui_config_node) lParam);\r
-            break;\r
-\r
-        case WMCFG_SYNC_NODE_LIST:\r
-            d = cfgui_get_wnd_data(hwnd);\r
-            cfgui_sync_node_list(d, hwnd);\r
-            break;\r
-        }\r
-\r
-        return TRUE;\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-static void \r
-cfgui_create_window(khui_config_node node) {\r
-#ifdef DEBUG\r
-    assert(cfgui_hwnd == NULL);\r
-#endif\r
-\r
-    khm_refresh_config();\r
-\r
-    cfgui_hwnd = CreateDialogParam(khm_hInstance,\r
-                                   MAKEINTRESOURCE(IDD_CFG_MAIN),\r
-                                   khm_hwnd_main,\r
-                                   cfgui_dlgproc,\r
-                                   (LPARAM) node);\r
-#ifdef DEBUG\r
-    assert(cfgui_hwnd != NULL);\r
-#endif\r
-    ShowWindow(cfgui_hwnd,SW_SHOW);\r
-}\r
-\r
-static void \r
-cfgui_destroy_window(void) {\r
-    if (cfgui_hwnd)\r
-        DestroyWindow(cfgui_hwnd);\r
-    /* cfgui_hwnd will be set to NULL in the dialog proc */\r
-}\r
-\r
-void \r
-khm_show_config_pane(khui_config_node node) {\r
-    if (cfgui_hwnd != NULL) {\r
-        SendMessage(cfgui_hwnd, KHUI_WM_CFG_NOTIFY,\r
-                    MAKEWPARAM(0, WMCFG_SHOW_NODE),\r
-                    (LPARAM) node);\r
-    } else {\r
-        cfgui_create_window(node);\r
-    }\r
-}\r
-\r
-void khm_refresh_config(void) {\r
-    khm_size cb;\r
-    khm_size n_idents;\r
-    wchar_t * idents = NULL;\r
-    wchar_t * t;\r
-    khm_int32 rv;\r
-    int n_tries = 0;\r
-    khui_config_node cfg_ids = NULL;\r
-    khui_config_node cfg_r = NULL;\r
-    khui_config_node cfg_iter = NULL;\r
-    khui_menu_def * omenu;\r
-    khm_boolean refresh_menu = FALSE;\r
-\r
-    do {\r
-        rv = kcdb_identity_enum(KCDB_IDENT_FLAG_CONFIG,\r
-                                KCDB_IDENT_FLAG_CONFIG,\r
-                                NULL,\r
-                                &cb,\r
-                                &n_idents);\r
-\r
-        if (rv != KHM_ERROR_TOO_LONG ||\r
-            n_idents == 0)\r
-            return;\r
-\r
-        if (idents)\r
-            PFREE(idents);\r
-        idents = PMALLOC(cb);\r
-#ifdef DEBUG\r
-        assert(idents);\r
-#endif\r
-\r
-        rv = kcdb_identity_enum(KCDB_IDENT_FLAG_CONFIG,\r
-                                KCDB_IDENT_FLAG_CONFIG,\r
-                                idents,\r
-                                &cb,\r
-                                &n_idents);\r
-\r
-        n_tries++;\r
-    } while(KHM_FAILED(rv) &&\r
-            n_tries < 5);\r
-\r
-    if (KHM_FAILED(rv))\r
-        goto _cleanup;\r
-\r
-    if (KHM_FAILED(khui_cfg_open(NULL,\r
-                                 L"KhmIdentities",\r
-                                 &cfg_ids)))\r
-        goto _cleanup;\r
-\r
-    for(t = idents; t && *t; t = multi_string_next(t)) {\r
-        khui_config_node cfg_id = NULL;\r
-\r
-        rv = khui_cfg_open(cfg_ids,\r
-                           t,\r
-                           &cfg_id);\r
-\r
-        if (KHM_FAILED(rv)) {\r
-            khui_config_node_reg reg;\r
-            wchar_t wshort[KHUI_MAXCCH_SHORT_DESC];\r
-            wchar_t wlong[KHUI_MAXCCH_LONG_DESC];\r
-            wchar_t wfmt[KHUI_MAXCCH_SHORT_DESC];\r
-\r
-            ZeroMemory(&reg, sizeof(reg));\r
-\r
-            reg.name = t;\r
-            reg.short_desc = wshort;\r
-            reg.long_desc = wlong;\r
-            reg.h_module = khm_hInstance;\r
-            reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_IDENTITY);\r
-            reg.dlg_proc = khm_cfg_identity_proc;\r
-            reg.flags = 0;\r
-\r
-            LoadString(khm_hInstance, IDS_CFG_IDENTITY_SHORT,\r
-                       wfmt, ARRAYLENGTH(wfmt));\r
-            StringCbPrintf(wshort, sizeof(wshort), wfmt, t);\r
-\r
-            LoadString(khm_hInstance, IDS_CFG_IDENTITY_LONG,\r
-                       wfmt, ARRAYLENGTH(wfmt));\r
-            StringCbPrintf(wlong, sizeof(wlong), wfmt, t);\r
-\r
-            khui_cfg_register(cfg_ids,\r
-                              &reg);\r
-        } else {\r
-            khui_cfg_release(cfg_id);\r
-        }\r
-    }\r
-\r
-    for (khui_cfg_get_first_child(cfg_ids, &cfg_iter);\r
-         cfg_iter;\r
-         khui_cfg_get_next_release(&cfg_iter)) {\r
-\r
-        wchar_t cfgname[KCDB_IDENT_MAXCCH_NAME];\r
-        khm_size cb;\r
-        khm_handle tident = NULL;\r
-        khm_int32 tflags = 0;\r
-\r
-        cb = sizeof(cfgname);\r
-        khui_cfg_get_name(cfg_iter, cfgname, &cb);\r
-\r
-        if (KHM_FAILED(kcdb_identity_create(cfgname, 0, &tident)) ||\r
-            KHM_FAILED(kcdb_identity_get_flags(tident, &tflags)) ||\r
-            !(tflags & KCDB_IDENT_FLAG_ACTIVE) ||\r
-            !(tflags & KCDB_IDENT_FLAG_CONFIG)) {\r
-\r
-            /* this configuration node needs to be removed */\r
-\r
-            khui_cfg_remove(cfg_iter);\r
-        }\r
-\r
-        if (tident)\r
-            kcdb_identity_release(tident);\r
-    }\r
-\r
-    /* Now iterate through the root level configuration nodes and make\r
-       sure we have a menu item for each of them. */\r
-    if (KHM_FAILED(khui_cfg_get_first_child(NULL, &cfg_r)))\r
-        goto _cleanup;\r
-\r
-    omenu = khui_find_menu(KHUI_MENU_OPTIONS);\r
-    if (omenu == NULL)\r
-        goto _cleanup;\r
-\r
-    khui_action_lock();\r
-\r
-    do {\r
-        khm_int32 action;\r
-        khm_int32 flags;\r
-        khui_action * paction;\r
-        wchar_t cname[KHUI_MAXCCH_NAME];\r
-        wchar_t wshort[KHUI_MAXCCH_SHORT_DESC];\r
-        khm_size cb;\r
-        khm_handle sub;\r
-        khui_config_node_reg reg;\r
-\r
-        flags = khui_cfg_get_flags(cfg_r);\r
-        if (flags & KHUI_CNFLAG_SYSTEM)\r
-            goto _next_cfg;\r
-\r
-        cb = sizeof(cname);\r
-        if (KHM_FAILED(khui_cfg_get_name(cfg_r, cname, &cb))) {\r
-#ifdef DEBUG\r
-            assert(FALSE);\r
-#endif\r
-            goto _next_cfg;\r
-        }\r
-\r
-        paction = khui_find_named_action(cname);\r
-\r
-        if (!paction) {\r
-            khui_cfg_get_reg(cfg_r, &reg);\r
-\r
-            kmq_create_hwnd_subscription(khm_hwnd_main, &sub);\r
-\r
-            StringCbCopy(wshort, sizeof(wshort), reg.short_desc);\r
-            StringCbCat(wshort, sizeof(wshort), L" ...");\r
-\r
-            action = khui_action_create(cname,\r
-                                        wshort,\r
-                                        reg.long_desc,\r
-                                        (void *) CFGACTION_MAGIC,\r
-                                        KHUI_ACTIONTYPE_TRIGGER,\r
-                                        sub);\r
-\r
-            if (action == 0) {\r
-#ifdef DEBUG\r
-                assert(FALSE);\r
-#endif\r
-                goto _next_cfg;\r
-            }\r
-\r
-            khui_menu_insert_action(omenu, -1, action, 0);\r
-\r
-            refresh_menu = TRUE;\r
-        }\r
-\r
-    _next_cfg:\r
-        if (KHM_FAILED(khui_cfg_get_next_release(&cfg_r)))\r
-            break;\r
-    } while(cfg_r);\r
-\r
-    khui_action_unlock();\r
-\r
-    if (refresh_menu) {\r
-        khui_refresh_actions();\r
-    }\r
-\r
- _cleanup:\r
-    if (cfg_ids)\r
-        khui_cfg_release(cfg_ids);\r
-\r
-    if (cfg_r)\r
-        khui_cfg_release(cfg_r);\r
-\r
-    if (idents)\r
-        PFREE(idents);\r
-}\r
-\r
-void khm_init_config(void) {\r
-    wchar_t wshort[KHUI_MAXCCH_SHORT_DESC];\r
-    wchar_t wlong[KHUI_MAXCCH_LONG_DESC];\r
-    khui_config_node_reg reg;\r
-    khui_config_node node;\r
-\r
-    reg.short_desc = wshort;\r
-    reg.long_desc = wlong;\r
-    reg.h_module = khm_hInstance;\r
-    reg.flags = KHUI_CNFLAG_SYSTEM;\r
-\r
-    reg.name = L"KhmGeneral";\r
-    reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_GENERAL);\r
-    reg.dlg_proc = khm_cfg_general_proc;\r
-    LoadString(khm_hInstance, IDS_CFG_GENERAL_SHORT,\r
-               wshort, ARRAYLENGTH(wshort));\r
-    LoadString(khm_hInstance, IDS_CFG_GENERAL_LONG,\r
-               wlong, ARRAYLENGTH(wlong));\r
-\r
-    khui_cfg_register(NULL, &reg);\r
-\r
-    reg.name = L"KhmAppear";\r
-    reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_APPEAR);\r
-    reg.dlg_proc = khm_cfg_appearance_proc;\r
-    LoadString(khm_hInstance, IDS_CFG_APPEAR_SHORT,\r
-               wshort, ARRAYLENGTH(wshort));\r
-    LoadString(khm_hInstance, IDS_CFG_APPEAR_LONG,\r
-               wlong, ARRAYLENGTH(wlong));\r
-\r
-    khui_cfg_register(NULL, &reg);\r
-\r
-    reg.name = L"KhmIdentities";\r
-    reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_IDENTITIES);\r
-    reg.dlg_proc = khm_cfg_identities_proc;\r
-    LoadString(khm_hInstance, IDS_CFG_IDENTITIES_SHORT,\r
-               wshort, ARRAYLENGTH(wshort));\r
-    LoadString(khm_hInstance, IDS_CFG_IDENTITIES_LONG,\r
-               wlong, ARRAYLENGTH(wlong));\r
-\r
-    khui_cfg_register(NULL, &reg);\r
-\r
-    node = NULL;\r
-    khui_cfg_open(NULL, L"KhmIdentities", &node);\r
-#ifdef DEBUG\r
-    assert(node);\r
-#endif\r
-\r
-    reg.name = L"KhmIdentitiesTab";\r
-    reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_IDS_TAB);\r
-    reg.dlg_proc = khm_cfg_ids_tab_proc;\r
-    LoadString(khm_hInstance, IDS_CFG_IDS_TAB_SHORT,\r
-               wshort, ARRAYLENGTH(wshort));\r
-    LoadString(khm_hInstance, IDS_CFG_IDS_TAB_LONG,\r
-               wlong, ARRAYLENGTH(wlong));\r
-    reg.flags = KHUI_CNFLAG_SUBPANEL | KHUI_CNFLAG_SYSTEM;\r
-\r
-    khui_cfg_register(node, &reg);\r
-\r
-    reg.name = L"KhmIdentitiesTabPlural";\r
-    reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_ID_TAB);\r
-    reg.dlg_proc = khm_cfg_id_tab_proc;\r
-    LoadString(khm_hInstance, IDS_CFG_ID_TAB_SHORT,\r
-               wshort, ARRAYLENGTH(wshort));\r
-    LoadString(khm_hInstance, IDS_CFG_ID_TAB_LONG,\r
-               wlong, ARRAYLENGTH(wlong));\r
-    reg.flags = KHUI_CNFLAG_PLURAL | KHUI_CNFLAG_SUBPANEL | KHUI_CNFLAG_SYSTEM;\r
-\r
-    khui_cfg_register(node, &reg);\r
-\r
-    reg.flags = KHUI_CNFLAG_SYSTEM;\r
-    khui_cfg_release(node);\r
-\r
-    reg.name = L"KhmNotifications";\r
-    reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_NOTIF);\r
-    reg.dlg_proc = khm_cfg_notifications_proc;\r
-    LoadString(khm_hInstance, IDS_CFG_NOTIF_SHORT,\r
-               wshort, ARRAYLENGTH(wshort));\r
-    LoadString(khm_hInstance, IDS_CFG_NOTIF_LONG,\r
-               wlong, ARRAYLENGTH(wlong));\r
-\r
-    khui_cfg_register(NULL, &reg);\r
-\r
-    reg.name = L"KhmPlugins";\r
-    reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_PLUGINS);\r
-    reg.dlg_proc = khm_cfg_plugins_proc;\r
-    LoadString(khm_hInstance, IDS_CFG_PLUGINS_SHORT,\r
-               wshort, ARRAYLENGTH(wshort));\r
-    LoadString(khm_hInstance, IDS_CFG_PLUGINS_LONG,\r
-               wlong, ARRAYLENGTH(wlong));\r
-\r
-    khui_cfg_register(NULL, &reg);\r
-}\r
-\r
-void khm_exit_config(void) {\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 
+                                    ILC_COLOR8 | ILC_MASK,
+                                    4,4);
+
+    hicon = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_CFG_DEFAULT),
+                      IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 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, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);
+
+    d->idx_modified = ImageList_AddIcon(d->hi_status, hicon);
+
+    DestroyIcon(hicon);
+
+    hicon = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_CFG_APPLIED),
+                      IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 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 HWND
+cfgui_create_config_node_window(HWND hwnd, khui_config_node node) {
+    khui_config_node_reg reg;
+    khm_int32 rv;
+    HWND hw_new;
+
+    khui_config_node parent;
+
+    if (KHM_SUCCEEDED(khui_cfg_get_parent(node, &parent))) {
+        HWND hwp;
+
+        hwp = khui_cfg_get_hwnd(parent);
+
+        if (hwp == NULL)
+            cfgui_create_config_node_window(hwnd, parent);
+
+        khui_cfg_release(parent);
+    }
+
+    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);
+
+    return hw_new;
+}
+
+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 {
+
+        hw_new = khui_cfg_get_hwnd(node);
+
+        if (hw_new == NULL) {
+            hw_new = cfgui_create_config_node_window(hwnd, node);
+        }
+    }
+
+    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;
+    khm_int32 flags;
+
+    hwnd = khui_cfg_get_hwnd(node);
+    flags = khui_cfg_get_flags(node);
+
+    if (hwnd && (flags & KHUI_CNFLAG_MODIFIED)) {
+        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_remove_item(HWND hwtv,
+                  HTREEITEM hItem) {
+    khui_config_node node;
+    HTREEITEM hChild;
+    TVITEMEX itemex;
+
+    for (hChild = TreeView_GetChild(hwtv, hItem);
+         hChild;
+         hChild = TreeView_GetChild(hwtv, hItem)) {
+
+        cfgui_remove_item(hwtv, hChild);
+
+    }
+
+    ZeroMemory(&itemex, sizeof(itemex));
+
+    itemex.mask = TVIF_PARAM;
+    itemex.hItem = hItem;
+
+    TreeView_GetChild(hwtv, &itemex);
+
+    node = (khui_config_node) itemex.lParam;
+
+    if (node) {
+        HWND hw;
+        hw = khui_cfg_get_hwnd(node);
+
+        if (hw)
+            DestroyWindow(hw);
+
+        khui_cfg_release(node);
+    }
+
+    TreeView_DeleteItem(hwtv, hItem);
+}
+
+struct cfgui_child_info {
+    HTREEITEM hItem;
+    khui_config_node node;
+    BOOL checked;
+};
+
+#define CI_ALLOC_INCR 8
+
+static void
+cfgui_sync_node(cfgui_wnd_data * d,
+                HWND hwtv,
+                khui_config_node c,
+                HTREEITEM hItem) {
+    khui_config_node child;
+    HTREEITEM hChild;
+    struct cfgui_child_info * childinfo = NULL;
+    khm_size n_childinfo = 0;
+    khm_size nc_childinfo = 0;
+    khm_size i;
+
+    /* first, get the list of children from the treeview control */
+    for (hChild = TreeView_GetChild(hwtv, hItem);
+         hChild;
+         hChild = TreeView_GetNextSibling(hwtv, hChild)) {
+
+        if (n_childinfo >= nc_childinfo) {
+            nc_childinfo = UBOUNDSS(n_childinfo + 1,
+                                    CI_ALLOC_INCR, CI_ALLOC_INCR);
+#ifdef DEBUG
+            assert(nc_childinfo > n_childinfo);
+#endif
+            childinfo = PREALLOC(childinfo,
+                                 sizeof(*childinfo) * nc_childinfo);
+#ifdef DEBUG
+            assert(childinfo);
+#endif
+        }
+
+        ZeroMemory(&childinfo[n_childinfo],
+                   sizeof(childinfo[n_childinfo]));
+
+        childinfo[n_childinfo].hItem = hChild;
+        childinfo[n_childinfo].checked = FALSE;
+        n_childinfo++;
+    }
+
+    /* now, go through the list of actual nodes and make sure they
+       match up */
+    child = NULL;
+    for (khui_cfg_get_first_child(c, &child);
+         child;
+         khui_cfg_get_next_release(&child)) {
+
+        hChild = (HTREEITEM) khui_cfg_get_param(child);
+
+        for (i=0; i < n_childinfo; i++) {
+            if (childinfo[i].hItem == hChild)
+                break;
+        }
+
+        if (i < n_childinfo) {
+            childinfo[i].checked = TRUE;
+        } else {
+            /* add it to the list, so we can create the node in the
+               tree view control later. */
+            if (n_childinfo >= nc_childinfo) {
+                nc_childinfo = UBOUNDSS(n_childinfo + 1,
+                                        CI_ALLOC_INCR, CI_ALLOC_INCR);
+#ifdef DEBUG
+                assert(nc_childinfo > n_childinfo);
+#endif
+                childinfo = PREALLOC(childinfo,
+                                     sizeof(*childinfo) * nc_childinfo);
+#ifdef DEBUG
+                assert(childinfo);
+#endif
+            }
+
+            ZeroMemory(&childinfo[n_childinfo],
+                       sizeof(childinfo[n_childinfo]));
+
+            childinfo[n_childinfo].node = child;
+            khui_cfg_hold(child);
+            n_childinfo++;
+        }
+    }
+
+    /* by this point, the childinfo list contains items of the
+       following forms:
+
+       1. childinfo[i].hItem != NULL && childinfo[i].checked == TRUE
+
+          Corresponds to a tree view item that has a matching
+          configuration node.  Nothing to do here.
+
+       2. childinfo[i].hItem != NULL && childinfo[i].checked == FALSE
+
+          Corresponds to a tree view item that has no matching
+          configuration node.  These should be removed.
+
+       3. childinfo[i].hItem == NULL && childinfo[i].node != NULL
+
+          Corresponds to a configuration node that has no matching
+          tree view item.  These nodes should be added.
+    */
+
+    /* first do the removals */
+    for (i=0; i < n_childinfo; i++) {
+        if (childinfo[i].hItem == NULL)
+            break;              /* nothing more to see from this point
+                                   on */
+        if (!childinfo[i].checked) {
+            /* remove! */
+            cfgui_remove_item(hwtv, childinfo[i].hItem);
+        }
+    }
+
+    /* continue from where the previous loop left off */
+    for (; i < n_childinfo; i++) {
+#ifdef DEBUG
+        assert(childinfo[i].hItem == NULL);
+        assert(childinfo[i].node != NULL);
+#endif
+
+        cfgui_add_node(d, hwtv, childinfo[i].node, c, FALSE);
+
+        khui_cfg_release(childinfo[i].node);
+        childinfo[i].node = NULL;
+    }
+
+    if (childinfo)
+        PFREE(childinfo);
+
+    /* finally recurse through to the next level */
+    for (hChild = TreeView_GetChild(hwtv, hItem);
+         hChild;
+         hChild = TreeView_GetNextSibling(hwtv, hChild)) {
+
+        TVITEMEX itemex;
+
+        ZeroMemory(&itemex, sizeof(itemex));
+
+        itemex.mask = TVIF_PARAM;
+        itemex.hItem = hChild;
+
+        TreeView_GetItem(hwtv, &itemex);
+
+        if (itemex.lParam) {
+            child = (khui_config_node) itemex.lParam;
+
+            cfgui_sync_node(d, hwtv, child, hChild);
+        }
+    }
+}
+
+static void
+cfgui_sync_node_list(cfgui_wnd_data * d, HWND hwnd) {
+    HWND hwtv;
+    HTREEITEM hItem;
+
+    hwtv = GetDlgItem(hwnd, IDC_CFG_NODELIST);
+    hItem = TreeView_GetRoot(hwtv);
+
+    cfgui_sync_node(d, hwtv, NULL, hItem);
+}
+
+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, IDAPPLY), TRUE);
+    } else {
+        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);
+
+            SetWindowLongPtr(hwnd, DWLP_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();
+
+        khui_cfg_set_configui_handle(hwnd);
+
+        d = PMALLOC(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);
+
+        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);
+
+        cfgui_set_wnd_data(hwnd, NULL);
+
+        khm_del_dialog(hwnd);
+
+        SetForegroundWindow(khm_hwnd_main);
+
+        PFREE(d);
+
+        return FALSE;
+
+    case WM_NOTIFY:
+        {
+            LPNMHDR lpnm;
+            LPNMTREEVIEW lptv;
+            LPNMTVGETINFOTIP lpgi;
+            khui_config_node node;
+
+            lpnm = (LPNMHDR) lParam;
+
+            switch (lpnm->code) {
+            case TVN_SELCHANGED:
+                lptv = (LPNMTREEVIEW) lParam;
+                cfgui_activate_node(hwnd,
+                                    (khui_config_node) 
+                                    lptv->itemNew.lParam);
+                return TRUE;
+
+            case TVN_GETINFOTIP:
+                lpgi = (LPNMTVGETINFOTIP) lParam;
+                node = (khui_config_node) lpgi->lParam;
+
+                if (node) {
+                    khm_int32 flags = 0;
+
+                    flags = khui_cfg_get_flags(node);
+
+                    if (flags & KHUI_CNFLAG_MODIFIED) {
+                        LoadString(khm_hInstance, IDS_CFG_IT_MOD,
+                                   lpgi->pszText, lpgi->cchTextMax);
+                    } else if (flags & KHUI_CNFLAG_APPLIED) {
+                        LoadString(khm_hInstance, IDS_CFG_IT_APP,
+                                   lpgi->pszText, lpgi->cchTextMax);
+                    } else {
+                        LoadString(khm_hInstance, IDS_CFG_IT_NONE,
+                                   lpgi->pszText, lpgi->cchTextMax);
+                    }
+                } else {
+                    StringCchCopy(lpgi->pszText, lpgi->cchTextMax, L"");
+                }
+
+                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):
+            khm_leave_modal();
+            DestroyWindow(hwnd);
+            break;
+
+        case MAKEWPARAM(IDAPPLY, BN_CLICKED):
+            cfgui_apply_settings(NULL);
+            break;
+
+        case MAKEWPARAM(IDOK, BN_CLICKED):
+            cfgui_apply_settings(NULL);
+            khm_leave_modal();
+            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;
+
+        case WMCFG_SYNC_NODE_LIST:
+            d = cfgui_get_wnd_data(hwnd);
+            cfgui_sync_node_list(d, hwnd);
+            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;
+    khui_config_node cfg_r = NULL;
+    khui_config_node cfg_iter = NULL;
+    khui_menu_def * omenu;
+    khm_boolean refresh_menu = FALSE;
+
+    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)
+            PFREE(idents);
+        idents = PMALLOC(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);
+        }
+    }
+
+    for (khui_cfg_get_first_child(cfg_ids, &cfg_iter);
+         cfg_iter;
+         khui_cfg_get_next_release(&cfg_iter)) {
+
+        wchar_t cfgname[KCDB_IDENT_MAXCCH_NAME];
+        khm_size cb;
+        khm_handle tident = NULL;
+        khm_int32 tflags = 0;
+
+        cb = sizeof(cfgname);
+        khui_cfg_get_name(cfg_iter, cfgname, &cb);
+
+        if (KHM_FAILED(kcdb_identity_create(cfgname, 0, &tident)) ||
+            KHM_FAILED(kcdb_identity_get_flags(tident, &tflags)) ||
+            !(tflags & KCDB_IDENT_FLAG_ACTIVE) ||
+            !(tflags & KCDB_IDENT_FLAG_CONFIG)) {
+
+            /* this configuration node needs to be removed */
+
+            khui_cfg_remove(cfg_iter);
+        }
+
+        if (tident)
+            kcdb_identity_release(tident);
+    }
+
+    /* Now iterate through the root level configuration nodes and make
+       sure we have a menu item for each of them. */
+    if (KHM_FAILED(khui_cfg_get_first_child(NULL, &cfg_r)))
+        goto _cleanup;
+
+    omenu = khui_find_menu(KHUI_MENU_OPTIONS);
+    if (omenu == NULL)
+        goto _cleanup;
+
+    khui_action_lock();
+
+    do {
+        khm_int32 action;
+        khm_int32 flags;
+        khui_action * paction;
+        wchar_t cname[KHUI_MAXCCH_NAME];
+        wchar_t wshort[KHUI_MAXCCH_SHORT_DESC];
+        khm_size cb;
+        khm_handle sub;
+        khui_config_node_reg reg;
+
+        flags = khui_cfg_get_flags(cfg_r);
+        if (flags & KHUI_CNFLAG_SYSTEM)
+            goto _next_cfg;
+
+        cb = sizeof(cname);
+        if (KHM_FAILED(khui_cfg_get_name(cfg_r, cname, &cb))) {
+#ifdef DEBUG
+            assert(FALSE);
+#endif
+            goto _next_cfg;
+        }
+
+        paction = khui_find_named_action(cname);
+
+        if (!paction) {
+            khui_cfg_get_reg(cfg_r, &reg);
+
+            kmq_create_hwnd_subscription(khm_hwnd_main, &sub);
+
+            StringCbCopy(wshort, sizeof(wshort), reg.short_desc);
+            StringCbCat(wshort, sizeof(wshort), L" ...");
+
+            action = khui_action_create(cname,
+                                        wshort,
+                                        reg.long_desc,
+                                        (void *) CFGACTION_MAGIC,
+                                        KHUI_ACTIONTYPE_TRIGGER,
+                                        sub);
+
+            if (action == 0) {
+#ifdef DEBUG
+                assert(FALSE);
+#endif
+                goto _next_cfg;
+            }
+
+            khui_menu_insert_action(omenu, -1, action, 0);
+
+            refresh_menu = TRUE;
+        }
+
+    _next_cfg:
+        if (KHM_FAILED(khui_cfg_get_next_release(&cfg_r)))
+            break;
+    } while(cfg_r);
+
+    khui_action_unlock();
+
+    if (refresh_menu) {
+        khui_refresh_actions();
+    }
+
+ _cleanup:
+    if (cfg_ids)
+        khui_cfg_release(cfg_ids);
+
+    if (cfg_r)
+        khui_cfg_release(cfg_r);
+
+    if (idents)
+        PFREE(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 = KHUI_CNFLAG_SYSTEM;
+
+    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"KhmAppear";
+    reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_APPEAR);
+    reg.dlg_proc = khm_cfg_appearance_proc;
+    LoadString(khm_hInstance, IDS_CFG_APPEAR_SHORT,
+               wshort, ARRAYLENGTH(wshort));
+    LoadString(khm_hInstance, IDS_CFG_APPEAR_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_CNFLAG_SYSTEM;
+
+    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_CNFLAG_SYSTEM;
+
+    khui_cfg_register(node, &reg);
+
+    reg.flags = KHUI_CNFLAG_SYSTEM;
+    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) {
+}
index 693ec7d72abd163bef88ef23b283c18718ff2296..712805fc14f088870e895e85050f31e68a926b4c 100644 (file)
@@ -1,88 +1,88 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_CONFIGWND_H\r
-#define __KHIMAIRA_CONFIGWND_H\r
-\r
-#define CFGACTION_MAGIC 0x38f8\r
-\r
-void \r
-khm_show_config_pane(khui_config_node node);\r
-\r
-void khm_init_config(void);\r
-void khm_exit_config(void);\r
-\r
-void khm_refresh_config(void);\r
-\r
-/* window procedures for other configuration windows */\r
-INT_PTR CALLBACK\r
-khm_cfg_general_proc(HWND hwnd,\r
-                     UINT uMsg,\r
-                     WPARAM wParam,\r
-                     LPARAM lParam);\r
-\r
-INT_PTR CALLBACK\r
-khm_cfg_identities_proc(HWND hwnd,\r
-                        UINT uMsg,\r
-                        WPARAM wParam,\r
-                        LPARAM lParam);\r
-\r
-INT_PTR CALLBACK\r
-khm_cfg_identity_proc(HWND hwnd,\r
-                      UINT uMsg,\r
-                      WPARAM wParam,\r
-                      LPARAM lParam);\r
-\r
-INT_PTR CALLBACK\r
-khm_cfg_id_tab_proc(HWND hwnd,\r
-                    UINT uMsg,\r
-                    WPARAM wParam,\r
-                    LPARAM lParam);\r
-\r
-INT_PTR CALLBACK\r
-khm_cfg_ids_tab_proc(HWND hwnd,\r
-                     UINT uMsg,\r
-                     WPARAM wParam,\r
-                     LPARAM lParam);\r
-\r
-INT_PTR CALLBACK\r
-khm_cfg_notifications_proc(HWND hwnd,\r
-                           UINT uMsg,\r
-                           WPARAM wParam,\r
-                           LPARAM lParam);\r
-\r
-INT_PTR CALLBACK\r
-khm_cfg_plugins_proc(HWND hwnd,\r
-                     UINT uMsg,\r
-                     WPARAM wParam,\r
-                     LPARAM lParam);\r
-\r
-INT_PTR CALLBACK\r
-khm_cfg_appearance_proc(HWND hwnd,\r
-                        UINT uMsg,\r
-                        WPARAM wParam,\r
-                        LPARAM lParam);\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
+
+#define CFGACTION_MAGIC 0x38f8
+
+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);
+
+INT_PTR CALLBACK
+khm_cfg_appearance_proc(HWND hwnd,
+                        UINT uMsg,
+                        WPARAM wParam,
+                        LPARAM lParam);
+#endif
index 2f1ac8033df58b07bd4b05631b2c0b1e7d658011..8eeba14a31b8a2777039dbb2495f5ee47ddbbdb2 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khmapp.h>\r
-#include<assert.h>\r
-\r
-static BOOL in_dialog = FALSE;\r
-static CRITICAL_SECTION cs_dialog;\r
-static HANDLE in_dialog_evt = NULL;\r
-static LONG init_dialog = 0;\r
-static khm_int32 dialog_result = 0;\r
-static wchar_t dialog_identity[KCDB_IDENT_MAXCCH_NAME];\r
-static khui_new_creds * dialog_nc = NULL;\r
-\r
-static void\r
-dialog_sync_init(void) {\r
-    if (InterlockedIncrement(&init_dialog) == 1) {\r
-#ifdef DEBUG\r
-        assert(in_dialog_evt == NULL);\r
-        assert(in_dialog == FALSE);\r
-#endif\r
-\r
-        InitializeCriticalSection(&cs_dialog);\r
-\r
-        in_dialog_evt = CreateEvent(NULL,\r
-                                    TRUE,\r
-                                    TRUE,\r
-                                    L"DialogCompletionEvent");\r
-    } else {\r
-        InterlockedDecrement(&init_dialog);\r
-        if (in_dialog_evt == NULL) {\r
-            Sleep(100);\r
-        }\r
-    }\r
-}\r
-\r
-BOOL \r
-khm_cred_begin_dialog(void) {\r
-    BOOL rv;\r
-\r
-    dialog_sync_init();\r
-\r
-    EnterCriticalSection(&cs_dialog);\r
-\r
-    if (in_dialog) {\r
-        rv = FALSE;\r
-\r
-        /* if a dialog is being displayed and we got a another request\r
-           to show one, we bring the existing one to the\r
-           foreground. */\r
-        if (dialog_nc && dialog_nc->hwnd) {\r
-            khm_int32 t = 0;\r
-\r
-            if (KHM_SUCCEEDED(khc_read_int32(NULL,\r
-                                             L"CredWindow\\Windows\\NewCred\\ForceToTop",\r
-                                             &t)) &&\r
-                t != 0) {\r
-\r
-                khm_activate_main_window();\r
-\r
-                SetWindowPos(dialog_nc->hwnd, HWND_TOP, 0, 0, 0, 0,\r
-                             (SWP_NOMOVE | SWP_NOSIZE));\r
-            }\r
-        }\r
-\r
-    } else {\r
-        rv = TRUE;\r
-        in_dialog = TRUE;\r
-        ResetEvent(in_dialog_evt);\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_dialog);\r
-    return rv;\r
-}\r
-\r
-void \r
-khm_cred_end_dialog(khui_new_creds * nc) {\r
-    dialog_sync_init();\r
-\r
-    EnterCriticalSection(&cs_dialog);\r
-    if (in_dialog) {\r
-        in_dialog = FALSE;\r
-        SetEvent(in_dialog_evt);\r
-    }\r
-    dialog_result = nc->result;\r
-#ifdef DEBUG\r
-    assert(dialog_nc == nc);\r
-#endif\r
-    dialog_nc = NULL;\r
-    if (nc->subtype == KMSG_CRED_NEW_CREDS &&\r
-        nc->n_identities > 0 &&\r
-        nc->identities[0]) {\r
-        khm_size cb;\r
-\r
-        cb = sizeof(dialog_identity);\r
-        if (KHM_FAILED(kcdb_identity_get_name(nc->identities[0],\r
-                                              dialog_identity,\r
-                                              &cb)))\r
-            dialog_identity[0] = 0;\r
-    } else {\r
-        dialog_identity[0] = 0;\r
-    }\r
-    LeaveCriticalSection(&cs_dialog);\r
-}\r
-\r
-BOOL\r
-khm_cred_is_in_dialog(void) {\r
-    BOOL rv;\r
-\r
-    dialog_sync_init();\r
-\r
-    EnterCriticalSection(&cs_dialog);\r
-    rv = in_dialog;\r
-    LeaveCriticalSection(&cs_dialog);\r
-\r
-    return rv;\r
-}\r
-\r
-khm_int32\r
-khm_cred_wait_for_dialog(DWORD timeout, khm_int32 * result,\r
-                         wchar_t * ident, khm_size cb_ident) {\r
-    khm_int32 rv;\r
-\r
-    dialog_sync_init();\r
-\r
-    EnterCriticalSection(&cs_dialog);\r
-    if (!in_dialog)\r
-        rv = KHM_ERROR_NOT_FOUND;\r
-    else {\r
-        DWORD dw;\r
-\r
-        do {\r
-            LeaveCriticalSection(&cs_dialog);\r
-\r
-            dw = WaitForSingleObject(in_dialog_evt, timeout);\r
-\r
-            EnterCriticalSection(&cs_dialog);\r
-\r
-            if (!in_dialog) {\r
-                rv = KHM_ERROR_SUCCESS;\r
-                if (result) {\r
-                    *result = dialog_result;\r
-                }\r
-                if (ident) {\r
-                    StringCbCopy(ident, cb_ident, dialog_identity);\r
-                }\r
-                break;\r
-            } else if(dw == WAIT_TIMEOUT) {\r
-                rv = KHM_ERROR_TIMEOUT;\r
-                break;\r
-            }\r
-        } while(TRUE);\r
-    }\r
-    LeaveCriticalSection(&cs_dialog);\r
-\r
-    return rv;\r
-}\r
-\r
-/* Completion handler for KMSG_CRED messages.  We control the overall\r
-   logic of credentials acquisition and other operations here.  Once a\r
-   credentials operation is triggered, each successive message\r
-   completion notification will be used to dispatch the messages for\r
-   the next step in processing the operation. */\r
-void KHMAPI \r
-kmsg_cred_completion(kmq_message *m)\r
-{\r
-    khui_new_creds * nc;\r
-\r
-#ifdef DEBUG\r
-    assert(m->type == KMSG_CRED);\r
-#else\r
-    if(m->type != KMSG_CRED)\r
-        return; /* huh? */\r
-#endif\r
-\r
-    switch(m->subtype) {\r
-    case KMSG_CRED_PASSWORD:\r
-        /* fallthrough */\r
-    case KMSG_CRED_NEW_CREDS:\r
-        /* Cred types have attached themselves.  Trigger the next\r
-           phase. */\r
-        kmq_post_message(KMSG_CRED, KMSG_CRED_DIALOG_SETUP, 0, \r
-                         m->vparam);\r
-        break;\r
-\r
-    case KMSG_CRED_RENEW_CREDS:\r
-        nc = (khui_new_creds *) m->vparam;\r
-\r
-        /* khm_cred_dispatch_process_message() deals with the case\r
-           where there are no credential types that wants to\r
-           participate in this operation. */\r
-        khm_cred_dispatch_process_message(nc);\r
-        break;\r
-\r
-    case KMSG_CRED_DIALOG_SETUP:\r
-        nc = (khui_new_creds *) m->vparam;\r
-\r
-        khm_prep_newcredwnd(nc->hwnd);\r
-            \r
-        /* all the controls have been created.  Now initialize them */\r
-        if (nc->n_types > 0) {\r
-            kmq_post_subs_msg(nc->type_subs, \r
-                              nc->n_types, \r
-                              KMSG_CRED, \r
-                              KMSG_CRED_DIALOG_PRESTART, \r
-                              0, \r
-                              m->vparam);\r
-        } else {\r
-            PostMessage(nc->hwnd, KHUI_WM_NC_NOTIFY, \r
-                        MAKEWPARAM(0, WMNC_DIALOG_PROCESS_COMPLETE), 0);\r
-        }\r
-        break;\r
-\r
-    case KMSG_CRED_DIALOG_PRESTART:\r
-        /* all prestart stuff is done.  Now to activate the dialog */\r
-        nc = (khui_new_creds *) m->vparam;\r
-        khm_show_newcredwnd(nc->hwnd);\r
-        \r
-        kmq_post_subs_msg(nc->type_subs,\r
-                          nc->n_types,\r
-                          KMSG_CRED, \r
-                          KMSG_CRED_DIALOG_START, \r
-                          0, \r
-                          m->vparam);\r
-        /* at this point, the dialog window takes over.  We let it run\r
-           the show until KMSG_CRED_DIALOG_END is posted by the dialog\r
-           procedure. */\r
-        break;\r
-\r
-    case KMSG_CRED_PROCESS:\r
-        /* a wave of these messages have completed.  We should check\r
-           if there's more */\r
-        nc = (khui_new_creds *) m->vparam;\r
-\r
-        /* if we are done processing all the plug-ins, then check if\r
-           there were any errors reported.  Otherwise we dispatch\r
-           another set of messages. */\r
-        if(!khm_cred_dispatch_process_level(nc)) {\r
-\r
-            if(kherr_is_error()) {\r
-                khui_alert * alert;\r
-                kherr_event * evt;\r
-                kherr_context * ctx;\r
-                wchar_t ws_tfmt[512];\r
-                wchar_t w_idname[KCDB_IDENT_MAXCCH_NAME];\r
-                wchar_t ws_title[ARRAYLENGTH(ws_tfmt) + KCDB_IDENT_MAXCCH_NAME];\r
-                khm_size cb;\r
-\r
-                /* For renewals, we suppress the error message for the\r
-                   following case:\r
-\r
-                   - The renewal was for an identity\r
-\r
-                   - There are no identity credentials for the\r
-                     identity (no credentials that have the same type\r
-                     as the identity provider). */\r
-\r
-                if (nc->subtype == KMSG_CRED_RENEW_CREDS &&\r
-                    nc->ctx.scope == KHUI_SCOPE_IDENT &&\r
-                    nc->ctx.identity != NULL) {\r
-                    khm_handle tcs = NULL; /* credential set */\r
-                    khm_size count = 0;\r
-                    khm_int32 id_ctype = KCDB_CREDTYPE_INVALID;\r
-                    khm_int32 delta = 0;\r
-\r
-                    kcdb_identity_get_type(&id_ctype);\r
-                    kcdb_credset_create(&tcs);\r
-                    kcdb_credset_collect(tcs, NULL,\r
-                                         nc->ctx.identity,\r
-                                         id_ctype,\r
-                                         &delta);\r
-                    kcdb_credset_get_size(tcs, &count);\r
-                    kcdb_credset_delete(tcs);\r
-\r
-                    if (count == 0) {\r
-                        goto done_with_op;\r
-                    }\r
-                }\r
-\r
-                ctx = kherr_peek_context();\r
-                evt = kherr_get_err_event(ctx);\r
-                kherr_evaluate_event(evt);\r
-\r
-                khui_alert_create_empty(&alert);\r
-\r
-                if (nc->subtype == KMSG_CRED_NEW_CREDS) {\r
-\r
-                    khui_alert_set_type(alert, KHUI_ALERTTYPE_ACQUIREFAIL);\r
-\r
-                    cb = sizeof(w_idname);\r
-                    if (nc->n_identities == 0 ||\r
-                        KHM_FAILED(kcdb_identity_get_name(nc->identities[0],\r
-                                                          w_idname, &cb))) {\r
-                        /* an identity could not be determined */\r
-                        LoadString(khm_hInstance, IDS_NC_FAILED_TITLE,\r
-                                   ws_title, ARRAYLENGTH(ws_title));\r
-                    } else {\r
-                        LoadString(khm_hInstance, IDS_NC_FAILED_TITLE_I,\r
-                                   ws_tfmt, ARRAYLENGTH(ws_tfmt));\r
-                        StringCbPrintf(ws_title, sizeof(ws_title),\r
-                                       ws_tfmt, w_idname);\r
-                        khui_alert_set_ctx(alert,\r
-                                           KHUI_SCOPE_IDENT,\r
-                                           nc->identities[0],\r
-                                           KCDB_CREDTYPE_INVALID,\r
-                                           NULL);\r
-                    }\r
-\r
-                } else if (nc->subtype == KMSG_CRED_PASSWORD) {\r
-\r
-                    khui_alert_set_type(alert, KHUI_ALERTTYPE_CHPW);\r
-\r
-                    cb = sizeof(w_idname);\r
-                    if (nc->n_identities == 0 ||\r
-                        KHM_FAILED(kcdb_identity_get_name(nc->identities[0],\r
-                                                          w_idname, &cb))) {\r
-                        LoadString(khm_hInstance, IDS_NC_PWD_FAILED_TITLE,\r
-                                   ws_title, ARRAYLENGTH(ws_title));\r
-                    } else {\r
-                        LoadString(khm_hInstance, IDS_NC_PWD_FAILED_TITLE_I,\r
-                                   ws_tfmt, ARRAYLENGTH(ws_tfmt));\r
-                        StringCbPrintf(ws_title, sizeof(ws_title),\r
-                                       ws_tfmt, w_idname);\r
-                        khui_alert_set_ctx(alert,\r
-                                           KHUI_SCOPE_IDENT,\r
-                                           nc->identities[0],\r
-                                           KCDB_CREDTYPE_INVALID,\r
-                                           NULL);\r
-                    }\r
-\r
-                } else if (nc->subtype == KMSG_CRED_RENEW_CREDS) {\r
-\r
-                    khui_alert_set_type(alert, KHUI_ALERTTYPE_RENEWFAIL);\r
-\r
-                    cb = sizeof(w_idname);\r
-                    if (nc->ctx.identity == NULL ||\r
-                        KHM_FAILED(kcdb_identity_get_name(nc->ctx.identity,\r
-                                                          w_idname, &cb))) {\r
-                        LoadString(khm_hInstance, IDS_NC_REN_FAILED_TITLE,\r
-                                   ws_title, ARRAYLENGTH(ws_title));\r
-                    } else {\r
-                        LoadString(khm_hInstance, IDS_NC_REN_FAILED_TITLE_I,\r
-                                   ws_tfmt, ARRAYLENGTH(ws_tfmt));\r
-                        StringCbPrintf(ws_title, sizeof(ws_title),\r
-                                       ws_tfmt, w_idname);\r
-                        khui_alert_set_ctx(alert,\r
-                                           KHUI_SCOPE_IDENT,\r
-                                           nc->ctx.identity,\r
-                                           KCDB_CREDTYPE_INVALID,\r
-                                           NULL);\r
-                    }\r
-\r
-                } else {\r
-#ifdef DEBUG\r
-                    assert(FALSE);\r
-#endif\r
-                }\r
-\r
-                khui_alert_set_title(alert, ws_title);\r
-                khui_alert_set_severity(alert, evt->severity);\r
-\r
-                if(!evt->long_desc)\r
-                    khui_alert_set_message(alert, evt->short_desc);\r
-                else\r
-                    khui_alert_set_message(alert, evt->long_desc);\r
-\r
-                if(evt->suggestion)\r
-                    khui_alert_set_suggestion(alert, evt->suggestion);\r
-\r
-                if (nc->subtype == KMSG_CRED_RENEW_CREDS &&\r
-                    nc->ctx.identity != NULL) {\r
-\r
-                    khm_int32 n_cmd;\r
-\r
-                    n_cmd = khm_get_identity_new_creds_action(nc->ctx.identity);\r
-\r
-                    if (n_cmd != 0) {\r
-                        khui_alert_add_command(alert, n_cmd);\r
-                        khui_alert_add_command(alert, KHUI_PACTION_CLOSE);\r
-\r
-                        khui_alert_set_flags(alert, KHUI_ALERT_FLAG_DISPATCH_CMD,\r
-                                             KHUI_ALERT_FLAG_DISPATCH_CMD);\r
-                    }\r
-                }\r
-\r
-                khui_alert_show(alert);\r
-                khui_alert_release(alert);\r
-\r
-                kherr_release_context(ctx);\r
-\r
-                kherr_clear_error();\r
-            }\r
-\r
-        done_with_op:\r
-\r
-            if (nc->subtype == KMSG_CRED_RENEW_CREDS) {\r
-                kmq_post_message(KMSG_CRED, KMSG_CRED_END, 0, \r
-                                 m->vparam);\r
-            } else {\r
-                PostMessage(nc->hwnd, KHUI_WM_NC_NOTIFY, \r
-                            MAKEWPARAM(0, WMNC_DIALOG_PROCESS_COMPLETE),\r
-                            0);\r
-            }\r
-        }\r
-        break;\r
-\r
-    case KMSG_CRED_END:\r
-        /* all is done. */\r
-        {\r
-            khui_new_creds * nc;\r
-            khm_boolean continue_cmdline = TRUE;\r
-\r
-            nc = (khui_new_creds *) m->vparam;\r
-\r
-            if (nc->subtype == KMSG_CRED_NEW_CREDS ||\r
-                nc->subtype == KMSG_CRED_PASSWORD) {\r
-\r
-                khm_cred_end_dialog(nc);\r
-\r
-            } else if (nc->subtype == KMSG_CRED_RENEW_CREDS) {\r
-\r
-                /* if this is a renewal that was triggered while we\r
-                   were processing the commandline, then we need to\r
-                   update the pending renewal count. */\r
-\r
-                if (khm_startup.processing) {\r
-                    LONG renewals;\r
-                    renewals = InterlockedDecrement(&khm_startup.pending_renewals);\r
-\r
-                    if (renewals != 0) {\r
-                        continue_cmdline = FALSE;\r
-                    }\r
-                }\r
-            }\r
-\r
-            khui_cw_destroy_cred_blob(nc);\r
-\r
-            kmq_post_message(KMSG_CRED, KMSG_CRED_REFRESH, 0, 0);\r
-\r
-            if (continue_cmdline)\r
-                kmq_post_message(KMSG_ACT, KMSG_ACT_CONTINUE_CMDLINE, 0, 0);\r
-        }\r
-        break;\r
-\r
-        /* property sheet stuff */\r
-\r
-    case KMSG_CRED_PP_BEGIN:\r
-        /* all the pages should have been added by now.  Just send out\r
-           the precreate message */\r
-        kmq_post_message(KMSG_CRED, KMSG_CRED_PP_PRECREATE, 0, \r
-                         m->vparam);\r
-        break;\r
-\r
-    case KMSG_CRED_PP_END:\r
-        kmq_post_message(KMSG_CRED, KMSG_CRED_PP_DESTROY, 0, \r
-                         m->vparam);\r
-        break;\r
-\r
-    case KMSG_CRED_DESTROY_CREDS:\r
-#ifdef DEBUG\r
-        assert(m->vparam != NULL);\r
-#endif\r
-        khui_context_release((khui_action_context *) m->vparam);\r
-        PFREE(m->vparam);\r
-\r
-        kmq_post_message(KMSG_CRED, KMSG_CRED_REFRESH, 0, 0);\r
-\r
-        kmq_post_message(KMSG_ACT, KMSG_ACT_CONTINUE_CMDLINE, 0, 0);\r
-        break;\r
-\r
-    case KMSG_CRED_IMPORT:\r
-        {\r
-            khm_boolean continue_cmdline = FALSE;\r
-            LONG pending_renewals;\r
-\r
-            /* once an import operation ends, we have to trigger a\r
-               renewal so that other plug-ins that didn't participate\r
-               in the import operation can have a chance at getting\r
-               the necessary credentials.\r
-\r
-               If we are in the middle of processing the commandline,\r
-               we have to be a little bit careful.  We can't issue a\r
-               commandline conituation message right now because the\r
-               import action is still ongoing (since the renewals are\r
-               part of the action).  Once the renewals have completed,\r
-               the completion handler will automatically issue a\r
-               commandline continuation message.  However, if there\r
-               were no identities to renew, then we have to issue the\r
-               message ourselves.\r
-            */\r
-\r
-            InterlockedIncrement(&khm_startup.pending_renewals);\r
-\r
-            khm_cred_renew_all_identities();\r
-\r
-            pending_renewals = InterlockedDecrement(&khm_startup.pending_renewals);\r
-\r
-            if (pending_renewals == 0 && khm_startup.processing)\r
-                kmq_post_message(KMSG_ACT, KMSG_ACT_CONTINUE_CMDLINE, 0, 0);\r
-        }\r
-        break;\r
-\r
-    case KMSG_CRED_REFRESH:\r
-        kcdb_identity_refresh_all();\r
-        break;\r
-    }\r
-}\r
-\r
-void khm_cred_import(void)\r
-{\r
-    _begin_task(KHERR_CF_TRANSITIVE);\r
-    _report_sr0(KHERR_NONE, IDS_CTX_IMPORT);\r
-    _describe();\r
-\r
-    kmq_post_message(KMSG_CRED, KMSG_CRED_IMPORT, 0, 0);\r
-\r
-    _end_task();\r
-}\r
-\r
-void khm_cred_set_default(void)\r
-{\r
-    khui_action_context ctx;\r
-    khm_int32 rv;\r
-\r
-    khui_context_get(&ctx);\r
-\r
-    if (ctx.identity) {\r
-        rv = kcdb_identity_set_default(ctx.identity);\r
-    }\r
-\r
-    khui_context_release(&ctx);\r
-}\r
-\r
-void khm_cred_destroy_creds(khm_boolean sync, khm_boolean quiet)\r
-{\r
-    khui_action_context * pctx;\r
-\r
-    pctx = PMALLOC(sizeof(*pctx));\r
-#ifdef DEBUG\r
-    assert(pctx);\r
-#endif\r
-\r
-    khui_context_get(pctx);\r
-\r
-    if(pctx->scope == KHUI_SCOPE_NONE && !quiet) {\r
-        /* this really shouldn't be necessary once we start enabling\r
-           and disbling actions based on context */\r
-        wchar_t title[256];\r
-        wchar_t message[256];\r
-\r
-        LoadString(khm_hInstance, \r
-                   IDS_ALERT_NOSEL_TITLE, \r
-                   title, \r
-                   ARRAYLENGTH(title));\r
-\r
-        LoadString(khm_hInstance, \r
-                   IDS_ALERT_NOSEL, \r
-                   message, \r
-                   ARRAYLENGTH(message));\r
-\r
-        khui_alert_show_simple(title, \r
-                               message, \r
-                               KHERR_WARNING);\r
-\r
-        khui_context_release(pctx);\r
-        PFREE(pctx);\r
-\r
-        return;\r
-    }\r
-\r
-    _begin_task(KHERR_CF_TRANSITIVE);\r
-    _report_sr0(KHERR_NONE, IDS_CTX_DESTROY_CREDS);\r
-    _describe();\r
-\r
-    if (sync)\r
-        kmq_send_message(KMSG_CRED,\r
-                         KMSG_CRED_DESTROY_CREDS,\r
-                         0,\r
-                         (void *) pctx);\r
-    else\r
-        kmq_post_message(KMSG_CRED,\r
-                         KMSG_CRED_DESTROY_CREDS,\r
-                         0,\r
-                         (void *) pctx);\r
-\r
-    _end_task();\r
-}\r
-\r
-void khm_cred_destroy_identity(khm_handle identity)\r
-{\r
-    khui_action_context * pctx;\r
-    wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-    khm_size cb;\r
-\r
-    if (identity == NULL)\r
-        return;\r
-\r
-    pctx = PMALLOC(sizeof(*pctx));\r
-#ifdef DEBUG\r
-    assert(pctx);\r
-#endif\r
-\r
-    khui_context_create(pctx,\r
-                        KHUI_SCOPE_IDENT,\r
-                        identity,\r
-                        KCDB_CREDTYPE_INVALID,\r
-                        NULL);\r
-\r
-    cb = sizeof(idname);\r
-    kcdb_identity_get_name(identity, idname, &cb);\r
-\r
-    _begin_task(KHERR_CF_TRANSITIVE);\r
-    _report_sr1(KHERR_NONE, IDS_CTX_DESTROY_ID, _dupstr(idname));\r
-    _describe();\r
-\r
-    kmq_post_message(KMSG_CRED,\r
-                     KMSG_CRED_DESTROY_CREDS,\r
-                     0,\r
-                     (void *) pctx);\r
-\r
-    _end_task();\r
-}\r
-\r
-void khm_cred_renew_all_identities(void)\r
-{\r
-    khm_size count;\r
-    khm_size cb = 0;\r
-    khm_size n_idents = 0;\r
-    khm_int32 rv;\r
-    wchar_t * ident_names = NULL;\r
-    wchar_t * this_ident;\r
-\r
-    kcdb_credset_get_size(NULL, &count);\r
-\r
-    /* if there are no credentials, we just skip over the renew\r
-       action. */\r
-\r
-    if (count == 0)\r
-        return;\r
-\r
-    ident_names = NULL;\r
-\r
-    while (TRUE) {\r
-        if (ident_names) {\r
-            PFREE(ident_names);\r
-            ident_names = NULL;\r
-        }\r
-\r
-        cb = 0;\r
-        rv = kcdb_identity_enum(KCDB_IDENT_FLAG_EMPTY, 0,\r
-                                NULL,\r
-                                &cb, &n_idents);\r
-\r
-        if (n_idents == 0 || rv != KHM_ERROR_TOO_LONG ||\r
-            cb == 0)\r
-            break;\r
-\r
-        ident_names = PMALLOC(cb);\r
-        ident_names[0] = L'\0';\r
-\r
-        rv = kcdb_identity_enum(KCDB_IDENT_FLAG_EMPTY, 0,\r
-                                ident_names,\r
-                                &cb, &n_idents);\r
-\r
-        if (KHM_SUCCEEDED(rv))\r
-            break;\r
-    }\r
-\r
-    if (ident_names) {\r
-        for (this_ident = ident_names;\r
-             this_ident && *this_ident;\r
-             this_ident = multi_string_next(this_ident)) {\r
-            khm_handle ident;\r
-\r
-            if (KHM_FAILED(kcdb_identity_create(this_ident, 0,\r
-                                                &ident)))\r
-                continue;\r
-\r
-            khm_cred_renew_identity(ident);\r
-\r
-            kcdb_identity_release(ident);\r
-        }\r
-\r
-        PFREE(ident_names);\r
-        ident_names = NULL;\r
-    }\r
-}\r
-\r
-void khm_cred_renew_identity(khm_handle identity)\r
-{\r
-    khui_new_creds * c;\r
-\r
-    khui_cw_create_cred_blob(&c);\r
-\r
-    c->subtype = KMSG_CRED_RENEW_CREDS;\r
-    c->result = KHUI_NC_RESULT_PROCESS;\r
-    khui_context_create(&c->ctx,\r
-                        KHUI_SCOPE_IDENT,\r
-                        identity,\r
-                        KCDB_CREDTYPE_INVALID,\r
-                        NULL);\r
-\r
-    _begin_task(KHERR_CF_TRANSITIVE);\r
-    _report_sr0(KHERR_NONE, IDS_CTX_RENEW_CREDS);\r
-    _describe();\r
-\r
-    /* if we are calling this while processing startup actions, we\r
-       need to keep track of how many we have issued. */\r
-    if (khm_startup.processing) {\r
-        InterlockedIncrement(&khm_startup.pending_renewals);\r
-    }\r
-\r
-    kmq_post_message(KMSG_CRED, KMSG_CRED_RENEW_CREDS, 0, (void *) c);\r
-\r
-    _end_task();\r
-}\r
-\r
-void khm_cred_renew_cred(khm_handle cred)\r
-{\r
-    khui_new_creds * c;\r
-\r
-    khui_cw_create_cred_blob(&c);\r
-\r
-    c->subtype = KMSG_CRED_RENEW_CREDS;\r
-    c->result = KHUI_NC_RESULT_PROCESS;\r
-    khui_context_create(&c->ctx,\r
-                        KHUI_SCOPE_CRED,\r
-                        NULL,\r
-                        KCDB_CREDTYPE_INVALID,\r
-                        cred);\r
-\r
-    _begin_task(KHERR_CF_TRANSITIVE);\r
-    _report_sr0(KHERR_NONE, IDS_CTX_RENEW_CREDS);\r
-    _describe();\r
-\r
-    /* if we are calling this while processing startup actions, we\r
-       need to keep track of how many we have issued. */\r
-    if (khm_startup.processing) {\r
-        InterlockedIncrement(&khm_startup.pending_renewals);\r
-    }\r
-\r
-    kmq_post_message(KMSG_CRED, KMSG_CRED_RENEW_CREDS, 0, (void *) c);\r
-\r
-    _end_task();\r
-}\r
-\r
-void khm_cred_renew_creds(void)\r
-{\r
-    khui_new_creds * c;\r
-\r
-    khui_cw_create_cred_blob(&c);\r
-    c->subtype = KMSG_CRED_RENEW_CREDS;\r
-    c->result = KHUI_NC_RESULT_PROCESS;\r
-    khui_context_get(&c->ctx);\r
-\r
-    _begin_task(KHERR_CF_TRANSITIVE);\r
-    _report_sr0(KHERR_NONE, IDS_CTX_RENEW_CREDS);\r
-    _describe();\r
-\r
-    /* if we are calling this while processing startup actions, we\r
-       need to keep track of how many we have issued. */\r
-    if (khm_startup.processing) {\r
-        InterlockedIncrement(&khm_startup.pending_renewals);\r
-    }\r
-\r
-    kmq_post_message(KMSG_CRED, KMSG_CRED_RENEW_CREDS, 0, (void *) c);\r
-\r
-    _end_task();\r
-}\r
-\r
-void khm_cred_change_password(wchar_t * title)\r
-{\r
-    khui_new_creds * nc;\r
-    LPNETID_DLGINFO pdlginfo;\r
-    khm_size cb;\r
-\r
-    if (!khm_cred_begin_dialog())\r
-        return;\r
-\r
-    khui_cw_create_cred_blob(&nc);\r
-    nc->subtype = KMSG_CRED_PASSWORD;\r
-    dialog_nc = nc;\r
-\r
-    khui_context_get(&nc->ctx);\r
-\r
-    kcdb_identpro_get_ui_cb((void *) &nc->ident_cb);\r
-\r
-    assert(nc->ident_cb);\r
-\r
-    if (title) {\r
-\r
-        if (SUCCEEDED(StringCbLength(title, KHUI_MAXCB_TITLE, &cb))) {\r
-            cb += sizeof(wchar_t);\r
-\r
-            nc->window_title = PMALLOC(cb);\r
-#ifdef DEBUG\r
-            assert(nc->window_title);\r
-#endif\r
-            StringCbCopy(nc->window_title, cb, title);\r
-        }\r
-    } else if (nc->ctx.cb_vparam == sizeof(NETID_DLGINFO) &&\r
-               (pdlginfo = nc->ctx.vparam) &&\r
-               pdlginfo->size == NETID_DLGINFO_V1_SZ &&\r
-               pdlginfo->in.title[0] &&\r
-               SUCCEEDED(StringCchLength(pdlginfo->in.title,\r
-                                         NETID_TITLE_SZ,\r
-                                         &cb))) {\r
-\r
-        cb = (cb + 1) * sizeof(wchar_t);\r
-        nc->window_title = PMALLOC(cb);\r
-#ifdef DEBUG\r
-        assert(nc->window_title);\r
-#endif\r
-        StringCbCopy(nc->window_title, cb, pdlginfo->in.title);\r
-    }\r
-\r
-    khm_create_newcredwnd(khm_hwnd_main, nc);\r
-\r
-    if (nc->hwnd != NULL) {\r
-        _begin_task(KHERR_CF_TRANSITIVE);\r
-        _report_sr0(KHERR_NONE, IDS_CTX_PASSWORD);\r
-        _describe();\r
-\r
-        kmq_post_message(KMSG_CRED, KMSG_CRED_PASSWORD, 0,\r
-                         (void *) nc);\r
-\r
-        _end_task();\r
-    } else {\r
-        khui_cw_destroy_cred_blob(nc);\r
-    }\r
-}\r
-\r
-void\r
-khm_cred_obtain_new_creds_for_ident(khm_handle ident, wchar_t * title)\r
-{\r
-    khui_action_context ctx;\r
-\r
-    if (ident == NULL)\r
-        khm_cred_obtain_new_creds(title);\r
-\r
-    khui_context_get(&ctx);\r
-\r
-    khui_context_set(KHUI_SCOPE_IDENT,\r
-                     ident,\r
-                     KCDB_CREDTYPE_INVALID,\r
-                     NULL,\r
-                     NULL,\r
-                     0,\r
-                     NULL);\r
-\r
-    khm_cred_obtain_new_creds(title);\r
-\r
-    khui_context_set_indirect(&ctx);\r
-\r
-    khui_context_release(&ctx);\r
-}\r
-\r
-void khm_cred_obtain_new_creds(wchar_t * title)\r
-{\r
-    khui_new_creds * nc;\r
-    LPNETID_DLGINFO pdlginfo;\r
-    khm_size cb;\r
-\r
-    if (!khm_cred_begin_dialog())\r
-        return;\r
-\r
-    khui_cw_create_cred_blob(&nc);\r
-    nc->subtype = KMSG_CRED_NEW_CREDS;\r
-    dialog_nc = nc;\r
-\r
-    khui_context_get(&nc->ctx);\r
-\r
-    kcdb_identpro_get_ui_cb((void *) &nc->ident_cb);\r
-\r
-    if (nc->ident_cb == NULL) {\r
-        wchar_t title[256];\r
-        wchar_t msg[512];\r
-        wchar_t suggestion[512];\r
-        khui_alert * a;\r
-\r
-        LoadString(khm_hInstance, IDS_ERR_TITLE_NO_IDENTPRO,\r
-                   title, ARRAYLENGTH(title));\r
-        LoadString(khm_hInstance, IDS_ERR_MSG_NO_IDENTPRO,\r
-                   msg, ARRAYLENGTH(msg));\r
-        LoadString(khm_hInstance, IDS_ERR_SUGG_NO_IDENTPRO,\r
-                   suggestion, ARRAYLENGTH(suggestion));\r
-\r
-        khui_alert_create_simple(title,\r
-                                 msg,\r
-                                 KHERR_ERROR,\r
-                                 &a);\r
-        khui_alert_set_suggestion(a, suggestion);\r
-\r
-        khui_alert_show(a);\r
-\r
-        khui_alert_release(a);\r
-\r
-        khui_context_release(&nc->ctx);\r
-        nc->result = KHUI_NC_RESULT_CANCEL;\r
-        khm_cred_end_dialog(nc);\r
-        khui_cw_destroy_cred_blob(nc);\r
-        return;\r
-    }\r
-\r
-    if (title) {\r
-        if (SUCCEEDED(StringCbLength(title, KHUI_MAXCB_TITLE, &cb))) {\r
-            cb += sizeof(wchar_t);\r
-\r
-            nc->window_title = PMALLOC(cb);\r
-#ifdef DEBUG\r
-            assert(nc->window_title);\r
-#endif\r
-            StringCbCopy(nc->window_title, cb, title);\r
-        }\r
-    } else if (nc->ctx.cb_vparam == sizeof(NETID_DLGINFO) &&\r
-               (pdlginfo = nc->ctx.vparam) &&\r
-               pdlginfo->size == NETID_DLGINFO_V1_SZ &&\r
-               pdlginfo->in.title[0] &&\r
-               SUCCEEDED(StringCchLength(pdlginfo->in.title,\r
-                                         NETID_TITLE_SZ,\r
-                                         &cb))) {\r
-\r
-        cb = (cb + 1) * sizeof(wchar_t);\r
-        nc->window_title = PMALLOC(cb);\r
-#ifdef DEBUG\r
-        assert(nc->window_title);\r
-#endif\r
-        StringCbCopy(nc->window_title, cb, pdlginfo->in.title);\r
-    }\r
-\r
-    khm_create_newcredwnd(khm_hwnd_main, nc);\r
-\r
-    if (nc->hwnd != NULL) {\r
-        _begin_task(KHERR_CF_TRANSITIVE);\r
-        _report_sr0(KHERR_NONE, IDS_CTX_NEW_CREDS);\r
-        _describe();\r
-\r
-        kmq_post_message(KMSG_CRED, KMSG_CRED_NEW_CREDS, 0, \r
-                         (void *) nc);\r
-\r
-        _end_task();\r
-    } else {\r
-        khui_context_release(&nc->ctx);\r
-        nc->result = KHUI_NC_RESULT_CANCEL;\r
-        khm_cred_end_dialog(nc);\r
-        khui_cw_destroy_cred_blob(nc);\r
-    }\r
-}\r
-\r
-/* this is called by khm_cred_dispatch_process_message and the\r
-   kmsg_cred_completion to initiate and continue checked broadcasts of\r
-   KMSG_CRED_DIALOG_PROCESS messages.\r
-   \r
-   Returns TRUE if more KMSG_CRED_DIALOG_PROCESS messages were\r
-   posted. */\r
-BOOL khm_cred_dispatch_process_level(khui_new_creds *nc)\r
-{\r
-    khm_size i,j;\r
-    khm_handle subs[KHUI_MAX_NCTYPES];\r
-    int n_subs = 0;\r
-    BOOL cont = FALSE;\r
-    khui_new_creds_by_type *t, *d;\r
-\r
-    /* at each level, we dispatch a wave of notifications to plug-ins\r
-       who's dependencies are all satisfied */\r
-    EnterCriticalSection(&nc->cs);\r
-\r
-    /* if any types have already completed, we mark them are processed\r
-       and skip them */\r
-    for (i=0; i < nc->n_types; i++) {\r
-        t = nc->types[i];\r
-        if(t->flags & KHUI_NC_RESPONSE_COMPLETED)\r
-            t->flags |= KHUI_NCT_FLAG_PROCESSED;\r
-    }\r
-\r
-    for(i=0; i<nc->n_types; i++) {\r
-        t = nc->types[i];\r
-\r
-        if((t->flags & KHUI_NCT_FLAG_PROCESSED) ||\r
-           (t->flags & KHUI_NC_RESPONSE_COMPLETED))\r
-            continue;\r
-\r
-        for(j=0; j<t->n_type_deps; j++) {\r
-            if(KHM_FAILED(khui_cw_find_type(nc, t->type_deps[j], &d)))\r
-                break;\r
-\r
-            if(!(d->flags & KHUI_NC_RESPONSE_COMPLETED))\r
-                break;\r
-        }\r
-\r
-        if(j<t->n_type_deps) /* there are unmet dependencies */\r
-            continue;\r
-\r
-        /* all dependencies for this type have been met. */\r
-        subs[n_subs++] = kcdb_credtype_get_sub(t->type);\r
-        t->flags |= KHUI_NCT_FLAG_PROCESSED;\r
-        cont = TRUE;\r
-    }\r
-\r
-    LeaveCriticalSection(&nc->cs);\r
-\r
-    /* the reason why we are posting messages in batches is because\r
-       when the message has completed we know that all the types that\r
-       have the KHUI_NCT_FLAG_PROCESSED set have completed processing.\r
-       Otherwise we have to individually track each message and update\r
-       the type */\r
-    if(n_subs > 0)\r
-        kmq_post_subs_msg(subs, n_subs, KMSG_CRED, KMSG_CRED_PROCESS, 0,\r
-                          (void *) nc);\r
-\r
-    return cont;\r
-}\r
-\r
-void \r
-khm_cred_dispatch_process_message(khui_new_creds *nc)\r
-{\r
-    khm_size i;\r
-    BOOL pending;\r
-    wchar_t wsinsert[512];\r
-    khm_size cbsize;\r
-\r
-    /* see if there's anything to do.  We can check this without\r
-       obtaining a lock */\r
-    if(nc->n_types == 0 ||\r
-       (nc->subtype == KMSG_CRED_NEW_CREDS &&\r
-        nc->n_identities == 0) ||\r
-       (nc->subtype == KMSG_CRED_PASSWORD &&\r
-        nc->n_identities == 0))\r
-        goto _terminate_job;\r
-\r
-    /* check dependencies and stuff first */\r
-    EnterCriticalSection(&nc->cs);\r
-    for(i=0; i<nc->n_types; i++) {\r
-        nc->types[i]->flags &= ~ KHUI_NCT_FLAG_PROCESSED;\r
-    }\r
-    LeaveCriticalSection(&nc->cs);\r
-\r
-    /* Consindering all that can go wrong here and the desire to\r
-       handle errors here separately from others, we create a new task\r
-       for the purpose of tracking the credentials acquisition\r
-       process. */\r
-    _begin_task(KHERR_CF_TRANSITIVE);\r
-\r
-    /* Describe the context */\r
-    if(nc->subtype == KMSG_CRED_NEW_CREDS) {\r
-        cbsize = sizeof(wsinsert);\r
-        kcdb_identity_get_name(nc->identities[0], wsinsert, &cbsize);\r
-\r
-        _report_sr1(KHERR_NONE,  IDS_CTX_PROC_NEW_CREDS,\r
-                    _cstr(wsinsert));\r
-        _resolve();\r
-    } else if (nc->subtype == KMSG_CRED_RENEW_CREDS) {\r
-        cbsize = sizeof(wsinsert);\r
-\r
-        if (nc->ctx.scope == KHUI_SCOPE_IDENT)\r
-            kcdb_identity_get_name(nc->ctx.identity, wsinsert, &cbsize);\r
-        else if (nc->ctx.scope == KHUI_SCOPE_CREDTYPE) {\r
-            if (nc->ctx.identity != NULL)\r
-                kcdb_identity_get_name(nc->ctx.identity, wsinsert, \r
-                                       &cbsize);\r
-            else\r
-                kcdb_credtype_get_name(nc->ctx.cred_type, wsinsert,\r
-                                       &cbsize);\r
-        } else if (nc->ctx.scope == KHUI_SCOPE_CRED) {\r
-            kcdb_cred_get_name(nc->ctx.cred, wsinsert, &cbsize);\r
-        } else {\r
-            StringCbCopy(wsinsert, sizeof(wsinsert), L"(?)");\r
-        }\r
-\r
-        _report_sr1(KHERR_NONE, IDS_CTX_PROC_RENEW_CREDS, \r
-                    _cstr(wsinsert));\r
-        _resolve();\r
-    } else if (nc->subtype == KMSG_CRED_PASSWORD) {\r
-        cbsize = sizeof(wsinsert);\r
-        kcdb_identity_get_name(nc->identities[0], wsinsert, &cbsize);\r
-\r
-        _report_sr1(KHERR_NONE, IDS_CTX_PROC_PASSWORD,\r
-                    _cstr(wsinsert));\r
-        _resolve();\r
-    } else {\r
-        assert(FALSE);\r
-    }\r
-\r
-    _describe();\r
-\r
-    pending = khm_cred_dispatch_process_level(nc);\r
-\r
-    _end_task();\r
-\r
-    if(!pending)\r
-        goto _terminate_job;\r
-\r
-    return;\r
-\r
- _terminate_job:\r
-    if (nc->subtype == KMSG_CRED_RENEW_CREDS)\r
-        kmq_post_message(KMSG_CRED, KMSG_CRED_END, 0, (void *) nc);\r
-    else\r
-        PostMessage(nc->hwnd, KHUI_WM_NC_NOTIFY, \r
-                    MAKEWPARAM(0, WMNC_DIALOG_PROCESS_COMPLETE), 0);\r
-}\r
-\r
-void\r
-khm_cred_process_startup_actions(void) {\r
-    khm_handle defident = NULL;\r
-\r
-    if (!khm_startup.processing)\r
-        return;\r
-\r
-    if (khm_startup.init ||\r
-        khm_startup.renew ||\r
-        khm_startup.destroy ||\r
-        khm_startup.autoinit) {\r
-        kcdb_identity_get_default(&defident);\r
-    }\r
-\r
-    /* For asynchronous actions, we trigger the action and then exit\r
-       the loop.  Once the action completes, the completion handler\r
-       will trigger a continuation message which will result in this\r
-       function getting called again.  Then we can proceed with the\r
-       rest of the startup actions. */\r
-    do {\r
-        if (khm_startup.init) {\r
-            if (defident)\r
-                khui_context_set(KHUI_SCOPE_IDENT,\r
-                                 defident,\r
-                                 KCDB_CREDTYPE_INVALID,\r
-                                 NULL, NULL, 0,\r
-                                 NULL);\r
-            else\r
-                khui_context_reset();\r
-\r
-            khm_cred_obtain_new_creds(NULL);\r
-            khm_startup.init = FALSE;\r
-            break;\r
-        }\r
-\r
-        if (khm_startup.import) {\r
-            khm_cred_import();\r
-            khm_startup.import = FALSE;\r
-\r
-            /* we also set the renew command to false here because we\r
-               trigger a renewal for all the identities at the end of\r
-               the import operation anyway. */\r
-            khm_startup.renew = FALSE;\r
-            break;\r
-        }\r
-\r
-        if (khm_startup.renew) {\r
-            LONG pending_renewals;\r
-\r
-            /* if there are no credentials, we just skip over the\r
-               renew action. */\r
-\r
-            khm_startup.renew = FALSE;\r
-\r
-            InterlockedIncrement(&khm_startup.pending_renewals);\r
-\r
-            khm_cred_renew_all_identities();\r
-\r
-            pending_renewals = InterlockedDecrement(&khm_startup.pending_renewals);\r
-\r
-            if (pending_renewals != 0)\r
-                break;\r
-\r
-            /* if there were no pending renewals, then we just fall\r
-               through. This means that either there were no\r
-               identities to renew, or all the renewals completed.  If\r
-               all the renewals completed, then the commandline\r
-               contiuation message wasn't triggered.  Either way, we\r
-               must fall through if the count is zero. */\r
-        }\r
-\r
-        if (khm_startup.destroy) {\r
-\r
-            khm_startup.destroy = FALSE;\r
-\r
-            if (defident) {\r
-                khui_context_set(KHUI_SCOPE_IDENT,\r
-                                 defident,\r
-                                 KCDB_CREDTYPE_INVALID,\r
-                                 NULL, NULL, 0,\r
-                                 NULL);\r
-\r
-                khm_cred_destroy_creds(FALSE, FALSE);\r
-                break;\r
-            }\r
-        }\r
-\r
-        if (khm_startup.autoinit) {\r
-            khm_size count = 0;\r
-            khm_handle credset = NULL;\r
-            khm_int32 ctype_ident = KCDB_CREDTYPE_INVALID;\r
-            khm_int32 delta = 0;\r
-\r
-            khm_startup.autoinit = FALSE;\r
-\r
-            kcdb_credset_create(&credset);\r
-            kcdb_identity_get_type(&ctype_ident);\r
-\r
-            kcdb_credset_collect(credset, NULL,\r
-                                 defident, ctype_ident,\r
-                                 &delta);\r
-\r
-            kcdb_credset_get_size(credset, &count);\r
-\r
-            kcdb_credset_delete(credset);\r
-\r
-            if (count == 0) {\r
-                if (defident)\r
-                    khui_context_set(KHUI_SCOPE_IDENT,\r
-                                     defident,\r
-                                     KCDB_CREDTYPE_INVALID,\r
-                                     NULL, NULL, 0,\r
-                                     NULL);\r
-                else\r
-                    khui_context_reset();\r
-\r
-                khm_cred_obtain_new_creds(NULL);\r
-                break;\r
-            }\r
-        }\r
-\r
-        if (khm_startup.exit) {\r
-            PostMessage(khm_hwnd_main,\r
-                        WM_COMMAND,\r
-                        MAKEWPARAM(KHUI_ACTION_EXIT, 0), 0);\r
-            khm_startup.exit = FALSE;\r
-            break;\r
-        }\r
-\r
-        /* when we get here, then we are all done with the command\r
-           line stuff */\r
-        khm_startup.processing = FALSE;\r
-        khm_startup.remote = FALSE;\r
-\r
-        kmq_post_message(KMSG_ACT, KMSG_ACT_END_CMDLINE, 0, 0);\r
-    } while(FALSE);\r
-\r
-    if (defident)\r
-        kcdb_identity_release(defident);\r
-}\r
-\r
-void\r
-khm_cred_begin_startup_actions(void) {\r
-    khm_handle csp_cw;\r
-\r
-    if (khm_startup.seen)\r
-        return;\r
-\r
-    if (!khm_startup.remote &&\r
-        KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow", 0, &csp_cw))) {\r
-\r
-        khm_int32 t = 0;\r
-\r
-        khc_read_int32(csp_cw, L"Autoinit", &t);\r
-        if (t)\r
-            khm_startup.autoinit = TRUE;\r
-\r
-        t = 0;\r
-        khc_read_int32(csp_cw, L"AutoImport", &t);\r
-        if (t)\r
-            khm_startup.import = TRUE;\r
-\r
-        khc_close_space(csp_cw);\r
-\r
-    }\r
-\r
-    khm_startup.seen = TRUE;\r
-    khm_startup.processing = TRUE;\r
-\r
-    khm_cred_process_startup_actions();\r
-}\r
-\r
-void\r
-khm_cred_refresh(void) {\r
-    kmq_post_message(KMSG_CRED, KMSG_CRED_REFRESH, 0, NULL);\r
-}\r
-\r
-void\r
-khm_cred_addr_change(void) {\r
-    khm_handle csp_cw = NULL;\r
-    khm_int32 check_net = 0;\r
-\r
-    wchar_t * ids = NULL;\r
-    wchar_t * t;\r
-    khm_size cb;\r
-    khm_size n_idents;\r
-\r
-    FILETIME ft_now;\r
-    FILETIME ft_exp;\r
-    FILETIME ft_issue;\r
-\r
-    if (KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow",\r
-                                     0, &csp_cw))) {\r
-        khc_read_int32(csp_cw, L"AutoDetectNet", &check_net);\r
-\r
-        khc_close_space(csp_cw);\r
-    }\r
-\r
-    if (!check_net)\r
-        return;\r
-\r
-    while(TRUE) {\r
-        if (ids)\r
-            PFREE(ids);\r
-        ids = NULL;\r
-\r
-        if (kcdb_identity_enum(KCDB_IDENT_FLAG_VALID |\r
-                               KCDB_IDENT_FLAG_RENEWABLE,\r
-                               KCDB_IDENT_FLAG_VALID |\r
-                               KCDB_IDENT_FLAG_RENEWABLE,\r
-                               NULL,\r
-                               &cb,\r
-                               &n_idents) != KHM_ERROR_TOO_LONG)\r
-            break;\r
-\r
-        ids = PMALLOC(cb);\r
-\r
-        if (KHM_SUCCEEDED\r
-            (kcdb_identity_enum(KCDB_IDENT_FLAG_VALID |\r
-                                KCDB_IDENT_FLAG_RENEWABLE,\r
-                                KCDB_IDENT_FLAG_VALID |\r
-                                KCDB_IDENT_FLAG_RENEWABLE,\r
-                                ids,\r
-                                &cb,\r
-                                &n_idents)))\r
-            break;\r
-    }\r
-\r
-    if (!ids)\r
-        return;\r
-\r
-    GetSystemTimeAsFileTime(&ft_now);\r
-\r
-    for (t=ids; t && *t; t = multi_string_next(t)) {\r
-        khm_handle ident;\r
-\r
-\r
-        if (KHM_FAILED\r
-            (kcdb_identity_create(t, 0, &ident)))\r
-            continue;\r
-\r
-        cb = sizeof(ft_issue);\r
-\r
-        if (KHM_SUCCEEDED\r
-            (kcdb_identity_get_attr(ident, KCDB_ATTR_ISSUE, NULL,\r
-                                    &ft_issue, &cb)) &&\r
-\r
-            (cb = sizeof(ft_exp)) &&\r
-            KHM_SUCCEEDED\r
-            (kcdb_identity_get_attr(ident, KCDB_ATTR_EXPIRE, NULL,\r
-                                    &ft_exp, &cb)) &&\r
-\r
-            CompareFileTime(&ft_now, &ft_exp) < 0) {\r
-\r
-            khm_int64 i_issue;\r
-            khm_int64 i_exp;\r
-            khm_int64 i_now;\r
-\r
-            i_issue = FtToInt(&ft_issue);\r
-            i_exp = FtToInt(&ft_exp);\r
-            i_now = FtToInt(&ft_now);\r
-\r
-            if (i_now > (i_issue + i_exp) / 2) {\r
-\r
-                khm_cred_renew_identity(ident);\r
-\r
-            }\r
-        }\r
-\r
-        kcdb_identity_release(ident);\r
-    }\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 wchar_t dialog_identity[KCDB_IDENT_MAXCCH_NAME];
+static khui_new_creds * dialog_nc = NULL;
+
+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;
+
+        /* if a dialog is being displayed and we got a another request
+           to show one, we bring the existing one to the
+           foreground. */
+        if (dialog_nc && dialog_nc->hwnd) {
+            khm_int32 t = 0;
+
+            if (KHM_SUCCEEDED(khc_read_int32(NULL,
+                                             L"CredWindow\\Windows\\NewCred\\ForceToTop",
+                                             &t)) &&
+                t != 0) {
+
+                khm_activate_main_window();
+
+                SetWindowPos(dialog_nc->hwnd, HWND_TOP, 0, 0, 0, 0,
+                             (SWP_NOMOVE | SWP_NOSIZE));
+            }
+        }
+
+    } else {
+        rv = TRUE;
+        in_dialog = TRUE;
+        ResetEvent(in_dialog_evt);
+    }
+
+    LeaveCriticalSection(&cs_dialog);
+    return rv;
+}
+
+void 
+khm_cred_end_dialog(khui_new_creds * nc) {
+    dialog_sync_init();
+
+    EnterCriticalSection(&cs_dialog);
+    if (in_dialog) {
+        in_dialog = FALSE;
+        SetEvent(in_dialog_evt);
+    }
+    dialog_result = nc->result;
+#ifdef DEBUG
+    assert(dialog_nc == nc);
+#endif
+    dialog_nc = NULL;
+    if (nc->subtype == KMSG_CRED_NEW_CREDS &&
+        nc->n_identities > 0 &&
+        nc->identities[0]) {
+        khm_size cb;
+
+        cb = sizeof(dialog_identity);
+        if (KHM_FAILED(kcdb_identity_get_name(nc->identities[0],
+                                              dialog_identity,
+                                              &cb)))
+            dialog_identity[0] = 0;
+    } else {
+        dialog_identity[0] = 0;
+    }
+    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,
+                         wchar_t * ident, khm_size cb_ident) {
+    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;
+                }
+                if (ident) {
+                    StringCbCopy(ident, cb_ident, dialog_identity);
+                }
+                break;
+            } else if(dw == WAIT_TIMEOUT) {
+                rv = KHM_ERROR_TIMEOUT;
+                break;
+            }
+        } while(TRUE);
+    }
+    LeaveCriticalSection(&cs_dialog);
+
+    return rv;
+}
+
+/* Completion handler for KMSG_CRED messages.  We control the overall
+   logic of credentials acquisition and other operations here.  Once a
+   credentials operation is triggered, each successive message
+   completion notification will be used to dispatch the messages for
+   the next step in processing the operation. */
+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 no 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 we are done processing all the plug-ins, then check if
+           there were any errors reported.  Otherwise we dispatch
+           another set of messages. */
+        if(!khm_cred_dispatch_process_level(nc)) {
+
+            if(kherr_is_error()) {
+                khui_alert * alert;
+                kherr_event * evt;
+                kherr_context * ctx;
+                wchar_t ws_tfmt[512];
+                wchar_t w_idname[KCDB_IDENT_MAXCCH_NAME];
+                wchar_t ws_title[ARRAYLENGTH(ws_tfmt) + KCDB_IDENT_MAXCCH_NAME];
+                khm_size cb;
+
+                /* For renewals, we suppress the error message for the
+                   following case:
+
+                   - The renewal was for an identity
+
+                   - There are no identity credentials for the
+                     identity (no credentials that have the same type
+                     as the identity provider). */
+
+                if (nc->subtype == KMSG_CRED_RENEW_CREDS &&
+                    nc->ctx.scope == KHUI_SCOPE_IDENT &&
+                    nc->ctx.identity != NULL) {
+                    khm_handle tcs = NULL; /* credential set */
+                    khm_size count = 0;
+                    khm_int32 id_ctype = KCDB_CREDTYPE_INVALID;
+                    khm_int32 delta = 0;
+
+                    kcdb_identity_get_type(&id_ctype);
+                    kcdb_credset_create(&tcs);
+                    kcdb_credset_collect(tcs, NULL,
+                                         nc->ctx.identity,
+                                         id_ctype,
+                                         &delta);
+                    kcdb_credset_get_size(tcs, &count);
+                    kcdb_credset_delete(tcs);
+
+                    if (count == 0) {
+                        goto done_with_op;
+                    }
+                }
+
+                ctx = kherr_peek_context();
+                evt = kherr_get_err_event(ctx);
+                kherr_evaluate_event(evt);
+
+                khui_alert_create_empty(&alert);
+
+                if (nc->subtype == KMSG_CRED_NEW_CREDS) {
+
+                    khui_alert_set_type(alert, KHUI_ALERTTYPE_ACQUIREFAIL);
+
+                    cb = sizeof(w_idname);
+                    if (nc->n_identities == 0 ||
+                        KHM_FAILED(kcdb_identity_get_name(nc->identities[0],
+                                                          w_idname, &cb))) {
+                        /* an identity could not be determined */
+                        LoadString(khm_hInstance, IDS_NC_FAILED_TITLE,
+                                   ws_title, ARRAYLENGTH(ws_title));
+                    } else {
+                        LoadString(khm_hInstance, IDS_NC_FAILED_TITLE_I,
+                                   ws_tfmt, ARRAYLENGTH(ws_tfmt));
+                        StringCbPrintf(ws_title, sizeof(ws_title),
+                                       ws_tfmt, w_idname);
+                        khui_alert_set_ctx(alert,
+                                           KHUI_SCOPE_IDENT,
+                                           nc->identities[0],
+                                           KCDB_CREDTYPE_INVALID,
+                                           NULL);
+                    }
+
+                } else if (nc->subtype == KMSG_CRED_PASSWORD) {
+
+                    khui_alert_set_type(alert, KHUI_ALERTTYPE_CHPW);
+
+                    cb = sizeof(w_idname);
+                    if (nc->n_identities == 0 ||
+                        KHM_FAILED(kcdb_identity_get_name(nc->identities[0],
+                                                          w_idname, &cb))) {
+                        LoadString(khm_hInstance, IDS_NC_PWD_FAILED_TITLE,
+                                   ws_title, ARRAYLENGTH(ws_title));
+                    } else {
+                        LoadString(khm_hInstance, IDS_NC_PWD_FAILED_TITLE_I,
+                                   ws_tfmt, ARRAYLENGTH(ws_tfmt));
+                        StringCbPrintf(ws_title, sizeof(ws_title),
+                                       ws_tfmt, w_idname);
+                        khui_alert_set_ctx(alert,
+                                           KHUI_SCOPE_IDENT,
+                                           nc->identities[0],
+                                           KCDB_CREDTYPE_INVALID,
+                                           NULL);
+                    }
+
+                } else if (nc->subtype == KMSG_CRED_RENEW_CREDS) {
+
+                    khui_alert_set_type(alert, KHUI_ALERTTYPE_RENEWFAIL);
+
+                    cb = sizeof(w_idname);
+                    if (nc->ctx.identity == NULL ||
+                        KHM_FAILED(kcdb_identity_get_name(nc->ctx.identity,
+                                                          w_idname, &cb))) {
+                        LoadString(khm_hInstance, IDS_NC_REN_FAILED_TITLE,
+                                   ws_title, ARRAYLENGTH(ws_title));
+                    } else {
+                        LoadString(khm_hInstance, IDS_NC_REN_FAILED_TITLE_I,
+                                   ws_tfmt, ARRAYLENGTH(ws_tfmt));
+                        StringCbPrintf(ws_title, sizeof(ws_title),
+                                       ws_tfmt, w_idname);
+                        khui_alert_set_ctx(alert,
+                                           KHUI_SCOPE_IDENT,
+                                           nc->ctx.identity,
+                                           KCDB_CREDTYPE_INVALID,
+                                           NULL);
+                    }
+
+                } else {
+#ifdef DEBUG
+                    assert(FALSE);
+#endif
+                }
+
+                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);
+
+                if (nc->subtype == KMSG_CRED_RENEW_CREDS &&
+                    nc->ctx.identity != NULL) {
+
+                    khm_int32 n_cmd;
+
+                    n_cmd = khm_get_identity_new_creds_action(nc->ctx.identity);
+
+                    if (n_cmd != 0) {
+                        khui_alert_add_command(alert, n_cmd);
+                        khui_alert_add_command(alert, KHUI_PACTION_CLOSE);
+
+                        khui_alert_set_flags(alert, KHUI_ALERT_FLAG_DISPATCH_CMD,
+                                             KHUI_ALERT_FLAG_DISPATCH_CMD);
+                    }
+                }
+
+                khui_alert_show(alert);
+                khui_alert_release(alert);
+
+                kherr_release_context(ctx);
+
+                kherr_clear_error();
+            }
+
+        done_with_op:
+
+            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;
+            khm_boolean continue_cmdline = TRUE;
+
+            nc = (khui_new_creds *) m->vparam;
+
+            if (nc->subtype == KMSG_CRED_NEW_CREDS ||
+                nc->subtype == KMSG_CRED_PASSWORD) {
+
+                khm_cred_end_dialog(nc);
+
+            } else if (nc->subtype == KMSG_CRED_RENEW_CREDS) {
+
+                /* if this is a renewal that was triggered while we
+                   were processing the commandline, then we need to
+                   update the pending renewal count. */
+
+                if (khm_startup.processing) {
+                    LONG renewals;
+                    renewals = InterlockedDecrement(&khm_startup.pending_renewals);
+
+                    if (renewals != 0) {
+                        continue_cmdline = FALSE;
+                    }
+                }
+            }
+
+            khui_cw_destroy_cred_blob(nc);
+
+            kmq_post_message(KMSG_CRED, KMSG_CRED_REFRESH, 0, 0);
+
+            if (continue_cmdline)
+                kmq_post_message(KMSG_ACT, KMSG_ACT_CONTINUE_CMDLINE, 0, 0);
+        }
+        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);
+        PFREE(m->vparam);
+
+        kmq_post_message(KMSG_CRED, KMSG_CRED_REFRESH, 0, 0);
+
+        kmq_post_message(KMSG_ACT, KMSG_ACT_CONTINUE_CMDLINE, 0, 0);
+        break;
+
+    case KMSG_CRED_IMPORT:
+        {
+            khm_boolean continue_cmdline = FALSE;
+            LONG pending_renewals;
+
+            /* once an import operation ends, we have to trigger a
+               renewal so that other plug-ins that didn't participate
+               in the import operation can have a chance at getting
+               the necessary credentials.
+
+               If we are in the middle of processing the commandline,
+               we have to be a little bit careful.  We can't issue a
+               commandline conituation message right now because the
+               import action is still ongoing (since the renewals are
+               part of the action).  Once the renewals have completed,
+               the completion handler will automatically issue a
+               commandline continuation message.  However, if there
+               were no identities to renew, then we have to issue the
+               message ourselves.
+            */
+
+            InterlockedIncrement(&khm_startup.pending_renewals);
+
+            khm_cred_renew_all_identities();
+
+            pending_renewals = InterlockedDecrement(&khm_startup.pending_renewals);
+
+            if (pending_renewals == 0 && khm_startup.processing)
+                kmq_post_message(KMSG_ACT, KMSG_ACT_CONTINUE_CMDLINE, 0, 0);
+        }
+        break;
+
+    case KMSG_CRED_REFRESH:
+        kcdb_identity_refresh_all();
+        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(khm_boolean sync, khm_boolean quiet)
+{
+    khui_action_context * pctx;
+
+    pctx = PMALLOC(sizeof(*pctx));
+#ifdef DEBUG
+    assert(pctx);
+#endif
+
+    khui_context_get(pctx);
+
+    if(pctx->scope == KHUI_SCOPE_NONE && !quiet) {
+        /* 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);
+        PFREE(pctx);
+
+        return;
+    }
+
+    _begin_task(KHERR_CF_TRANSITIVE);
+    _report_sr0(KHERR_NONE, IDS_CTX_DESTROY_CREDS);
+    _describe();
+
+    if (sync)
+        kmq_send_message(KMSG_CRED,
+                         KMSG_CRED_DESTROY_CREDS,
+                         0,
+                         (void *) pctx);
+    else
+        kmq_post_message(KMSG_CRED,
+                         KMSG_CRED_DESTROY_CREDS,
+                         0,
+                         (void *) pctx);
+
+    _end_task();
+}
+
+void khm_cred_destroy_identity(khm_handle identity)
+{
+    khui_action_context * pctx;
+    wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
+    khm_size cb;
+
+    if (identity == NULL)
+        return;
+
+    pctx = PMALLOC(sizeof(*pctx));
+#ifdef DEBUG
+    assert(pctx);
+#endif
+
+    khui_context_create(pctx,
+                        KHUI_SCOPE_IDENT,
+                        identity,
+                        KCDB_CREDTYPE_INVALID,
+                        NULL);
+
+    cb = sizeof(idname);
+    kcdb_identity_get_name(identity, idname, &cb);
+
+    _begin_task(KHERR_CF_TRANSITIVE);
+    _report_sr1(KHERR_NONE, IDS_CTX_DESTROY_ID, _dupstr(idname));
+    _describe();
+
+    kmq_post_message(KMSG_CRED,
+                     KMSG_CRED_DESTROY_CREDS,
+                     0,
+                     (void *) pctx);
+
+    _end_task();
+}
+
+void khm_cred_renew_all_identities(void)
+{
+    khm_size count;
+    khm_size cb = 0;
+    khm_size n_idents = 0;
+    khm_int32 rv;
+    wchar_t * ident_names = NULL;
+    wchar_t * this_ident;
+
+    kcdb_credset_get_size(NULL, &count);
+
+    /* if there are no credentials, we just skip over the renew
+       action. */
+
+    if (count == 0)
+        return;
+
+    ident_names = NULL;
+
+    while (TRUE) {
+        if (ident_names) {
+            PFREE(ident_names);
+            ident_names = NULL;
+        }
+
+        cb = 0;
+        rv = kcdb_identity_enum(KCDB_IDENT_FLAG_EMPTY, 0,
+                                NULL,
+                                &cb, &n_idents);
+
+        if (n_idents == 0 || rv != KHM_ERROR_TOO_LONG ||
+            cb == 0)
+            break;
+
+        ident_names = PMALLOC(cb);
+        ident_names[0] = L'\0';
+
+        rv = kcdb_identity_enum(KCDB_IDENT_FLAG_EMPTY, 0,
+                                ident_names,
+                                &cb, &n_idents);
+
+        if (KHM_SUCCEEDED(rv))
+            break;
+    }
+
+    if (ident_names) {
+        for (this_ident = ident_names;
+             this_ident && *this_ident;
+             this_ident = multi_string_next(this_ident)) {
+            khm_handle ident;
+
+            if (KHM_FAILED(kcdb_identity_create(this_ident, 0,
+                                                &ident)))
+                continue;
+
+            khm_cred_renew_identity(ident);
+
+            kcdb_identity_release(ident);
+        }
+
+        PFREE(ident_names);
+        ident_names = NULL;
+    }
+}
+
+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_PROCESS;
+    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();
+
+    /* if we are calling this while processing startup actions, we
+       need to keep track of how many we have issued. */
+    if (khm_startup.processing) {
+        InterlockedIncrement(&khm_startup.pending_renewals);
+    }
+
+    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_PROCESS;
+    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();
+
+    /* if we are calling this while processing startup actions, we
+       need to keep track of how many we have issued. */
+    if (khm_startup.processing) {
+        InterlockedIncrement(&khm_startup.pending_renewals);
+    }
+
+    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_PROCESS;
+    khui_context_get(&c->ctx);
+
+    _begin_task(KHERR_CF_TRANSITIVE);
+    _report_sr0(KHERR_NONE, IDS_CTX_RENEW_CREDS);
+    _describe();
+
+    /* if we are calling this while processing startup actions, we
+       need to keep track of how many we have issued. */
+    if (khm_startup.processing) {
+        InterlockedIncrement(&khm_startup.pending_renewals);
+    }
+
+    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;
+    dialog_nc = nc;
+
+    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 = PMALLOC(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 = PMALLOC(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_for_ident(khm_handle ident, wchar_t * title)
+{
+    khui_action_context ctx;
+
+    if (ident == NULL)
+        khm_cred_obtain_new_creds(title);
+
+    khui_context_get(&ctx);
+
+    khui_context_set(KHUI_SCOPE_IDENT,
+                     ident,
+                     KCDB_CREDTYPE_INVALID,
+                     NULL,
+                     NULL,
+                     0,
+                     NULL);
+
+    khm_cred_obtain_new_creds(title);
+
+    khui_context_set_indirect(&ctx);
+
+    khui_context_release(&ctx);
+}
+
+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;
+    dialog_nc = nc;
+
+    khui_context_get(&nc->ctx);
+
+    kcdb_identpro_get_ui_cb((void *) &nc->ident_cb);
+
+    if (nc->ident_cb == NULL) {
+        wchar_t title[256];
+        wchar_t msg[512];
+        wchar_t suggestion[512];
+        khui_alert * a;
+
+        LoadString(khm_hInstance, IDS_ERR_TITLE_NO_IDENTPRO,
+                   title, ARRAYLENGTH(title));
+        LoadString(khm_hInstance, IDS_ERR_MSG_NO_IDENTPRO,
+                   msg, ARRAYLENGTH(msg));
+        LoadString(khm_hInstance, IDS_ERR_SUGG_NO_IDENTPRO,
+                   suggestion, ARRAYLENGTH(suggestion));
+
+        khui_alert_create_simple(title,
+                                 msg,
+                                 KHERR_ERROR,
+                                 &a);
+        khui_alert_set_suggestion(a, suggestion);
+
+        khui_alert_show(a);
+
+        khui_alert_release(a);
+
+        khui_context_release(&nc->ctx);
+        nc->result = KHUI_NC_RESULT_CANCEL;
+        khm_cred_end_dialog(nc);
+        khui_cw_destroy_cred_blob(nc);
+        return;
+    }
+
+    if (title) {
+        if (SUCCEEDED(StringCbLength(title, KHUI_MAXCB_TITLE, &cb))) {
+            cb += sizeof(wchar_t);
+
+            nc->window_title = PMALLOC(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 = PMALLOC(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_context_release(&nc->ctx);
+        nc->result = KHUI_NC_RESULT_CANCEL;
+        khm_cred_end_dialog(nc);
+        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_startup_actions(void) {
+    khm_handle defident = NULL;
+
+    if (!khm_startup.processing)
+        return;
+
+    if (khm_startup.init ||
+        khm_startup.renew ||
+        khm_startup.destroy ||
+        khm_startup.autoinit) {
+        kcdb_identity_get_default(&defident);
+    }
+
+    /* For asynchronous actions, we trigger the action and then exit
+       the loop.  Once the action completes, the completion handler
+       will trigger a continuation message which will result in this
+       function getting called again.  Then we can proceed with the
+       rest of the startup actions. */
+    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;
+
+            /* we also set the renew command to false here because we
+               trigger a renewal for all the identities at the end of
+               the import operation anyway. */
+            khm_startup.renew = FALSE;
+            break;
+        }
+
+        if (khm_startup.renew) {
+            LONG pending_renewals;
+
+            /* if there are no credentials, we just skip over the
+               renew action. */
+
+            khm_startup.renew = FALSE;
+
+            InterlockedIncrement(&khm_startup.pending_renewals);
+
+            khm_cred_renew_all_identities();
+
+            pending_renewals = InterlockedDecrement(&khm_startup.pending_renewals);
+
+            if (pending_renewals != 0)
+                break;
+
+            /* if there were no pending renewals, then we just fall
+               through. This means that either there were no
+               identities to renew, or all the renewals completed.  If
+               all the renewals completed, then the commandline
+               contiuation message wasn't triggered.  Either way, we
+               must fall through if the count is zero. */
+        }
+
+        if (khm_startup.destroy) {
+
+            khm_startup.destroy = FALSE;
+
+            if (defident) {
+                khui_context_set(KHUI_SCOPE_IDENT,
+                                 defident,
+                                 KCDB_CREDTYPE_INVALID,
+                                 NULL, NULL, 0,
+                                 NULL);
+
+                khm_cred_destroy_creds(FALSE, FALSE);
+                break;
+            }
+        }
+
+        if (khm_startup.autoinit) {
+            khm_size count = 0;
+            khm_handle credset = NULL;
+            khm_int32 ctype_ident = KCDB_CREDTYPE_INVALID;
+            khm_int32 delta = 0;
+
+            khm_startup.autoinit = FALSE;
+
+            kcdb_credset_create(&credset);
+            kcdb_identity_get_type(&ctype_ident);
+
+            kcdb_credset_collect(credset, NULL,
+                                 defident, ctype_ident,
+                                 &delta);
+
+            kcdb_credset_get_size(credset, &count);
+
+            kcdb_credset_delete(credset);
+
+            if (count == 0) {
+                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);
+                break;
+            }
+        }
+
+        if (khm_startup.exit) {
+            PostMessage(khm_hwnd_main,
+                        WM_COMMAND,
+                        MAKEWPARAM(KHUI_ACTION_EXIT, 0), 0);
+            khm_startup.exit = FALSE;
+            break;
+        }
+
+        /* when we get here, then we are all done with the command
+           line stuff */
+        khm_startup.processing = FALSE;
+        khm_startup.remote = FALSE;
+
+        kmq_post_message(KMSG_ACT, KMSG_ACT_END_CMDLINE, 0, 0);
+    } while(FALSE);
+
+    if (defident)
+        kcdb_identity_release(defident);
+}
+
+void
+khm_cred_begin_startup_actions(void) {
+    khm_handle csp_cw;
+
+    if (khm_startup.seen)
+        return;
+
+    if (!khm_startup.remote &&
+        KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow", 0, &csp_cw))) {
+
+        khm_int32 t = 0;
+
+        khc_read_int32(csp_cw, L"Autoinit", &t);
+        if (t)
+            khm_startup.autoinit = TRUE;
+
+        t = 0;
+        khc_read_int32(csp_cw, L"AutoImport", &t);
+        if (t)
+            khm_startup.import = TRUE;
+
+        khc_close_space(csp_cw);
+
+    }
+
+    khm_startup.seen = TRUE;
+    khm_startup.processing = TRUE;
+
+    khm_cred_process_startup_actions();
+}
+
+void
+khm_cred_refresh(void) {
+    kmq_post_message(KMSG_CRED, KMSG_CRED_REFRESH, 0, NULL);
+}
+
+void
+khm_cred_addr_change(void) {
+    khm_handle csp_cw = NULL;
+    khm_int32 check_net = 0;
+
+    wchar_t * ids = NULL;
+    wchar_t * t;
+    khm_size cb;
+    khm_size n_idents;
+
+    FILETIME ft_now;
+    FILETIME ft_exp;
+    FILETIME ft_issue;
+
+    if (KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow",
+                                     0, &csp_cw))) {
+        khc_read_int32(csp_cw, L"AutoDetectNet", &check_net);
+
+        khc_close_space(csp_cw);
+    }
+
+    if (!check_net)
+        return;
+
+    while(TRUE) {
+        if (ids)
+            PFREE(ids);
+        ids = NULL;
+
+        if (kcdb_identity_enum(KCDB_IDENT_FLAG_VALID |
+                               KCDB_IDENT_FLAG_RENEWABLE,
+                               KCDB_IDENT_FLAG_VALID |
+                               KCDB_IDENT_FLAG_RENEWABLE,
+                               NULL,
+                               &cb,
+                               &n_idents) != KHM_ERROR_TOO_LONG)
+            break;
+
+        ids = PMALLOC(cb);
+
+        if (KHM_SUCCEEDED
+            (kcdb_identity_enum(KCDB_IDENT_FLAG_VALID |
+                                KCDB_IDENT_FLAG_RENEWABLE,
+                                KCDB_IDENT_FLAG_VALID |
+                                KCDB_IDENT_FLAG_RENEWABLE,
+                                ids,
+                                &cb,
+                                &n_idents)))
+            break;
+    }
+
+    if (!ids)
+        return;
+
+    GetSystemTimeAsFileTime(&ft_now);
+
+    for (t=ids; t && *t; t = multi_string_next(t)) {
+        khm_handle ident;
+
+
+        if (KHM_FAILED
+            (kcdb_identity_create(t, 0, &ident)))
+            continue;
+
+        cb = sizeof(ft_issue);
+
+        if (KHM_SUCCEEDED
+            (kcdb_identity_get_attr(ident, KCDB_ATTR_ISSUE, NULL,
+                                    &ft_issue, &cb)) &&
+
+            (cb = sizeof(ft_exp)) &&
+            KHM_SUCCEEDED
+            (kcdb_identity_get_attr(ident, KCDB_ATTR_EXPIRE, NULL,
+                                    &ft_exp, &cb)) &&
+
+            CompareFileTime(&ft_now, &ft_exp) < 0) {
+
+            khm_int64 i_issue;
+            khm_int64 i_exp;
+            khm_int64 i_now;
+
+            i_issue = FtToInt(&ft_issue);
+            i_exp = FtToInt(&ft_exp);
+            i_now = FtToInt(&ft_now);
+
+            if (i_now > (i_issue + i_exp) / 2) {
+
+                khm_cred_renew_identity(ident);
+
+            }
+        }
+
+        kcdb_identity_release(ident);
+    }
+}
index 761cbf5062deef9507270b644765ae3c2877d492..0a4e2531a6d36af8c58a75fb54ac3eb8707273c0 100644 (file)
@@ -1,93 +1,93 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_CREDFUNCS_H\r
-#define __KHIMAIRA_CREDFUNCS_H\r
-\r
-void KHMAPI \r
-kmsg_cred_completion(kmq_message *m);\r
-\r
-void \r
-khm_cred_destroy_creds(khm_boolean sync,\r
-                       khm_boolean quiet);\r
-\r
-void\r
-khm_cred_destroy_identity(khm_handle identity);\r
-\r
-void\r
-khm_cred_renew_all_identities(void);\r
-\r
-void \r
-khm_cred_renew_identity(khm_handle identity);\r
-\r
-void \r
-khm_cred_renew_cred(khm_handle cred);\r
-\r
-void \r
-khm_cred_renew_creds(void);\r
-\r
-void \r
-khm_cred_obtain_new_creds(wchar_t * window_title);\r
-\r
-void\r
-khm_cred_obtain_new_creds_for_ident(khm_handle ident, wchar_t * title);\r
-\r
-void \r
-khm_cred_set_default(void);\r
-\r
-void \r
-khm_cred_change_password(wchar_t * window_title);\r
-\r
-void \r
-khm_cred_dispatch_process_message(khui_new_creds *nc);\r
-\r
-BOOL \r
-khm_cred_dispatch_process_level(khui_new_creds *nc);\r
-\r
-BOOL\r
-khm_cred_is_in_dialog(void);\r
-\r
-khm_int32\r
-khm_cred_wait_for_dialog(DWORD timeout, khm_int32 * result,\r
-                         wchar_t * ident, khm_size cb_ident);\r
-\r
-void\r
-khm_cred_begin_startup_actions(void);\r
-\r
-void\r
-khm_cred_process_startup_actions(void);\r
-\r
-void\r
-khm_cred_refresh(void);\r
-\r
-void\r
-khm_cred_addr_change(void);\r
-\r
-void\r
-khm_cred_import(void);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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(khm_boolean sync,
+                       khm_boolean quiet);
+
+void
+khm_cred_destroy_identity(khm_handle identity);
+
+void
+khm_cred_renew_all_identities(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_obtain_new_creds_for_ident(khm_handle ident, wchar_t * 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,
+                         wchar_t * ident, khm_size cb_ident);
+
+void
+khm_cred_begin_startup_actions(void);
+
+void
+khm_cred_process_startup_actions(void);
+
+void
+khm_cred_refresh(void);
+
+void
+khm_cred_addr_change(void);
+
+void
+khm_cred_import(void);
+
+#endif
index d72d1693e5d91a72b918578f629f848f7a32ed30..50e6c4efcdc2fc04972931d8852a3ecfe6607432 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khmapp.h>\r
-#include<prsht.h>\r
-#include<assert.h>\r
-\r
-ATOM khui_credwnd_cls;\r
-khm_int32 khui_cw_flag_id;\r
-\r
-khm_int32 attr_to_action[KCDB_ATTR_MAX_ID + 1];\r
-\r
-void\r
-khm_set_cw_element_font(wchar_t * name, LOGFONT * pfont) {\r
-    khm_handle csp_cw = NULL;\r
-    wchar_t * element_name;\r
-\r
-    if (name == NULL)\r
-        element_name = L"FontBase";\r
-    else\r
-        element_name = name;\r
-\r
-    if (KHM_FAILED(khc_open_space(NULL, L"CredWindow", KHM_PERM_WRITE,\r
-                                  &csp_cw)))\r
-        return;\r
-\r
-    khc_write_binary(csp_cw, element_name, pfont, sizeof(LOGFONT));\r
-\r
-    khc_close_space(csp_cw);\r
-}\r
-\r
-void\r
-khm_get_cw_element_font(HDC hdc, wchar_t * name, BOOL use_default, LOGFONT * pfont) {\r
-    khm_handle csp_cw = NULL;\r
-    khm_size cb;\r
-    wchar_t * element_name;\r
-    khm_boolean try_derive = FALSE;\r
-\r
-    if (name == NULL)\r
-        element_name = L"FontBase";\r
-    else\r
-        element_name = name;\r
-\r
-    if (use_default)\r
-        goto _use_defaults;\r
-\r
-    if (KHM_FAILED(khc_open_space(NULL, L"CredWindow", 0,\r
-                                  &csp_cw)))\r
-        goto _use_defaults;\r
-\r
-    cb = sizeof(LOGFONT);\r
-    if (KHM_FAILED(khc_read_binary(csp_cw, element_name, pfont,\r
-                                   &cb)) ||\r
-        cb != sizeof(LOGFONT)) {\r
-        try_derive = TRUE;\r
-    }\r
-\r
-    if (try_derive) {\r
-        cb = sizeof(LOGFONT);\r
-        if (!name ||\r
-            KHM_FAILED(khc_read_binary(csp_cw, L"FontBase", pfont,\r
-                                       &cb)) ||\r
-            cb != sizeof(LOGFONT)) {\r
-            khc_close_space(csp_cw);\r
-            goto _use_defaults;\r
-        }\r
-\r
-        if (!wcscmp(name, L"FontHeaderBold") ||\r
-            !wcscmp(name, L"FontBold")) {\r
-\r
-            pfont->lfWeight = FW_BOLD;\r
-\r
-        }\r
-    }\r
-\r
-    khc_close_space(csp_cw);\r
-\r
-    return;\r
-\r
- _use_defaults:\r
-\r
-    ZeroMemory(pfont, sizeof(*pfont));\r
-\r
-    if (name == NULL) {\r
-        LOGFONT lf = {\r
-            -MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72), 0, /* width/height */\r
-            0,0, /* escapement */\r
-            FW_THIN,\r
-            FALSE,\r
-            FALSE,\r
-            FALSE,\r
-            DEFAULT_CHARSET,\r
-            OUT_DEFAULT_PRECIS,\r
-            CLIP_DEFAULT_PRECIS,\r
-            DEFAULT_QUALITY,\r
-            FF_SWISS,\r
-            L"MS Shell Dlg"};\r
-\r
-        *pfont = lf;\r
-\r
-    } else if (!wcscmp(name, L"FontHeader")) {\r
-        LOGFONT lf = {\r
-            -MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72), 0, /* width/height */\r
-            0,0, /* escapement */\r
-            FW_THIN,\r
-            FALSE,\r
-            FALSE,\r
-            FALSE,\r
-            DEFAULT_CHARSET,\r
-            OUT_DEFAULT_PRECIS,\r
-            CLIP_DEFAULT_PRECIS,\r
-            DEFAULT_QUALITY,\r
-            FF_SWISS,\r
-            L"MS Shell Dlg"};\r
-\r
-        *pfont = lf;\r
-\r
-    } else if (!wcscmp(name, L"FontHeaderBold")) {\r
-        LOGFONT lf = {\r
-            -MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72), 0, /* width/height */\r
-            0,0, /* escapement */\r
-            FW_BOLD,\r
-            FALSE,\r
-            FALSE,\r
-            FALSE,\r
-            DEFAULT_CHARSET,\r
-            OUT_DEFAULT_PRECIS,\r
-            CLIP_DEFAULT_PRECIS,\r
-            DEFAULT_QUALITY,\r
-            FF_SWISS,\r
-            L"MS Shell Dlg"};\r
-\r
-        *pfont = lf;\r
-\r
-    } else if (!wcscmp(name, L"FontNormal")) {\r
-        LOGFONT lf = {\r
-            -MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72), 0, /* width/height */\r
-            0,0, /* escapement */\r
-            FW_THIN,\r
-            FALSE,\r
-            FALSE,\r
-            FALSE,\r
-            DEFAULT_CHARSET,\r
-            OUT_DEFAULT_PRECIS,\r
-            CLIP_DEFAULT_PRECIS,\r
-            DEFAULT_QUALITY,\r
-            FF_SWISS,\r
-            L"MS Shell Dlg"};\r
-\r
-        *pfont = lf;\r
-\r
-    } else if (!wcscmp(name, L"FontBold")) {\r
-        LOGFONT lf = {\r
-            -MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72), 0, /* width/height */\r
-            0,0, /* escapement */\r
-            FW_BOLD,\r
-            FALSE,\r
-            FALSE,\r
-            FALSE,\r
-            DEFAULT_CHARSET,\r
-            OUT_DEFAULT_PRECIS,\r
-            CLIP_DEFAULT_PRECIS,\r
-            DEFAULT_QUALITY,\r
-            FF_SWISS,\r
-            L"MS Shell Dlg"};\r
-\r
-        *pfont = lf;\r
-\r
-    } else {\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-    }\r
-}\r
-\r
-void\r
-cw_refresh_attribs(HWND hwnd) {\r
-    khm_int32 act;\r
-    kcdb_attrib * attrib;\r
-    khui_menu_def * menu;\r
-    khm_int32 i;\r
-\r
-    menu = khui_find_menu(KHUI_MENU_COLUMNS);\r
-#ifdef DEBUG\r
-    assert(menu);\r
-#endif\r
-\r
-    for (i=0; i <= KCDB_ATTR_MAX_ID; i++) {\r
-        if (KHM_FAILED(kcdb_attrib_get_info(i, &attrib))) {\r
-            if (attr_to_action[i] != 0) {\r
-                /* the action should be removed */\r
-                khui_menu_remove_action(menu, attr_to_action[i]);\r
-                khui_action_delete(attr_to_action[i]);\r
-                attr_to_action[i] = 0;\r
-            }\r
-        } else {\r
-            if (attr_to_action[i] == 0 &&\r
-                !(attrib->flags & KCDB_ATTR_FLAG_HIDDEN) &&\r
-                (attrib->short_desc || attrib->long_desc)) {\r
-                /* new action */\r
-                khm_handle sub = NULL;\r
-\r
-                kmq_create_hwnd_subscription(hwnd, &sub);\r
-\r
-                act = khui_action_create(attrib->name,\r
-                                         (attrib->short_desc?\r
-                                          attrib->short_desc: attrib->long_desc),\r
-                                         NULL,\r
-                                         (void *)(UINT_PTR) i,\r
-                                         KHUI_ACTIONTYPE_TOGGLE,\r
-                                         sub);\r
-\r
-                attr_to_action[i] = act;\r
-\r
-                khui_menu_insert_action(menu, 5000, act, 0);\r
-            }\r
-\r
-            kcdb_attrib_release_info(attrib);\r
-        }\r
-    }\r
-}\r
-\r
-khm_int32 \r
-cw_get_custom_attr_id(wchar_t * s)\r
-{\r
-    if(!wcscmp(s, CW_CANAME_FLAGS))\r
-        return CW_CA_FLAGS;\r
-    if(!wcscmp(s, CW_CANAME_TYPEICON))\r
-        return CW_CA_TYPEICON;\r
-    return 0;\r
-}\r
-\r
-const wchar_t *\r
-cw_get_custom_attr_string(khm_int32 attr_id)\r
-{\r
-    if (attr_id == CW_CA_FLAGS)\r
-        return CW_CANAME_FLAGS;\r
-    if (attr_id == CW_CA_TYPEICON)\r
-        return CW_CANAME_TYPEICON;\r
-    return NULL;\r
-}\r
-\r
-void\r
-cw_save_view(khui_credwnd_tbl * tbl, wchar_t * view_name) {\r
-    wchar_t * col_list = NULL;\r
-    khm_size cb_col_list;\r
-    khm_handle csp_cw = NULL;\r
-    khm_handle csp_views = NULL;\r
-    khm_handle csp_view = NULL;\r
-    khm_handle csp_cols = NULL;\r
-    khm_size cb;\r
-    int i;\r
-\r
-    if (tbl->n_cols == 0)\r
-        return;\r
-\r
-    cb_col_list = (KCONF_MAXCB_NAME + 1) * tbl->n_cols;\r
-\r
-    col_list = PMALLOC(cb_col_list);\r
-#ifdef DEBUG\r
-    assert(col_list);\r
-#endif\r
-\r
-    if (!col_list)\r
-        goto _cleanup;\r
-\r
-    multi_string_init(col_list, cb_col_list);\r
-\r
-    /* if we aren't saving to a specific view, and the view has been\r
-       customized, then we save it to "Custom_0", unless we are in the\r
-       mini mode, in which case we save it to "Custom_1" */\r
-    if (!view_name && (tbl->flags & KHUI_CW_TBL_CUSTVIEW)) {\r
-        if (!(tbl->flags & KHUI_CW_TBL_EXPIDENT)) {\r
-            view_name = L"Custom_0";\r
-        } else {\r
-            view_name = L"Custom_1";\r
-        }\r
-    }\r
-\r
-    if (view_name) {\r
-        if (KHM_FAILED(khc_open_space(NULL, L"CredWindow",\r
-                                      KHM_PERM_READ | KHM_PERM_WRITE, &csp_cw)))\r
-            goto _cleanup;\r
-\r
-        if (KHM_FAILED(khc_open_space(csp_cw, L"Views", KHM_PERM_READ, &csp_views)))\r
-            goto _cleanup;\r
-\r
-        if (KHM_FAILED(khc_open_space(csp_views, view_name,\r
-                                      KHM_PERM_WRITE | KHM_FLAG_CREATE,\r
-                                      &csp_view)))\r
-            goto _cleanup;\r
-\r
-        /* if we are switching to a custom view, then we should mark\r
-           that as the default. */\r
-        if (tbl->flags & KHUI_CW_TBL_CUSTVIEW) {\r
-            khc_write_string(csp_cw, ((!(tbl->flags & KHUI_CW_TBL_EXPIDENT))?\r
-                                      L"DefaultView":\r
-                                      L"DefaultViewMini"), view_name);\r
-        }\r
-\r
-    } else {\r
-        csp_view = tbl->csp_view;\r
-    }\r
-\r
-    if (!csp_view)\r
-        goto _cleanup;\r
-\r
-    if (tbl->flags & KHUI_CW_TBL_EXPIDENT) {\r
-        khc_write_int32(csp_view, L"ExpandedIdentity", 1);\r
-    } else {\r
-        khm_int32 t;\r
-        if (KHM_SUCCEEDED(khc_read_int32(csp_view, L"ExpandedIdentity", &t)) && t)\r
-            khc_write_int32(csp_view, L"ExpandedIdentity", 0);\r
-    }\r
-\r
-    if (tbl->flags & KHUI_CW_TBL_NOHEADER) {\r
-        khc_write_int32(csp_view, L"NoHeader", 1);\r
-    } else {\r
-        khm_int32 t;\r
-        if (KHM_SUCCEEDED(khc_read_int32(csp_view, L"NoHeader", &t)) && t)\r
-            khc_write_int32(csp_view, L"NoHeader", 0);\r
-    }\r
-\r
-    if (KHM_FAILED(khc_open_space(csp_view, L"Columns",\r
-                                  KHM_PERM_WRITE | KHM_FLAG_CREATE,\r
-                                  &csp_cols)))\r
-        goto _cleanup;\r
-\r
-    for (i=0; i < tbl->n_cols; i++) {\r
-        const wchar_t * attr_name;\r
-        kcdb_attrib * attrib = NULL;\r
-        khm_handle csp_col = NULL;\r
-\r
-        if (tbl->cols[i].attr_id < 0) {\r
-            attr_name = cw_get_custom_attr_string(tbl->cols[i].attr_id);\r
-        } else {\r
-            if (KHM_FAILED(kcdb_attrib_get_info(tbl->cols[i].attr_id,\r
-                                                &attrib))) {\r
-#ifdef DEBUG\r
-                assert(FALSE);\r
-#endif\r
-                goto _clean_col;\r
-            }\r
-\r
-            attr_name = attrib->name;\r
-        }\r
-#ifdef DEBUG\r
-        assert(attr_name);\r
-#endif\r
-\r
-        cb = cb_col_list;\r
-        multi_string_append(col_list, &cb, attr_name);\r
-\r
-        if (KHM_FAILED(khc_open_space(csp_cols, attr_name,\r
-                                      KHM_PERM_WRITE | KHM_FLAG_CREATE,\r
-                                      &csp_col)))\r
-            goto _clean_col;\r
-\r
-        khc_write_int32(csp_col, L"Width", tbl->cols[i].width);\r
-        khc_write_int32(csp_col, L"SortIndex", tbl->cols[i].sort_index);\r
-        khc_write_int32(csp_col, L"Flags", tbl->cols[i].flags);\r
-\r
-    _clean_col:\r
-\r
-        if (csp_col)\r
-            khc_close_space(csp_col);\r
-\r
-        if (attrib)\r
-            kcdb_attrib_release_info(attrib);\r
-    }\r
-\r
-    khc_write_multi_string(csp_view, L"ColumnList", col_list);\r
-\r
-    {\r
-        khm_version v = app_version;\r
-\r
-        khc_write_binary(csp_view, L"_AppVersion", &v, sizeof(v));\r
-    }\r
-\r
- _cleanup:\r
-\r
-    if (view_name) {\r
-        if (csp_view)\r
-            khc_close_space(csp_view);\r
-\r
-        if (csp_views)\r
-            khc_close_space(csp_views);\r
-\r
-        if (csp_cw)\r
-            khc_close_space(csp_cw);\r
-    }\r
-\r
-    if (csp_cols)\r
-        khc_close_space(csp_cols);\r
-\r
-    if (col_list)\r
-        PFREE(col_list);\r
-}\r
-\r
-static COLORREF\r
-cw_mix_colors(COLORREF c1, COLORREF c2, int alpha) {\r
-    int r = (GetRValue(c1) * alpha + GetRValue(c2) * (255 - alpha)) / 255;\r
-    int g = (GetGValue(c1) * alpha + GetGValue(c2) * (255 - alpha)) / 255;\r
-    int b = (GetBValue(c1) * alpha + GetBValue(c2) * (255 - alpha)) / 255;\r
-\r
-#ifdef DEBUG\r
-    assert(alpha >= 0 && alpha < 256);\r
-#endif\r
-\r
-    return RGB(r,g,b);\r
-}\r
-\r
-void \r
-cw_load_view(khui_credwnd_tbl * tbl, wchar_t * view, HWND hwnd) {\r
-    khm_handle hc_cw = NULL;\r
-    khm_handle hc_vs = NULL;\r
-    khm_handle hc_v = NULL;\r
-    khm_handle hc_cs = NULL;\r
-    khm_handle hc_c = NULL;\r
-    wchar_t buf[KCONF_MAXCCH_NAME];\r
-    wchar_t * clist = NULL;\r
-    khm_size cbsize;\r
-    wchar_t * iter = NULL;\r
-    int i;\r
-    HDC hdc;\r
-    LOGFONT log_font;\r
-    khm_int32 t;\r
-    const wchar_t * viewval;\r
-    khm_boolean reopen_csp = FALSE;\r
-\r
-    tbl->hwnd = hwnd;\r
-\r
-    if (khm_main_wnd_mode == KHM_MAIN_WND_MINI)\r
-        viewval = L"DefaultViewMini";\r
-    else\r
-        viewval = L"DefaultView";\r
-\r
-    if(KHM_FAILED(khc_open_space(NULL, L"CredWindow", KHM_PERM_READ | KHM_PERM_WRITE,\r
-                                 &hc_cw)))\r
-        return;\r
-\r
-    if(KHM_FAILED(khc_open_space(hc_cw, L"Views", KHM_PERM_READ, &hc_vs)))\r
-        goto _exit;\r
-\r
-    if(!view) {\r
-        cbsize = sizeof(buf);\r
-        if(KHM_FAILED(khc_read_string(hc_cw, viewval, buf, &cbsize)))\r
-            goto _exit;\r
-        view = buf;\r
-    } else {\r
-        khc_write_string(hc_cw, viewval, view);\r
-    }\r
-\r
-        /* in addition, if we are loading the default view, we should\r
-           also check the appropriate menu item */\r
-\r
-    if (!wcscmp(view, L"ByIdentity"))\r
-        khui_check_radio_action(khui_find_menu(KHUI_MENU_LAYOUT),\r
-                                KHUI_ACTION_LAYOUT_ID);\r
-    else if (!wcscmp(view, L"ByLocation"))\r
-        khui_check_radio_action(khui_find_menu(KHUI_MENU_LAYOUT),\r
-                                KHUI_ACTION_LAYOUT_LOC);\r
-    else if (!wcscmp(view, L"ByType"))\r
-        khui_check_radio_action(khui_find_menu(KHUI_MENU_LAYOUT),\r
-                                KHUI_ACTION_LAYOUT_TYPE);\r
-    else if (!wcscmp(view, L"Custom_0"))\r
-        khui_check_radio_action(khui_find_menu(KHUI_MENU_LAYOUT),\r
-                                KHUI_ACTION_LAYOUT_CUST);\r
-    else {\r
-        /* do nothing */\r
-    }\r
-\r
-    kmq_post_message(KMSG_ACT, KMSG_ACT_REFRESH, 0, 0);\r
-\r
-    if(KHM_FAILED(khc_open_space(hc_vs, view, 0, &hc_v)))\r
-        goto _exit;\r
-\r
-    /* view data is very sensitive to version changes.  We need to\r
-       check if this configuration data was created with this version\r
-       of NetIDMgr.  If not, we switch to using a schema handle. */\r
-    {\r
-        khm_version this_v = app_version;\r
-        khm_version cfg_v;\r
-\r
-        cbsize = sizeof(cfg_v);\r
-        if (KHM_FAILED(khc_read_binary(hc_v, L"_AppVersion", &cfg_v, &cbsize)) ||\r
-            khm_compare_version(&cfg_v, &this_v) != 0) {\r
-\r
-            khc_close_space(hc_v);\r
-\r
-            if (KHM_FAILED(khc_open_space(hc_vs, view, KCONF_FLAG_SCHEMA,\r
-                                          &hc_v)) &&\r
-                (wcscmp(view, L"Custom_1") ||\r
-                 KHM_FAILED(khc_open_space(hc_vs, L"CompactIdentity",\r
-                                           KCONF_FLAG_SCHEMA, &hc_v)))) {\r
-                goto _exit;\r
-            }\r
-\r
-            reopen_csp = TRUE;\r
-        }\r
-    }\r
-\r
-    tbl->csp_view = hc_v;\r
-\r
-    if(KHM_FAILED(khc_open_space(hc_v, L"Columns",\r
-                                 KHM_PERM_READ | (reopen_csp ? KCONF_FLAG_SCHEMA : 0),\r
-                                 &hc_cs)))\r
-        goto _exit;\r
-\r
-    cbsize = 0;\r
-    if(khc_read_multi_string(hc_v, L"ColumnList", NULL, &cbsize) != KHM_ERROR_TOO_LONG)\r
-        goto _exit;\r
-\r
-    /* temporary */\r
-    clist = PMALLOC(cbsize);\r
-\r
-    if(KHM_FAILED(khc_read_multi_string(hc_v, L"ColumnList", clist, &cbsize)))\r
-        goto _exit;\r
-\r
-    tbl->n_cols = (int) multi_string_length_n(clist);\r
-    tbl->n_total_cols = UBOUNDSS(tbl->n_cols,\r
-                                 KHUI_CW_COL_INITIAL, KHUI_CW_COL_INCREMENT);\r
-    tbl->cols = PMALLOC(sizeof(khui_credwnd_col) * tbl->n_total_cols);\r
-    ZeroMemory(tbl->cols, sizeof(khui_credwnd_col) * tbl->n_total_cols);\r
-\r
-    tbl->flags &= ~(KHUI_CW_TBL_CUSTVIEW | KHUI_CW_TBL_COLSKIP);\r
-\r
-    if (KHM_SUCCEEDED(khc_read_int32(hc_v, L"ExpandedIdentity", &t)) && t) {\r
-        tbl->flags |= KHUI_CW_TBL_EXPIDENT;\r
-    } else {\r
-        tbl->flags &= ~KHUI_CW_TBL_EXPIDENT;\r
-    }\r
-\r
-    if (KHM_SUCCEEDED(khc_read_int32(hc_v, L"NoHeader", &t)) && t) {\r
-        tbl->flags |= KHUI_CW_TBL_NOHEADER;\r
-    } else {\r
-        tbl->flags &= ~KHUI_CW_TBL_NOHEADER;\r
-    }\r
-\r
-    iter = clist;\r
-    i = 0;\r
-    while(iter) {\r
-        khm_int32 attr_id;\r
-\r
-        attr_id = cw_get_custom_attr_id(iter);\r
-        if(!attr_id) {\r
-            /* a KCDB attribute */\r
-            if(KHM_FAILED(kcdb_attrib_get_id(iter, &attr_id))) {\r
-                tbl->flags |= KHUI_CW_TBL_COLSKIP;\r
-                goto _skip_col;\r
-            }\r
-\r
-            if(kcdb_attrib_describe(attr_id, NULL,\r
-                                    &cbsize, KCDB_TS_SHORT) != KHM_ERROR_TOO_LONG ||\r
-               cbsize == 0) {\r
-                tbl->flags |= KHUI_CW_TBL_COLSKIP;\r
-                goto _skip_col;\r
-            }\r
-\r
-            tbl->cols[i].title = PMALLOC(cbsize);\r
-            kcdb_attrib_describe(attr_id, tbl->cols[i].title, &cbsize, KCDB_TS_SHORT);\r
-\r
-            if (attr_id >= 0 &&\r
-                attr_id <= KCDB_ATTR_MAX_ID &&\r
-                attr_to_action[attr_id]) {\r
-                khui_check_action(attr_to_action[attr_id], TRUE);\r
-            }\r
-\r
-        } else {\r
-            /* All current custom attributes are represented by icons,\r
-               not names */\r
-            tbl->cols[i].title = NULL;\r
-        }\r
-\r
-        tbl->cols[i].attr_id = attr_id;\r
-\r
-        if(KHM_SUCCEEDED(khc_open_space(hc_cs, iter,\r
-                                        KHM_PERM_READ | (reopen_csp ? KCONF_FLAG_SCHEMA : 0),\r
-                                        &hc_c))) {\r
-            if(KHM_FAILED(khc_read_int32(hc_c, L"Flags", &(tbl->cols[i].flags))))\r
-                tbl->cols[i].flags = 0;\r
-            if(KHM_FAILED(khc_read_int32(hc_c, L"Width", &(tbl->cols[i].width))))\r
-                tbl->cols[i].width = 100;\r
-            if(KHM_FAILED(khc_read_int32(hc_c, L"SortIndex",\r
-                                         &(tbl->cols[i].sort_index))))\r
-                tbl->cols[i].sort_index = -1;\r
-            khc_close_space(hc_c);\r
-            hc_c = NULL;\r
-        } else {\r
-            tbl->cols[i].flags = 0;\r
-            tbl->cols[i].width = -1;\r
-            tbl->cols[i].sort_index = -1;\r
-        }\r
-        i++;\r
-_skip_col:\r
-        iter = multi_string_next(iter);\r
-    }\r
-\r
-    /* refresh the menus since we checked a few items */\r
-    kmq_post_message(KMSG_ACT, KMSG_ACT_REFRESH, 0, 0);\r
-\r
-    /* adjust the number of columns.  We may have skipped columns due to\r
-       inconsistencies above */\r
-    tbl->n_cols = i;\r
-\r
-    /* now that all the columns have been loaded, load the view\r
-       parameters */\r
-    if(KHM_FAILED(khc_read_int32(hc_v, L"PaddingHorizontal", &(tbl->hpad))))\r
-        khc_read_int32(hc_cw, L"PaddingHorizontal", &(tbl->hpad));\r
-    if(KHM_FAILED(khc_read_int32(hc_v, L"PaddingVertical", &(tbl->vpad))))\r
-        khc_read_int32(hc_cw, L"PaddingVertical", &(tbl->vpad));\r
-    if(KHM_FAILED(khc_read_int32(hc_v, L"PaddingHeader", &(tbl->hpad_h))))\r
-        khc_read_int32(hc_cw, L"PaddingHeader", &(tbl->hpad_h));\r
-    if(KHM_FAILED(khc_read_int32(hc_v, L"WarnThreshold", &(tbl->threshold_warn))))\r
-        khc_read_int32(hc_cw, L"WarnThreshold", &(tbl->threshold_warn));\r
-    if(KHM_FAILED(khc_read_int32(hc_v, L"CriticalThreshold",\r
-                                 &(tbl->threshold_critical))))\r
-        khc_read_int32(hc_cw, L"CriticalThreshold",\r
-                       &(tbl->threshold_critical));\r
-\r
-    /* and the font resources and stuff */\r
-\r
-    tbl->flags |= KHUI_CW_TBL_INITIALIZED | KHUI_CW_TBL_COL_DIRTY | KHUI_CW_TBL_ACTIVE;\r
-\r
-    /*TODO: the graphics objects should be customizable */\r
-\r
-    hdc = GetWindowDC(hwnd);\r
-\r
-    khm_get_cw_element_font(hdc, L"FontHeader", FALSE, &log_font);\r
-    tbl->hf_header = CreateFontIndirect(&log_font);\r
-\r
-    if(tbl->hf_header && tbl->hwnd_header)\r
-        SendMessage(tbl->hwnd_header, WM_SETFONT, (WPARAM) tbl->hf_header, 0);\r
-\r
-    khm_get_cw_element_font(hdc, L"FontHeaderBold", FALSE, &log_font);\r
-    tbl->hf_bold_header = CreateFontIndirect(&log_font);\r
-\r
-\r
-    khm_get_cw_element_font(hdc, L"FontNormal", FALSE, &log_font);\r
-    tbl->hf_normal = CreateFontIndirect(&log_font);\r
-\r
-    khm_get_cw_element_font(hdc, L"FontBold", FALSE, &log_font);\r
-    tbl->hf_bold = CreateFontIndirect(&log_font);\r
-\r
-    ReleaseDC(hwnd, hdc);\r
-\r
-    khui_bitmap_from_hbmp(&(tbl->kbm_logo_shade),\r
-                          LoadImage(khm_hInstance,\r
-                                    MAKEINTRESOURCE(IDB_LOGO_SHADE),\r
-                                    IMAGE_BITMAP,\r
-                                    0,\r
-                                    0,\r
-                                    LR_DEFAULTCOLOR));\r
-\r
-    {\r
-#define SEL_ALPHA 50\r
-\r
-        COLORREF bg_s = GetSysColor(COLOR_HIGHLIGHT);\r
-        COLORREF bg_normal = GetSysColor(COLOR_WINDOW);\r
-        COLORREF bg_gray = RGB(240,240,240);\r
-        COLORREF bg_hdr = RGB(240,240,240);\r
-        COLORREF bg_hdr_warn = RGB(235,235,134);\r
-        COLORREF bg_hdr_crit = RGB(235,184,134);\r
-        COLORREF bg_hdr_exp = RGB(235,134,134);\r
-        COLORREF bg_hdr_def = RGB(184,235,134);\r
-\r
-        tbl->cr_normal =       GetSysColor(COLOR_WINDOWTEXT);\r
-        tbl->cr_s =            GetSysColor(COLOR_WINDOWTEXT);\r
-        tbl->cr_hdr_outline =  RGB(0,0,0);\r
-        tbl->cr_hdr_normal =   GetSysColor(COLOR_WINDOWTEXT);\r
-        tbl->cr_hdr_s =        GetSysColor(COLOR_WINDOWTEXT);\r
-        tbl->cr_hdr_gray =     GetSysColor(COLOR_GRAYTEXT);\r
-        tbl->cr_hdr_gray_s =   GetSysColor(COLOR_HIGHLIGHTTEXT);\r
-\r
-        if (khm_main_wnd_mode == KHM_MAIN_WND_MINI) {\r
-            bg_hdr = bg_normal;\r
-            tbl->cr_hdr_outline = bg_gray;\r
-        }\r
-\r
-        tbl->hb_normal =      CreateSolidBrush(bg_normal);\r
-        tbl->hb_grey =        CreateSolidBrush(bg_gray);\r
-        tbl->hb_s =           CreateSolidBrush(cw_mix_colors(bg_s, bg_normal, SEL_ALPHA));\r
-\r
-        tbl->hb_hdr_bg =      CreateSolidBrush(bg_hdr);\r
-        tbl->hb_hdr_bg_warn = CreateSolidBrush(bg_hdr_warn);\r
-        tbl->hb_hdr_bg_crit = CreateSolidBrush(bg_hdr_crit);\r
-        tbl->hb_hdr_bg_exp =  CreateSolidBrush(bg_hdr_exp);\r
-        tbl->hb_hdr_bg_def =  CreateSolidBrush(bg_hdr_def);\r
-\r
-        tbl->hb_hdr_bg_s =      CreateSolidBrush(cw_mix_colors(bg_s, bg_hdr, SEL_ALPHA));\r
-        tbl->hb_hdr_bg_warn_s = CreateSolidBrush(cw_mix_colors(bg_s, bg_hdr_warn, SEL_ALPHA));\r
-        tbl->hb_hdr_bg_crit_s = CreateSolidBrush(cw_mix_colors(bg_s, bg_hdr_crit, SEL_ALPHA));\r
-        tbl->hb_hdr_bg_exp_s =  CreateSolidBrush(cw_mix_colors(bg_s, bg_hdr_exp, SEL_ALPHA));\r
-        tbl->hb_hdr_bg_def_s =  CreateSolidBrush(cw_mix_colors(bg_s, bg_hdr_def, SEL_ALPHA));\r
-    }\r
-\r
-    tbl->ilist = khui_create_ilist(KHUI_SMICON_CX, KHUI_SMICON_CY-1, 20, 8, 0);\r
-    {\r
-        HBITMAP hbm;\r
-\r
-#define ADD_BITMAP(i) \\r
-        hbm = LoadImage(khm_hInstance, MAKEINTRESOURCE(i), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR); \\r
-        if(hbm) { \\r
-            khui_ilist_add_masked_id(tbl->ilist, hbm, KHUI_TOOLBAR_BGCOLOR, i); \\r
-            DeleteObject(hbm); \\r
-        }\r
-\r
-        ADD_BITMAP(IDB_WDG_COLLAPSE);\r
-        ADD_BITMAP(IDB_WDG_EXPAND);\r
-        ADD_BITMAP(IDB_ID_SM);\r
-        ADD_BITMAP(IDB_ID_DIS_SM);\r
-\r
-        ADD_BITMAP(IDB_TK_NEW_SM);\r
-        ADD_BITMAP(IDB_TK_REFRESH_SM);\r
-        ADD_BITMAP(IDB_WDG_COLLAPSE_HI);\r
-        ADD_BITMAP(IDB_WDG_EXPAND_HI);\r
-\r
-        ADD_BITMAP(IDB_WDG_FLAG);\r
-        ADD_BITMAP(IDB_WDG_CREDTYPE);\r
-        ADD_BITMAP(IDB_FLAG_WARN);\r
-        ADD_BITMAP(IDB_FLAG_EXPIRED);\r
-\r
-        ADD_BITMAP(IDB_FLAG_CRITICAL);\r
-        ADD_BITMAP(IDB_FLAG_RENEW);\r
-        ADD_BITMAP(IDB_WDG_STUCK);\r
-        ADD_BITMAP(IDB_WDG_STUCK_HI);\r
-\r
-        ADD_BITMAP(IDB_WDG_STICK);\r
-        ADD_BITMAP(IDB_WDG_STICK_HI);\r
-        ADD_BITMAP(IDB_TK_SM);\r
-\r
-#undef ADD_BITMAP\r
-    }\r
-\r
-    if (tbl->flags & KHUI_CW_TBL_EXPIDENT) {\r
-        tbl->hi_lg_ident = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_MAIN_APP),\r
-                                     IMAGE_ICON,\r
-                                     GetSystemMetrics(SM_CXICON),\r
-                                     GetSystemMetrics(SM_CYICON),\r
-                                     LR_DEFAULTCOLOR);\r
-    }\r
-\r
-    tbl->cursor_row = -1;\r
-    tbl->scr_left = 0;\r
-    tbl->scr_top = 0;\r
-    tbl->ext_height = 0;\r
-    tbl->ext_width = 0;\r
-\r
-    if (reopen_csp) {\r
-        khc_close_space(hc_v);\r
-\r
-        hc_v = NULL;\r
-\r
-        khc_open_space(hc_vs, view, 0, &hc_v);\r
-\r
-        tbl->csp_view = hc_v;\r
-    }\r
-\r
-_exit:\r
-    if(hc_cw)\r
-        khc_close_space(hc_cw);\r
-    if(hc_vs)\r
-        khc_close_space(hc_vs);\r
-    if(hc_cs)\r
-        khc_close_space(hc_cs);\r
-    if(clist)\r
-        PFREE(clist);\r
-    /* we leave hc_v held, because tbl->csp_view is the same handle.\r
-       We keep that open until the view is unloaded. */\r
-}\r
-\r
-khui_credwnd_ident *\r
-cw_find_ident(khui_credwnd_tbl * tbl, khm_handle ident) {\r
-    khm_size i;\r
-\r
-    for (i=0; i < tbl->n_idents; i++) {\r
-        if (kcdb_identity_is_equal(ident, tbl->idents[i].ident))\r
-            break;\r
-    }\r
-\r
-    if (i < tbl->n_idents)\r
-        return &tbl->idents[i];\r
-    else\r
-        return NULL;\r
-}\r
-\r
-khm_int32 KHMAPI\r
-cw_credset_iter_func(khm_handle cred, void * rock) {\r
-    khui_credwnd_tbl * tbl = (khui_credwnd_tbl *) rock;\r
-    khm_handle ident = NULL;\r
-    khm_size i;\r
-    khui_credwnd_ident * cwi = NULL;\r
-    khm_int32 cred_credtype = KCDB_CREDTYPE_INVALID;\r
-    khm_int32 cred_flags = 0;\r
-\r
-    kcdb_cred_get_identity(cred, &ident);\r
-\r
-    if (ident == NULL)\r
-        goto _cleanup;\r
-\r
-    for (i=0; i < tbl->n_idents; i++) {\r
-        if (kcdb_identity_is_equal(ident, tbl->idents[i].ident))\r
-            break;\r
-    }\r
-\r
-    if (i >= tbl->n_idents) {\r
-        khm_size cb;\r
-\r
-        /* need to add this one */\r
-        if (tbl->n_idents == tbl->nc_idents) {\r
-            tbl->nc_idents = UBOUNDSS(tbl->n_idents + 1,\r
-                                      CW_IDENT_ALLOC_INCR,\r
-                                      CW_IDENT_ALLOC_INCR);\r
-#ifdef DEBUG\r
-            assert(tbl->nc_idents > tbl->n_idents);\r
-#endif\r
-            tbl->idents = PREALLOC(tbl->idents, sizeof(tbl->idents[0]) * tbl->nc_idents);\r
-#ifdef DEBUG\r
-            assert(tbl->idents);\r
-#endif\r
-            ZeroMemory(&tbl->idents[tbl->n_idents],\r
-                       sizeof(tbl->idents[0]) * (tbl->nc_idents - tbl->n_idents));\r
-        }\r
-\r
-        i = tbl->n_idents;\r
-        cwi = &tbl->idents[tbl->n_idents++];\r
-\r
-        ZeroMemory(cwi, sizeof(*cwi));\r
-\r
-        cwi->ident = ident;\r
-        kcdb_identity_hold(ident);\r
-\r
-        cb = sizeof(cwi->name);\r
-        kcdb_identity_get_name(ident, cwi->name, &cb);\r
-    }\r
-\r
-    cwi = &tbl->idents[i];\r
-\r
-    /* this is the first time we are seeing this identity. */\r
-    if (cwi->credcount == 0) {\r
-        khm_size cb;\r
-\r
-        cb = sizeof(cwi->credtype);\r
-        if (KHM_SUCCEEDED(kcdb_identity_get_attr(ident, KCDB_ATTR_TYPE, NULL,\r
-                                                 &cwi->credtype, &cb))) {\r
-            cwi->credtype_name[0] = L'\0';\r
-\r
-            cb = sizeof(cwi->credtype_name);\r
-            if (KHM_FAILED(kcdb_identity_get_attr(ident, KCDB_ATTR_TYPE_NAME, NULL,\r
-                                                  &cwi->credtype, &cb))) {\r
-                cb = sizeof(cwi->credtype_name);\r
-                kcdb_credtype_describe(cwi->credtype, cwi->credtype_name,\r
-                                       &cb, KCDB_TS_SHORT);\r
-            }\r
-        } else {\r
-            cwi->credtype = KCDB_CREDTYPE_INVALID;\r
-            cwi->credtype_name[0] = L'\0';\r
-        }\r
-\r
-        cb = sizeof(cwi->ft_expire);\r
-        if (KHM_FAILED(kcdb_identity_get_attr(ident, KCDB_ATTR_EXPIRE, NULL,\r
-                                              &cwi->ft_expire, &cb))) {\r
-            cwi->ft_expire = IntToFt(0);\r
-        }\r
-\r
-        kcdb_identity_get_flags(cwi->ident, &cwi->ident_flags);\r
-    }\r
-\r
-    cwi->credcount++;\r
-\r
-    kcdb_cred_get_type(cred, &cred_credtype);\r
-    if (cred_credtype >= 0 && cred_credtype == cwi->credtype) {\r
-        cwi->id_credcount++;\r
-\r
-        kcdb_cred_get_flags(cred, &cred_flags);\r
-        if (cred_flags & KCDB_CRED_FLAG_INITIAL) {\r
-            cwi->init_credcount++;\r
-        }\r
-    }\r
-\r
- _cleanup:\r
-    if (ident)\r
-        kcdb_identity_release(ident);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-void \r
-cw_update_creds(khui_credwnd_tbl * tbl)\r
-{\r
-    kcdb_cred_comp_field * fields;\r
-    kcdb_cred_comp_order comp_order;\r
-    int i;\r
-    khm_int32 n;\r
-    khm_int32 delta;\r
-    khm_handle hc;\r
-    khm_int32 flags;\r
-\r
-    if(!tbl->credset) {\r
-        if(KHM_FAILED(kcdb_credset_create(&(tbl->credset))))\r
-            return;\r
-    }\r
-\r
-    kcdb_credset_purge(tbl->credset);\r
-\r
-    kcdb_identity_refresh_all();\r
-\r
-    kcdb_credset_collect(\r
-        tbl->credset,\r
-        NULL,\r
-        NULL,\r
-        KCDB_CREDTYPE_ALL,\r
-        &delta);\r
-\r
-    /* now we need to figure out how to sort the credentials */\r
-    fields = PMALLOC(sizeof(kcdb_cred_comp_field) * tbl->n_cols);\r
-    ZeroMemory(fields, sizeof(kcdb_cred_comp_field) * tbl->n_cols);\r
-\r
-    for(i=0, n=0; i<tbl->n_cols; i++) {\r
-        if((tbl->cols[i].flags & KHUI_CW_COL_SORT_INC) ||\r
-           (tbl->cols[i].flags & KHUI_CW_COL_SORT_DEC) ||\r
-           (tbl->cols[i].flags & KHUI_CW_COL_GROUP)) {\r
-            int si;\r
-            /* we need to sort by this column */\r
-            si = tbl->cols[i].sort_index;\r
-\r
-            if(si < 0 || si >= (int) tbl->n_cols)\r
-            {\r
-                /* this shouldn't happen */\r
-                tbl->cols[i].flags &= ~(KHUI_CW_COL_SORT_INC | \r
-                                        KHUI_CW_COL_SORT_DEC | \r
-                                        KHUI_CW_COL_GROUP);\r
-                continue;\r
-            }\r
-\r
-            fields[si].attrib = tbl->cols[i].attr_id;\r
-            if(tbl->cols[i].flags & KHUI_CW_COL_SORT_DEC)\r
-                fields[si].order = KCDB_CRED_COMP_DECREASING;\r
-            else\r
-                fields[si].order = KCDB_CRED_COMP_INCREASING;\r
-\r
-            /* special case.  if we are sorting by name, we group\r
-               initial tickets before non-initial tickets.\r
-\r
-               Also, if we are sorting by credential type name, then\r
-               we allow the primary credential type first before\r
-               others. */\r
-\r
-            if (fields[si].attrib == KCDB_ATTR_NAME ||\r
-                fields[si].attrib == KCDB_ATTR_TYPE_NAME)\r
-                fields[si].order |= KCDB_CRED_COMP_INITIAL_FIRST;\r
-\r
-            if(si >= n)\r
-                n = si+1;\r
-        }\r
-    }\r
-\r
-    /* we assume that the sort order is sane */\r
-    /*TODO: don't assume; check if the sort order is sane */\r
-\r
-    comp_order.nFields = n;\r
-    comp_order.fields = fields;\r
-\r
-    kcdb_credset_sort(tbl->credset, \r
-                      kcdb_cred_comp_generic, \r
-                      (void *) &comp_order);\r
-\r
-    /* also, if new credentials were added, initialize the UI flag\r
-       attribute to 0 */\r
-    if(delta & KCDB_DELTA_ADD) {\r
-        khm_size s;\r
-\r
-        kcdb_credset_get_size(tbl->credset, &s);\r
-        for(i=0;i< (int) s;i++) {\r
-            if(KHM_FAILED(kcdb_credset_get_cred(tbl->credset,\r
-                                                (khm_int32) i, &hc)))\r
-                continue; /* lost a race */\r
-            if(KHM_FAILED(kcdb_cred_get_attr(hc, khui_cw_flag_id, NULL, \r
-                                             NULL, NULL))) {\r
-                flags = 0;\r
-                kcdb_cred_set_attr(hc, khui_cw_flag_id, &flags, sizeof(flags));\r
-            }\r
-            kcdb_cred_release(hc);\r
-        }\r
-    }\r
-\r
-    /* refresh the per-identity information */\r
-    for (i=0; i < (int) tbl->n_idents; i++) {\r
-        tbl->idents[i].credcount = 0;\r
-        tbl->idents[i].id_credcount = 0;\r
-        tbl->idents[i].init_credcount = 0;\r
-    }\r
-\r
-    kcdb_credset_apply(tbl->credset, cw_credset_iter_func, (void *) tbl);\r
-\r
-    if (fields)\r
-        PFREE(fields);\r
-}\r
-\r
-void \r
-cw_del_outline(khui_credwnd_outline *o) {\r
-    khui_credwnd_outline * c;\r
-    if(!o)\r
-        return;\r
-\r
-    /* the outline object is still in a list */\r
-    if(o->next || o->prev)\r
-        return;\r
-\r
-    if(o->header)\r
-        PFREE(o->header);\r
-\r
-    if ((o->flags & KHUI_CW_O_DATAALLOC) &&\r
-        o->data)\r
-        PFREE(o->data);\r
-\r
-    if ((o->flags & KHUI_CW_O_RELIDENT) &&\r
-        o->data)\r
-        kcdb_identity_release((khm_handle) o->data);\r
-\r
-    LPOP(&(o->children), &c);\r
-    while(c) {\r
-        cw_del_outline(c);\r
-        LPOP(&(o->children), &c);\r
-    }\r
-\r
-    ZeroMemory(o, sizeof(*o));\r
-    PFREE(o);\r
-}\r
-\r
-khui_credwnd_outline * \r
-cw_new_outline_node(wchar_t * heading) {\r
-    khui_credwnd_outline * o;\r
-    size_t cblen;\r
-\r
-    o = PMALLOC(sizeof(khui_credwnd_outline));\r
-    ZeroMemory(o, sizeof(khui_credwnd_outline));\r
-    \r
-    if(SUCCEEDED(StringCbLength(heading, KHUI_MAXCB_HEADING, &cblen))) {\r
-        cblen += sizeof(wchar_t);\r
-        o->header = PMALLOC(cblen);\r
-        StringCbCopy(o->header, cblen, heading);\r
-    }\r
-    o->start = -1;\r
-\r
-    return o;\r
-}\r
-\r
-/* buf is a handle to a credential or an identity.  the kcdb_buf_*\r
-   functions work with either. */\r
-khm_int32 \r
-cw_get_buf_exp_flags(khui_credwnd_tbl * tbl, khm_handle buf)\r
-{\r
-    khm_int32 flags;\r
-    long s;\r
-    FILETIME ft_expire;\r
-    FILETIME ft_current;\r
-    FILETIME ft_difference;\r
-    khm_size cbsize;\r
-\r
-    cbsize = sizeof(ft_expire);\r
-    if(KHM_FAILED(kcdb_buf_get_attr(buf, KCDB_ATTR_EXPIRE, NULL,\r
-                                    &ft_expire, &cbsize)))\r
-        return 0;\r
-\r
-    GetSystemTimeAsFileTime(&ft_current);\r
-    ft_difference = FtSub(&ft_expire, &ft_current);\r
-\r
-    s = FtIntervalToSeconds(&ft_difference);\r
-\r
-    flags = 0;\r
-    if(s < 0)\r
-        flags = CW_EXPSTATE_EXPIRED;\r
-    else if(s < tbl->threshold_critical)\r
-        flags = CW_EXPSTATE_CRITICAL;\r
-    else if(s < tbl->threshold_warn)\r
-        flags = CW_EXPSTATE_WARN;\r
-    else\r
-        flags = CW_EXPSTATE_NONE;\r
-\r
-    return flags;\r
-}\r
-\r
-void cw_update_outline(khui_credwnd_tbl * tbl);\r
-\r
-static void \r
-cw_update_selection_state(khui_credwnd_tbl * tbl);\r
-\r
-VOID CALLBACK \r
-cw_timer_proc(HWND hwnd,\r
-              UINT uMsg,\r
-              UINT_PTR idEvent,\r
-              DWORD dwTime)\r
-{\r
-    khui_credwnd_tbl * tbl;\r
-    khui_credwnd_row * r;\r
-    khm_int32 nflags;\r
-    int nr;\r
-    long ms;\r
-    FILETIME ft;\r
-    khm_size cbsize;\r
-    int timer_set = 0;\r
-\r
-    KillTimer(hwnd, idEvent);\r
-\r
-    tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-    r = (khui_credwnd_row *) idEvent;\r
-    r->flags &= ~KHUI_CW_ROW_TIMERSET;\r
-\r
-    nr = (int)(r - tbl->rows);\r
-\r
-    if(nr < 0 || nr >= tbl->n_rows)\r
-        return;\r
-\r
-    if(r->flags & KHUI_CW_ROW_CRED) {\r
-\r
-        nflags = cw_get_buf_exp_flags(tbl, (khm_handle) r->data);\r
-        if((r->flags & CW_EXPSTATE_MASK) != nflags) {\r
-            /* flags have changed */\r
-            /* the outline needs to be updated */\r
-            cw_update_outline(tbl);\r
-            InvalidateRect(tbl->hwnd, NULL, FALSE);\r
-        } else {\r
-            /* just invalidate the row */\r
-            RECT rc,rr,ri;\r
-\r
-            GetClientRect(tbl->hwnd, &rc);\r
-            rc.top += tbl->header_height;\r
-\r
-            rr = r->r_ext;\r
-            OffsetRect(&rr, 0, tbl->header_height - tbl->scr_top);\r
-\r
-            if(IntersectRect(&ri, &rc, &rr))\r
-                InvalidateRect(tbl->hwnd, &ri, FALSE);\r
-\r
-            cbsize = sizeof(ft);\r
-            if(KHM_SUCCEEDED(kcdb_cred_get_attr((khm_handle) r->data,\r
-                                                KCDB_ATTR_TIMELEFT, NULL,\r
-                                                &ft, &cbsize))) {\r
-                ms = FtIntervalMsToRepChange(&ft);\r
-                if(ms > 0) {\r
-                    SetTimer(tbl->hwnd, (UINT_PTR) r, ms + 100, cw_timer_proc);\r
-                    timer_set = 1;\r
-                }\r
-            }\r
-\r
-            if (timer_set)\r
-                r->flags |= KHUI_CW_ROW_TIMERSET;\r
-        }\r
-    } else {\r
-        khui_credwnd_outline * o;\r
-        khui_credwnd_ident * cwi;\r
-        FILETIME ft_now;\r
-\r
-        o = (khui_credwnd_outline *) r->data;\r
-#ifdef DEBUG\r
-        assert(r->flags & KHUI_CW_ROW_EXPVIEW);\r
-        assert(o->attr_id == KCDB_ATTR_ID);\r
-        assert(tbl->flags & KHUI_CW_TBL_EXPIDENT);\r
-#endif\r
-\r
-        nflags = cw_get_buf_exp_flags(tbl, (khm_handle) o->data);\r
-        if ((o->flags & CW_EXPSTATE_MASK) != nflags) {\r
-            cw_update_outline(tbl);\r
-            InvalidateRect(tbl->hwnd, NULL, FALSE);\r
-        } else {\r
-            RECT rc, rr, ri;\r
-\r
-            GetClientRect(tbl->hwnd, &rc);\r
-            rc.top += tbl->header_height;\r
-\r
-            rr = r->r_ext;\r
-            OffsetRect(&rr, 0, tbl->header_height - tbl->scr_top);\r
-\r
-            if (IntersectRect(&ri, &rc, &rr))\r
-                InvalidateRect(tbl->hwnd, &ri, FALSE);\r
-\r
-            cwi = cw_find_ident(tbl, o->data);\r
-\r
-            GetSystemTimeAsFileTime(&ft_now);\r
-            if (CompareFileTime(&cwi->ft_expire, &ft_now) > 0) {\r
-                ft = FtSub(&cwi->ft_expire, &ft_now);\r
-                ms = FtIntervalMsToRepChange(&ft);\r
-                if (ms > 0) {\r
-                    SetTimer(tbl->hwnd, (UINT_PTR) r, ms + 100, cw_timer_proc);\r
-                    timer_set = 1;\r
-                }\r
-            }\r
-\r
-            if (timer_set)\r
-                r->flags |= KHUI_CW_ROW_TIMERSET;\r
-        }\r
-    }\r
-}\r
-\r
-void \r
-cw_set_tbl_row_cred(khui_credwnd_tbl * tbl, \r
-                    int row, \r
-                    khm_handle cred, \r
-                    int col)\r
-{\r
-    FILETIME ft;\r
-    long ms;\r
-    khm_size cbsize;\r
-\r
-    if((int) tbl->n_total_rows <= row) {\r
-        /* we need to resize the allocation */\r
-        khui_credwnd_row * newrows;\r
-        int newsize;\r
-\r
-        newsize = UBOUNDSS(row+1,KHUI_CW_ROW_INITIAL, KHUI_CW_ROW_INCREMENT);\r
-        newrows = PMALLOC(sizeof(khui_credwnd_row) * newsize);\r
-        memcpy(newrows, tbl->rows, sizeof(khui_credwnd_row) * tbl->n_rows);\r
-        PFREE(tbl->rows);\r
-        tbl->rows = newrows;\r
-        tbl->n_total_rows = newsize;\r
-    }\r
-\r
-    tbl->rows[row].col = col;\r
-    tbl->rows[row].data = cred;\r
-    tbl->rows[row].flags = KHUI_CW_ROW_CRED;\r
-\r
-    /* Set any required timer events */\r
-    cbsize = sizeof(ft);\r
-    if(KHM_SUCCEEDED(kcdb_cred_get_attr(cred, KCDB_ATTR_TIMELEFT, NULL, &ft, &cbsize))) {\r
-        ms = FtIntervalMsToRepChange(&ft);\r
-        if(ms > 0) {\r
-            SetTimer(tbl->hwnd, (UINT_PTR) &(tbl->rows[row]), ms + 100, cw_timer_proc);\r
-            tbl->rows[row].flags |= KHUI_CW_ROW_TIMERSET;\r
-        }\r
-    }\r
-}\r
-\r
-void \r
-cw_set_tbl_row_header(khui_credwnd_tbl * tbl, \r
-                      int row, int col, \r
-                      khui_credwnd_outline * o)\r
-{\r
-    if((int) tbl->n_total_rows <= row) {\r
-        /* we need to resize the allocation */\r
-        khui_credwnd_row * newrows;\r
-        int newsize;\r
-\r
-        newsize = UBOUNDSS(row+1,KHUI_CW_ROW_INITIAL, KHUI_CW_ROW_INCREMENT);\r
-        newrows = PMALLOC(sizeof(khui_credwnd_row) * newsize);\r
-        memcpy(newrows, tbl->rows, sizeof(khui_credwnd_row) * tbl->n_rows);\r
-        PFREE(tbl->rows);\r
-        tbl->rows = newrows;\r
-        tbl->n_total_rows = newsize;\r
-    }\r
-\r
-    tbl->rows[row].col = col;\r
-    tbl->rows[row].data = (khm_handle) o;\r
-    tbl->rows[row].flags = KHUI_CW_ROW_HEADER;\r
-    if(o->flags & KHUI_CW_O_SELECTED)\r
-        tbl->rows[row].flags |= KHUI_CW_ROW_SELECTED;\r
-\r
-    /* if we are showing expanded identity information, we need to set\r
-       a timer so that we can update the identity row when the\r
-       identity changes. */\r
-    if ((tbl->flags & KHUI_CW_TBL_EXPIDENT) &&\r
-        tbl->cols[col].attr_id == KCDB_ATTR_ID_NAME) {\r
-\r
-        khui_credwnd_ident * cwi;\r
-\r
-        tbl->rows[row].flags |= KHUI_CW_ROW_EXPVIEW;\r
-\r
-        cwi = cw_find_ident(tbl, o->data);\r
-        if (cwi && FtToInt(&cwi->ft_expire) != 0) {\r
-            FILETIME ft;\r
-            FILETIME ft_now;\r
-\r
-            ft = cwi->ft_expire;\r
-            GetSystemTimeAsFileTime(&ft_now);\r
-\r
-            if (CompareFileTime(&ft, &ft_now) > 0) {\r
-                long ms;\r
-\r
-                ft = FtSub(&ft, &ft_now);\r
-                ms = FtIntervalMsToRepChange(&ft);\r
-                if (ms > 0) {\r
-                    SetTimer(tbl->hwnd, (UINT_PTR) &(tbl->rows[row]), ms + 100,\r
-                             cw_timer_proc);\r
-                    tbl->rows[row].flags |= KHUI_CW_ROW_TIMERSET;\r
-                }\r
-            }\r
-        }\r
-    }\r
-}\r
-\r
-static int \r
-iwcscmp(const void * p1, const void * p2) {\r
-    const wchar_t * s1 = *(wchar_t **) p1;\r
-    const wchar_t * s2 = *(wchar_t **) p2;\r
-\r
-    return wcscmp(s1, s2);\r
-}\r
-\r
-void \r
-cw_update_outline(khui_credwnd_tbl * tbl)\r
-{\r
-    int i,j,n_rows;\r
-    int level;\r
-    int visible;\r
-    khm_size n_creds = 0;\r
-    khm_handle prevcred = NULL;\r
-    khm_handle thiscred = NULL;\r
-    /* grouping[0..n_grouping-1] are the columns that we are going to\r
-       group the display by.  Say we are grouping by identity and then\r
-       by type, then grouping[0]=col# of identity and grouping[1]=col#\r
-       of type */\r
-    khm_int32 * grouping = NULL;\r
-    khui_credwnd_outline * ol = NULL;\r
-    int n_grouping;\r
-    wchar_t buf[256];\r
-    khm_size cbbuf;\r
-    khm_int32 flags;\r
-    int selected;\r
-    khm_int32 expstate = 0;\r
-\r
-    /*  this is called after calling cw_update_creds, so we assume\r
-        that the credentials are all loaded and sorted according to\r
-        grouping rules  */\r
-\r
-    /* if the columns have changed, then any outline info we have\r
-       cached are unreliable */\r
-    if(tbl->flags & KHUI_CW_TBL_COL_DIRTY) {\r
-        khui_credwnd_outline * o;\r
-        LPOP(&(tbl->outline), &o);\r
-        while(o) {\r
-            cw_del_outline(o);\r
-            LPOP(&(tbl->outline), &o);\r
-        }\r
-        tbl->n_rows = 0;\r
-    }\r
-\r
-    /* Otherwise, we should reset the outline indices.  Just the first\r
-       level is enough */\r
-    if (tbl->outline) {\r
-        khui_credwnd_outline * o;\r
-\r
-        o = tbl->outline;\r
-        while(o) {\r
-            o->start = -1;\r
-            o = LNEXT(o);\r
-        }\r
-    }\r
-\r
-    /* determine the grouping order */\r
-    grouping = PMALLOC(sizeof(khm_int32) * tbl->n_cols);\r
-    for(i=0; i < (int) tbl->n_cols; i++)\r
-        grouping[i] = -1;\r
-    n_grouping = 0;\r
-\r
-    for(i=0; i < (int) tbl->n_cols; i++) {\r
-        /* since cw_update_creds has run, the KHUI_CW_COL_GROUP flag\r
-           only exists for columns that has a valid sort_index */\r
-        if(tbl->cols[i].flags & KHUI_CW_COL_GROUP) {\r
-            grouping[tbl->cols[i].sort_index] = i;\r
-            if(n_grouping <= tbl->cols[i].sort_index)\r
-                n_grouping = tbl->cols[i].sort_index + 1;\r
-        }\r
-    }\r
-\r
-    /* if we have sorted by an index without grouping by it, we can't\r
-       establish any grouping beyond that index. */\r
-    for(i=0; i < n_grouping; i++) {\r
-        if(grouping[i] == -1)\r
-            break;\r
-    }\r
-    n_grouping = i;\r
-\r
-    if(!tbl->rows) {\r
-        /* we haven't allocated memory yet */\r
-        tbl->n_total_rows = KHUI_CW_ROW_INITIAL;\r
-        tbl->n_rows = 0;\r
-        tbl->rows = PMALLOC(sizeof(khui_credwnd_row) * tbl->n_total_rows);\r
-    } else {\r
-        /* kill any pending timers */\r
-        for(i=0; i < (int) tbl->n_rows; i++) \r
-            if(tbl->rows[i].flags & KHUI_CW_ROW_TIMERSET)\r
-            {\r
-                KillTimer(tbl->hwnd, (UINT_PTR) &(tbl->rows[i]));\r
-                tbl->rows[i].flags &= ~KHUI_CW_ROW_TIMERSET;\r
-            }\r
-    }\r
-\r
-    if(KHM_FAILED(kcdb_credset_get_size(tbl->credset, &n_creds)))\r
-        goto _exit;\r
-\r
-    n_rows = 0;\r
-    prevcred = NULL;\r
-    ol = NULL;\r
-\r
-    for(i=0; i < (int) n_creds; i++) {\r
-        if(KHM_FAILED(kcdb_credset_get_cred(tbl->credset, i, &thiscred)))\r
-            continue;\r
-\r
-        /* if this credential appears to be the same as another for\r
-           this view, we skip it. */\r
-        if(prevcred && n_grouping > 0) {\r
-            for(j=0; j < (int) tbl->n_cols; j++) {\r
-                if(kcdb_creds_comp_attr(prevcred, thiscred,\r
-                                        tbl->cols[j].attr_id))\r
-                    break;\r
-            }\r
-\r
-            if(j >= (int) tbl->n_cols) {\r
-                if (n_rows > 0) {\r
-                    tbl->rows[n_rows - 1].idx_end = i;\r
-                }\r
-                continue;\r
-            }\r
-        }\r
-\r
-        if(!prevcred)\r
-            level = 0;\r
-        else {\r
-            for(j=0; j < n_grouping; j++) {\r
-                /* determine the grouping level at which thiscred\r
-                   differs from prevcred */\r
-                if(kcdb_creds_comp_attr(prevcred,thiscred,\r
-                                        tbl->cols[grouping[j]].attr_id))\r
-                    break;\r
-            }\r
-            level = j;\r
-        }\r
-\r
-        /* now we have to walk up until we get to the parent of the\r
-           outline level we should be in */\r
-        while(ol && ol->level >= level) {\r
-            ol->length = n_rows - ol->start;\r
-            ol->idx_end = i - 1;\r
-            ol = TPARENT(ol);\r
-        }\r
-\r
-        if(ol) {\r
-            visible = (ol->flags & KHUI_CW_O_VISIBLE) && \r
-                (ol->flags & KHUI_CW_O_EXPAND);\r
-            selected = (ol->flags & KHUI_CW_O_SELECTED);\r
-        } else {\r
-            visible = TRUE;\r
-            selected = FALSE;\r
-        }\r
-\r
-        /* now ol points to an outline node at the next highest level\r
-           or is NULL if level = 0 */\r
-\r
-        for(j=level; j < n_grouping; j++) {\r
-            khui_credwnd_outline * to;\r
-            /*  now we search for an outline object at the next level\r
-                which matches the heading */\r
-            cbbuf = sizeof(buf);\r
-            buf[0] = L'\0';\r
-            if(KHM_FAILED\r
-               (kcdb_cred_get_attr_string(thiscred, \r
-                                          tbl->cols[grouping[j]].attr_id, \r
-                                          buf, &cbbuf, 0))) {\r
-                cbbuf = sizeof(wchar_t);\r
-                buf[0] = L'\0';\r
-            }\r
-\r
-            if(ol)\r
-                to = TFIRSTCHILD(ol);\r
-            else\r
-                to = tbl->outline;\r
-\r
-            while(to) {\r
-                if(!wcscmp(buf, to->header))\r
-                    break;\r
-                to = LNEXT(to);\r
-            }\r
-\r
-            if(to) {\r
-                /* found it */\r
-                ol = to;\r
-            } else {\r
-                /* not found. create */\r
-                to = cw_new_outline_node(buf);\r
-                if(ol) {\r
-                    TADDCHILD(ol, to);\r
-                } else {\r
-                    LPUSH(&(tbl->outline), to);\r
-                }\r
-                ol = to;\r
-                ol->flags = KHUI_CW_O_EXPAND;\r
-                ol->level = j;\r
-                ol->col = grouping[j];\r
-\r
-                if(tbl->cols[grouping[j]].attr_id == KCDB_ATTR_ID_NAME) {\r
-                    khm_handle h;\r
-                    if(KHM_SUCCEEDED(kcdb_identity_create(buf, 0, &h))) {\r
-                        ol->attr_id = KCDB_ATTR_ID;\r
-                        ol->data = (void *) h;\r
-\r
-                        /* the outline only lasts as long as the\r
-                           credential, and the credential has a hold\r
-                           on the identity. */\r
-                        kcdb_identity_release(h);\r
-                    }\r
-                    else\r
-                        ol->data = 0;\r
-                } else if(tbl->cols[grouping[j]].attr_id == \r
-                          KCDB_ATTR_TYPE_NAME) {\r
-                    khm_int32 t;\r
-\r
-                    ol->attr_id = KCDB_ATTR_TYPE;\r
-                    if(KHM_SUCCEEDED(kcdb_cred_get_type(thiscred, &t)))\r
-                        ol->data = (void *)(ssize_t) t;\r
-                    else\r
-                        ol->data = (void *)(ssize_t) KCDB_CREDTYPE_INVALID;\r
-                } else {\r
-                    khm_int32 rv;\r
-                    khm_int32 alt_id;\r
-                    kcdb_attrib * attrib;\r
-\r
-                    rv = \r
-                        kcdb_attrib_get_info(tbl->cols[grouping[j]].attr_id,\r
-                                             &attrib);\r
-                    assert(KHM_SUCCEEDED(rv));\r
-\r
-                    if (attrib->flags & KCDB_ATTR_FLAG_ALTVIEW)\r
-                        alt_id = attrib->alt_id;\r
-                    else\r
-                        alt_id = tbl->cols[grouping[j]].attr_id;\r
-\r
-                    ol->attr_id = alt_id;\r
-\r
-                    kcdb_attrib_release_info(attrib);\r
-\r
-                    rv = kcdb_cred_get_attr(thiscred,\r
-                                            alt_id,\r
-                                            NULL,\r
-                                            NULL,\r
-                                            &cbbuf);\r
-                    if (rv != KHM_ERROR_TOO_LONG || cbbuf == 0) {\r
-                        ol->data = NULL;\r
-                    } else {\r
-                        ol->data = PMALLOC(cbbuf);\r
-                        assert(ol->data);\r
-                        rv = kcdb_cred_get_attr(thiscred,\r
-                                                alt_id,\r
-                                                NULL,\r
-                                                ol->data,\r
-                                                &cbbuf);\r
-                        assert(KHM_SUCCEEDED(rv));\r
-                        ol->cb_data = cbbuf;\r
-                        ol->flags |= KHUI_CW_O_DATAALLOC;\r
-                    }\r
-                }\r
-            }\r
-\r
-            /* now ol points at the node at level j we want to be\r
-               in */\r
-            ol->start = n_rows;\r
-            ol->length = 0;\r
-            ol->idx_start = i;\r
-            ol->idx_end = i;\r
-            ol->flags &= ~(CW_EXPSTATE_MASK |\r
-                           KHUI_CW_O_SHOWFLAG |\r
-                           KHUI_CW_O_STICKY |\r
-                           KHUI_CW_O_EMPTY);\r
-\r
-            /* if the outline node is for an identity, then we have to\r
-               check the expiration state for the identity. */\r
-\r
-            if (ol->attr_id == KCDB_ATTR_ID) {\r
-                khm_handle ident = (khm_handle) ol->data;\r
-\r
-                flags = cw_get_buf_exp_flags(tbl, ident);\r
-\r
-                if (flags) {\r
-                    ol->flags |= flags;\r
-                    ol->flags |= KHUI_CW_O_SHOWFLAG;\r
-                   expstate |= flags;\r
-                } else if (grouping[j] == tbl->n_cols - 1) {\r
-                    /* if we aren't showing any creds under this\r
-                       outline level, we should also show any\r
-                       flags. */\r
-                    ol->flags |= KHUI_CW_O_SHOWFLAG;\r
-                }\r
-            }\r
-\r
-            if (grouping[j] == tbl->n_cols - 1) {\r
-                ol->flags |= KHUI_CW_O_NOOUTLINE;\r
-            } else {\r
-                ol->flags &= ~KHUI_CW_O_NOOUTLINE;\r
-            }\r
-\r
-            if(selected) {\r
-                ol->flags |= KHUI_CW_O_SELECTED;\r
-            }\r
-            if(visible) {\r
-                cw_set_tbl_row_header(tbl, n_rows, grouping[j], ol);\r
-                n_rows ++;\r
-                ol->flags |= KHUI_CW_O_VISIBLE;\r
-            } else {\r
-                ol->flags &= ~KHUI_CW_O_VISIBLE;\r
-            }\r
-            visible = visible && (ol->flags & KHUI_CW_O_EXPAND);\r
-            selected = (selected || (ol->flags & KHUI_CW_O_SELECTED));\r
-\r
-        }\r
-\r
-        /* we need to do this here too just in case we were already at\r
-           the level we were supposed to be in */\r
-        if (ol)\r
-            visible = visible && (ol->flags & KHUI_CW_O_EXPAND);\r
-\r
-        if(visible && n_grouping > 0 &&\r
-           grouping[n_grouping - 1] < tbl->n_cols - 1) {\r
-            khm_int32 c_flags;\r
-\r
-            cw_set_tbl_row_cred(tbl, n_rows, thiscred, \r
-                                grouping[n_grouping-1]);\r
-\r
-            flags = cw_get_buf_exp_flags(tbl, thiscred);\r
-            if(flags) {\r
-                tbl->rows[n_rows].flags |= flags;\r
-            }\r
-\r
-            kcdb_cred_get_flags(thiscred, &c_flags);\r
-            if(selected ||\r
-               (c_flags & KCDB_CRED_FLAG_SELECTED)) {\r
-                tbl->rows[n_rows].flags |= KHUI_CW_ROW_SELECTED;\r
-            }\r
-\r
-            tbl->rows[n_rows].idx_start = i;\r
-            tbl->rows[n_rows].idx_end = i;\r
-\r
-            n_rows++;\r
-        }\r
-\r
-        if(prevcred)\r
-            kcdb_cred_release(prevcred);\r
-        prevcred = thiscred;\r
-    }\r
-\r
-    while(ol) {\r
-        ol->length = n_rows - ol->start;\r
-        ol->idx_end = i - 1;\r
-        ol = TPARENT(ol);\r
-    }\r
-\r
-    if(prevcred) {\r
-        kcdb_cred_release(prevcred);\r
-        prevcred = NULL;\r
-    }\r
-\r
-    /* Add any default identities with no credentials and sticky\r
-       identities that we haven't seen yet */\r
-    if (n_grouping > 0 && \r
-        tbl->cols[grouping[0]].attr_id == KCDB_ATTR_ID_NAME) {\r
-\r
-        khui_credwnd_outline * o;\r
-        wchar_t * idnames = NULL;\r
-        wchar_t * t;\r
-        khm_size n_idents;\r
-        khm_size cb_names;\r
-        wchar_t ** idarray = NULL;\r
-        int i;\r
-\r
-        /* see if the defualt identity is in the list */\r
-        {\r
-            khm_handle id_def = NULL;\r
-            wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-            khm_size cb;\r
-            khm_int32 flags;\r
-\r
-            if (KHM_FAILED(kcdb_identity_get_default(&id_def))) {\r
-                goto done_with_defident;\r
-            }\r
-\r
-            kcdb_identity_get_flags(id_def, &flags);\r
-            cb = sizeof(idname);\r
-            kcdb_identity_get_name(id_def, idname, &cb);\r
-\r
-            for (o = tbl->outline; o; o = LNEXT(o)) {\r
-                if (!wcscmp(idname, o->header))\r
-                    break;\r
-            }\r
-\r
-            if (o == NULL) {\r
-                o = cw_new_outline_node(idname);\r
-                LPUSH(&tbl->outline, o);\r
-                o->flags = KHUI_CW_O_VISIBLE | KHUI_CW_O_RELIDENT | KHUI_CW_O_EMPTY;\r
-                o->level = 0;\r
-                o->col = grouping[0];\r
-                o->data = id_def;\r
-                o->attr_id = KCDB_ATTR_ID;\r
-                o->start = -1;\r
-            } else {\r
-                kcdb_identity_release(id_def);\r
-            }\r
-\r
-            if (o->start != -1)\r
-                goto done_with_defident;\r
-\r
-            if (flags & KCDB_IDENT_FLAG_STICKY)\r
-                o->flags |= KHUI_CW_O_STICKY;\r
-            else\r
-                o->flags &= ~KHUI_CW_O_STICKY;\r
-\r
-            o->start = n_rows;\r
-            o->length = 1;\r
-            o->idx_start = -1;\r
-            o->idx_end = -1;\r
-\r
-            if (grouping[0] == tbl->n_cols - 1)\r
-                o->flags |= KHUI_CW_O_NOOUTLINE;\r
-\r
-            cw_set_tbl_row_header(tbl, n_rows, grouping[0], o);\r
-\r
-            n_rows ++;\r
-\r
-        done_with_defident:\r
-            ;\r
-        }\r
-\r
-        if (kcdb_identity_enum(KCDB_IDENT_FLAG_STICKY,\r
-                               KCDB_IDENT_FLAG_STICKY,\r
-                               NULL,\r
-                               &cb_names,\r
-                               &n_idents) != KHM_ERROR_TOO_LONG ||\r
-            n_idents == 0 ||\r
-            cb_names == 0)\r
-            goto _cleanup_sticky;\r
-\r
-        idnames = PMALLOC(cb_names);\r
-        idarray = PMALLOC(n_idents * sizeof(*idarray));\r
-#ifdef DEBUG\r
-        assert(idnames);\r
-        assert(idarray);\r
-#endif\r
-\r
-        if (KHM_FAILED(kcdb_identity_enum(KCDB_IDENT_FLAG_STICKY,\r
-                                          KCDB_IDENT_FLAG_STICKY,\r
-                                          idnames,\r
-                                          &cb_names,\r
-                                          &n_idents)))\r
-            goto _cleanup_sticky;\r
-\r
-        for (i=0, t=idnames; t && *t; t = multi_string_next(t), i++) {\r
-            idarray[i] = t;\r
-        }\r
-\r
-        qsort(idarray, n_idents, sizeof(*idarray), iwcscmp);\r
-\r
-        for (i=0; i < (int) n_idents; i++) {\r
-            khm_handle h;\r
-\r
-            if (KHM_FAILED(kcdb_identity_create(idarray[i], \r
-                                                KCDB_IDENT_FLAG_CREATE, &h)))\r
-                continue;\r
-\r
-            for (o = tbl->outline; o; o = LNEXT(o)) {\r
-                if (!wcscmp(idarray[i], o->header))\r
-                    break;\r
-            }\r
-\r
-            if (o) {\r
-                /* found it */\r
-                if (o->start != -1) /* already visible? */\r
-                    continue;\r
-                o->flags &= KHUI_CW_O_RELIDENT;\r
-                o->flags |= KHUI_CW_O_STICKY | KHUI_CW_O_VISIBLE | KHUI_CW_O_EMPTY;\r
-\r
-                if (!kcdb_identity_is_equal(o->data, h)) {\r
-                    if (o->flags & KHUI_CW_O_RELIDENT)\r
-                        kcdb_identity_release(o->data);\r
-                    o->data = h;\r
-                    o->flags |= KHUI_CW_O_RELIDENT;\r
-                    kcdb_identity_hold(h);\r
-                }\r
-            } else {\r
-                /* not found.  create */\r
-                o = cw_new_outline_node(idarray[i]);\r
-                LPUSH(&tbl->outline, o);\r
-                o->flags = KHUI_CW_O_STICKY | KHUI_CW_O_VISIBLE | KHUI_CW_O_EMPTY | KHUI_CW_O_RELIDENT;\r
-                o->level = 0;\r
-                o->col = grouping[0];\r
-                o->data = h;\r
-                kcdb_identity_hold(h);\r
-                o->attr_id = KCDB_ATTR_ID;\r
-            }\r
-\r
-            if (grouping[0] == tbl->n_cols - 1)\r
-                o->flags |= KHUI_CW_O_NOOUTLINE;\r
-\r
-            kcdb_identity_release(h);\r
-\r
-            o->flags &= ~KHUI_CW_O_EXPAND;\r
-            o->start = n_rows;\r
-            o->length = 1;\r
-            o->idx_start = -1;\r
-            o->idx_end = -1;\r
-\r
-            cw_set_tbl_row_header(tbl, n_rows, grouping[0], o);\r
-\r
-            n_rows ++;\r
-        }\r
-\r
-    _cleanup_sticky:\r
-        if (idnames)\r
-            PFREE(idnames);\r
-        if (idarray)\r
-            PFREE(idarray);\r
-    }\r
-\r
-    tbl->n_rows = n_rows;\r
-    tbl->flags |= KHUI_CW_TBL_ROW_DIRTY;\r
-\r
-    tbl->flags &= ~KHUI_CW_TBL_COL_DIRTY;\r
-\r
-    if (tbl->cursor_row >= tbl->n_rows)\r
-        tbl->cursor_row = tbl->n_rows - 1;\r
-    if (tbl->cursor_row < 0)\r
-        tbl->cursor_row = 0;\r
-_exit:\r
-    if(grouping)\r
-        PFREE(grouping);\r
-\r
-    /* note that the expstate is derived from whether or not \r
-     * we have expiration states set for any active identities */\r
-    if (n_creds == 0)\r
-        khm_notify_icon_expstate(KHM_NOTIF_EMPTY);\r
-    else if ((expstate & CW_EXPSTATE_EXPIRED) == CW_EXPSTATE_EXPIRED)\r
-        khm_notify_icon_expstate(KHM_NOTIF_EXP);\r
-    else if ((expstate & CW_EXPSTATE_WARN) == CW_EXPSTATE_WARN ||\r
-             (expstate & CW_EXPSTATE_CRITICAL) == CW_EXPSTATE_CRITICAL)\r
-        khm_notify_icon_expstate(KHM_NOTIF_WARN);\r
-    else\r
-        khm_notify_icon_expstate(KHM_NOTIF_OK);\r
-}\r
-\r
-void \r
-cw_unload_view(khui_credwnd_tbl * tbl)\r
-{\r
-#define SafeDeleteObject(o) \\r
-    do { \\r
-        if(o) { \\r
-            DeleteObject(o); \\r
-            o = NULL; \\r
-        } \\r
-    } while(0)\r
-\r
-    SafeDeleteObject(tbl->hf_header);\r
-    SafeDeleteObject(tbl->hf_normal);\r
-    SafeDeleteObject(tbl->hf_bold);\r
-    SafeDeleteObject(tbl->hf_bold_header);\r
-\r
-    SafeDeleteObject(tbl->hb_grey);\r
-    SafeDeleteObject(tbl->hb_normal);\r
-    SafeDeleteObject(tbl->hb_s);\r
-\r
-    SafeDeleteObject(tbl->hb_hdr_bg);\r
-    SafeDeleteObject(tbl->hb_hdr_bg_crit);\r
-    SafeDeleteObject(tbl->hb_hdr_bg_exp);\r
-    SafeDeleteObject(tbl->hb_hdr_bg_warn);\r
-    SafeDeleteObject(tbl->hb_hdr_bg_def);\r
-\r
-    SafeDeleteObject(tbl->hb_hdr_bg_s);\r
-    SafeDeleteObject(tbl->hb_hdr_bg_crit_s);\r
-    SafeDeleteObject(tbl->hb_hdr_bg_exp_s);\r
-    SafeDeleteObject(tbl->hb_hdr_bg_warn_s);\r
-    SafeDeleteObject(tbl->hb_hdr_bg_def_s);\r
-\r
-#undef SafeDeleteObject\r
-\r
-    if (tbl->hi_lg_ident) {\r
-        DestroyIcon(tbl->hi_lg_ident);\r
-        tbl->hi_lg_ident = NULL;\r
-    }\r
-\r
-    if(tbl->credset) {\r
-        kcdb_credset_delete(tbl->credset);\r
-        tbl->credset = NULL;\r
-    }\r
-    if(tbl->ilist) {\r
-        khui_delete_ilist(tbl->ilist);\r
-        tbl->ilist = NULL;\r
-    }\r
-\r
-    if(tbl->cols) {\r
-        int i;\r
-\r
-        for(i=0; i < tbl->n_cols; i++) {\r
-            if(tbl->cols[i].title)\r
-                PFREE(tbl->cols[i].title);\r
-            Header_DeleteItem(tbl->hwnd_header, 0);\r
-\r
-            if (tbl->cols[i].attr_id >= 0 &&\r
-                tbl->cols[i].attr_id <= KCDB_ATTR_MAX_ID &&\r
-                attr_to_action[tbl->cols[i].attr_id]) {\r
-\r
-                khui_check_action(attr_to_action[tbl->cols[i].attr_id], FALSE);\r
-\r
-            }\r
-        }\r
-        PFREE(tbl->cols);\r
-        tbl->cols = NULL;\r
-        tbl->n_cols = 0;\r
-        tbl->n_total_cols = 0;\r
-\r
-        kmq_post_message(KMSG_ACT, KMSG_ACT_REFRESH, 0, 0);\r
-    }\r
-\r
-    if(tbl->rows) {\r
-        PFREE(tbl->rows);\r
-        tbl->rows = NULL;\r
-        tbl->n_rows = 0;\r
-        tbl->n_total_rows = 0;\r
-    }\r
-\r
-    khui_delete_bitmap(&tbl->kbm_logo_shade);\r
-\r
-    if (tbl->csp_view) {\r
-        khc_close_space(tbl->csp_view);\r
-        tbl->csp_view = NULL;\r
-    }\r
-\r
-    tbl->cell_height = 0;       /* recalculate cell height next time */\r
-\r
-    if (tbl->idents) {\r
-        khm_size i;\r
-\r
-        for (i=0; i < tbl->n_idents; i++) {\r
-            if (tbl->idents[i].ident) {\r
-                kcdb_identity_release(tbl->idents[i].ident);\r
-            }\r
-        }\r
-\r
-        PFREE(tbl->idents);\r
-        tbl->idents = NULL;\r
-        tbl->n_idents = 0;\r
-        tbl->nc_idents = 0;\r
-    }\r
-}\r
-\r
-void \r
-cw_hditem_from_tbl_col(khui_credwnd_col * col, HDITEM *phi)\r
-{\r
-    size_t cchsize;\r
-\r
-    phi->mask = HDI_FORMAT | HDI_LPARAM | HDI_WIDTH;\r
-    if(cw_is_custom_attr(col->attr_id)) {\r
-        if(col->attr_id == CW_CA_FLAGS) {\r
-            phi->fmt = 0;\r
-        } else if(col->attr_id == CW_CA_TYPEICON) {\r
-            phi->fmt = 0;\r
-        } else {\r
-            /* what the? */\r
-            /*TODO: throw up and die */\r
-        }\r
-    } else {\r
-        phi->mask |= HDI_TEXT;\r
-        phi->pszText = col->title;\r
-        StringCchLength(col->title, KCDB_MAXCCH_SHORT_DESC, &cchsize);\r
-        phi->cchTextMax = (int) cchsize;\r
-        phi->fmt = HDF_CENTER | HDF_STRING;\r
-    }\r
-    phi->lParam = col->attr_id;\r
-#if (_WIN32_WINNT >= 0x501)\r
-    if (IS_COMMCTL6()) {\r
-        if(col->flags & KHUI_CW_COL_SORT_INC) {\r
-            phi->fmt |= HDF_SORTUP;\r
-        } else if(col->flags & KHUI_CW_COL_SORT_DEC) {\r
-            phi->fmt |= HDF_SORTDOWN;\r
-        }\r
-    }\r
-#endif\r
-    if(col->width < 0) {\r
-        /*TODO: come up with a better way to handle this case */\r
-        col->width = 200;\r
-    }\r
-    phi->cxy = col->width;\r
-}\r
-\r
-int\r
-cw_get_cell_height(HDC hdc, HFONT hf) {\r
-    SIZE size;\r
-    size_t cbbuf;\r
-    wchar_t buf[64];\r
-    HFONT hfold = NULL;\r
-\r
-    if (hf)\r
-        hfold = SelectFont(hdc, hf);\r
-\r
-    LoadString(khm_hInstance, IDS_SAMPLE_STRING, buf, sizeof(buf)/sizeof(buf[0]));\r
-    StringCchLength(buf, sizeof(buf)/sizeof(buf[0]), &cbbuf);\r
-    GetTextExtentPoint32(hdc, buf, (int) cbbuf, &size);\r
-\r
-    if (hf)\r
-        SelectFont(hdc, hfold);\r
-\r
-    return size.cy;\r
-}\r
-\r
-int\r
-cw_update_header_column_width(khui_credwnd_tbl * tbl, int c) {\r
-    int idx;\r
-    HDITEM hi;\r
-\r
-#ifdef DEBUG\r
-    assert(c >= 0 && c < tbl->n_cols);\r
-#endif\r
-\r
-    if (tbl->hwnd_header == NULL)\r
-        return 0;\r
-\r
-    idx = Header_OrderToIndex(tbl->hwnd_header, c);\r
-    ZeroMemory(&hi, sizeof(hi));\r
-    hi.mask = HDI_WIDTH;\r
-    hi.cxy = tbl->cols[c].width;\r
-    return Header_SetItem(tbl->hwnd_header, idx, &hi);\r
-}\r
-\r
-/* returns a bitmask indicating which measures were changed */\r
-int \r
-cw_update_extents(khui_credwnd_tbl * tbl, \r
-                  khm_boolean update_scroll) {\r
-    int ext_x = 0;\r
-    int ext_y = 0;\r
-    int i;\r
-    int filler_col = -1;\r
-    int fill_adjusted = 0;\r
-\r
- recompute_columns:\r
-\r
-    ext_x = 0;\r
-    for(i=0; i < (int) tbl->n_cols; i++) {\r
-        tbl->cols[i].x = ext_x;\r
-        if (tbl->cols[i].flags & KHUI_CW_COL_FILLER) {\r
-            if (filler_col == -1)\r
-                filler_col = i;\r
-        }\r
-        ext_x += tbl->cols[i].width;\r
-    }\r
-\r
-    if (filler_col != -1 && !fill_adjusted) {\r
-        RECT r;\r
-        int delta;\r
-\r
-        GetClientRect(tbl->hwnd, &r);\r
-\r
-        /* we decrement the width so that the width data area is\r
-           strictly less than the width of the client area.  Windows\r
-           doesn't disable a scrollbar unless the range is strictly\r
-           less than the page size. */\r
-        delta = ((r.right - r.left) - 1) - ext_x;\r
-\r
-        if (tbl->cols[filler_col].width + delta <= GetSystemMetrics(SM_CXSMICON)) {\r
-            tbl->cols[filler_col].width = GetSystemMetrics(SM_CXICON);\r
-        } else {\r
-            tbl->cols[filler_col].width += delta;\r
-        }\r
-\r
-        cw_update_header_column_width(tbl, filler_col);\r
-\r
-        fill_adjusted = 1;\r
-        goto recompute_columns;\r
-    }\r
-\r
-    if(!tbl->cell_height) {\r
-        HDC dc;\r
-        int maxheight = 0;\r
-        int height;\r
-\r
-        dc = GetWindowDC(tbl->hwnd);\r
-\r
-        maxheight = cw_get_cell_height(dc, tbl->hf_normal);\r
-        height = cw_get_cell_height(dc, tbl->hf_bold);\r
-        if (height > maxheight)\r
-            maxheight = height;\r
-        height = cw_get_cell_height(dc, tbl->hf_header);\r
-        if (height > maxheight)\r
-            maxheight = height;\r
-        height = cw_get_cell_height(dc, tbl->hf_bold_header);\r
-        if (height > maxheight)\r
-            maxheight = height;\r
-\r
-        ReleaseDC(tbl->hwnd, dc);\r
-\r
-        tbl->cell_height = height + tbl->vpad * 2;\r
-    }\r
-\r
-    if (tbl->flags & KHUI_CW_TBL_EXPIDENT) {\r
-        RECT r;\r
-\r
-        ext_y = 0;\r
-        r.left = 0;\r
-        r.right = ext_x;\r
-\r
-        for (i=0; i < (int) tbl->n_rows; i++) {\r
-            r.top = ext_y;\r
-            if (tbl->rows[i].flags & KHUI_CW_ROW_EXPVIEW) {\r
-                ext_y += tbl->cell_height * CW_EXP_ROW_MULT;\r
-            } else {\r
-                ext_y += tbl->cell_height;\r
-            }\r
-            r.bottom = ext_y;\r
-            tbl->rows[i].r_ext = r;\r
-        }\r
-    } else {\r
-        RECT r;\r
-\r
-        r.left = 0;\r
-        r.right = ext_x;\r
-\r
-        for (i=0; i < (int) tbl->n_rows; i++) {\r
-            r.top = i * tbl->cell_height;\r
-            r.bottom = r.top + tbl->cell_height;\r
-\r
-            tbl->rows[i].r_ext = r;\r
-        }\r
-\r
-        ext_y = (int) tbl->n_rows * tbl->cell_height;\r
-    }\r
-\r
-    tbl->ext_width = ext_x;\r
-    tbl->ext_height = ext_y;\r
-\r
-    /* useful in the future when implementing variable height rows.\r
-       The KHUI_CW_TBL_ROW_DIRTY bit indicates that the rows have\r
-       changed and that the y extent has to be recalculated. */\r
-    tbl->flags &= ~KHUI_CW_TBL_ROW_DIRTY;\r
-\r
-    if(update_scroll) {\r
-        RECT r;\r
-        int cl_w;\r
-        int cl_h;\r
-        SCROLLINFO si;\r
-        WINDOWPOS pw;\r
-        HDLAYOUT hdl;\r
-\r
-        /* update the header control first */\r
-\r
-    retry_update_scroll:\r
-        GetClientRect(tbl->hwnd, &r);\r
-\r
-        cl_w = r.right - r.left;\r
-        cl_h = (r.bottom - r.top);\r
-        cl_h -= tbl->header_height;\r
-\r
-        if(tbl->scr_top < 0 || tbl->ext_height < cl_h)\r
-            tbl->scr_top = 0;\r
-        else if(tbl->scr_top > tbl->ext_height - cl_h)\r
-            tbl->scr_top = tbl->ext_height - cl_h;\r
-        if(tbl->scr_left < 0 || tbl->ext_width < cl_w)\r
-            tbl->scr_left = 0;\r
-        else if(tbl->scr_left > tbl->ext_width - cl_w)\r
-            tbl->scr_left = tbl->ext_width - cl_w;\r
-\r
-        /* adjustments for scrolling */\r
-        r.left -= tbl->scr_left;\r
-        r.right = max(tbl->ext_width + r.left, r.right);\r
-\r
-        hdl.prc = &r;\r
-        hdl.pwpos = &pw;\r
-\r
-        Header_Layout(tbl->hwnd_header, &hdl);\r
-\r
-        if(tbl->header_height == 0) {\r
-            tbl->header_height = pw.cy;\r
-            goto retry_update_scroll;\r
-        } else\r
-            tbl->header_height = pw.cy;\r
-\r
-        SetWindowPos(\r
-            tbl->hwnd_header, \r
-            pw.hwndInsertAfter, \r
-            pw.x, \r
-            pw.y, \r
-            pw.cx, \r
-            pw.cy, \r
-            pw.flags);\r
-\r
-        si.cbSize = sizeof(si);\r
-        si.nMin = 0;\r
-        si.nMax = tbl->ext_height;\r
-        si.nPage = cl_h;\r
-        si.nPos = tbl->scr_top;\r
-        si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;\r
-        SetScrollInfo(tbl->hwnd, SB_VERT, &si, TRUE);\r
-\r
-        si.cbSize = sizeof(si);\r
-        si.nMin = 0;\r
-        si.nMax = tbl->ext_width;\r
-        si.nPage = cl_w;\r
-        si.nPos = tbl->scr_left;\r
-        si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;\r
-        SetScrollInfo(tbl->hwnd, SB_HORZ, &si, TRUE);\r
-    }\r
-\r
-    return 0;\r
-}\r
-\r
-void \r
-cw_insert_header_cols(khui_credwnd_tbl * tbl) {\r
-    HWND hdr;\r
-    HDITEM hi;\r
-    int i;\r
-\r
-    hdr = tbl->hwnd_header;\r
-    \r
-    for(i=0; i < (int) tbl->n_cols; i++) {\r
-        cw_hditem_from_tbl_col(&(tbl->cols[i]), &hi);\r
-        Header_InsertItem(hdr, 512, &hi);\r
-    }\r
-}\r
-\r
-#define CW_ER_BLANK 0\r
-#define CW_ER_GREY  1\r
-#define CW_ER_SEL   2\r
-\r
-void \r
-cw_erase_rect(HDC hdc, \r
-              khui_credwnd_tbl * tbl, \r
-              RECT * r_wnd, \r
-              RECT * r_erase, \r
-              int type)\r
-{\r
-    RECT rlogo;\r
-    RECT ri;\r
-    RECT t;\r
-    BOOL rie;\r
-    HBRUSH hbr;\r
-\r
-    switch(type) {\r
-    case CW_ER_BLANK:\r
-        hbr = tbl->hb_normal;\r
-        break;\r
-\r
-    case CW_ER_GREY:\r
-        hbr = tbl->hb_grey;\r
-        break;\r
-\r
-    case CW_ER_SEL:\r
-        hbr = tbl->hb_s;\r
-        break;\r
-\r
-    default:\r
-        return;\r
-    }\r
-\r
-    if(tbl->kbm_logo_shade.cx != -1 && type == CW_ER_BLANK) {\r
-        rlogo.left = r_wnd->right - tbl->kbm_logo_shade.cx;\r
-        rlogo.right = r_wnd->right;\r
-        rlogo.top = r_wnd->bottom - tbl->kbm_logo_shade.cy;\r
-        rlogo.bottom = r_wnd->bottom;\r
-        rie = IntersectRect(&ri, r_erase, &rlogo);\r
-    } else {\r
-        ZeroMemory(&rlogo, sizeof(rlogo));\r
-        ZeroMemory(&ri, sizeof(ri));\r
-        rie = FALSE;\r
-    }\r
-\r
-    if(!rie) {\r
-        FillRect(hdc, r_erase, hbr);\r
-    } else {\r
-        HDC hdcb = CreateCompatibleDC(hdc);\r
-        HBITMAP hbmold = SelectObject(hdcb, tbl->kbm_logo_shade.hbmp);\r
-        \r
-        BitBlt(hdc, ri.left, ri.top, ri.right - ri.left, ri.bottom - ri.top,\r
-               hdcb, ri.left - rlogo.left, ri.top - rlogo.top, SRCCOPY);\r
-            \r
-        SelectObject(hdcb, hbmold);\r
-        DeleteDC(hdcb);\r
-\r
-        if(r_erase->top < ri.top && r_erase->left < ri.left) {\r
-            t.left = r_erase->left;\r
-            t.top = r_erase->top;\r
-            t.right = ri.left;\r
-            t.bottom = ri.top;\r
-            FillRect(hdc, &t, hbr);\r
-        }\r
-\r
-        if(r_erase->left < ri.left) {\r
-            t.left = r_erase->left;\r
-            t.top = ri.top;\r
-            t.right = ri.left;\r
-            t.bottom = ri.bottom;\r
-            FillRect(hdc, &t, hbr);\r
-        }\r
-\r
-        if(r_erase->top < ri.top) {\r
-            t.left = ri.left;\r
-            t.top = r_erase->top;\r
-            t.right = ri.right;\r
-            t.bottom = ri.top;\r
-            FillRect(hdc, &t, hbr);\r
-        }\r
-    }\r
-}\r
-\r
-void \r
-cw_draw_header(HDC hdc, \r
-               khui_credwnd_tbl * tbl, \r
-               int row, \r
-               RECT * r)\r
-{\r
-    int colattr;\r
-    HPEN pl, pold;\r
-    khui_credwnd_row * cr;\r
-    khui_credwnd_outline * o;\r
-    int selected = 0;\r
-    khm_int32 idf = 0;\r
-\r
-    /* each header consists of a couple of widgets and some text */\r
-    /* we need to figure out the background color first */\r
-    \r
-    cr = &(tbl->rows[row]);\r
-    o = (khui_credwnd_outline *) cr->data;\r
-\r
-    colattr = tbl->cols[cr->col].attr_id;\r
-\r
-    if (colattr == KCDB_ATTR_ID_NAME) {\r
-        khm_handle ident = o->data;\r
-\r
-        kcdb_identity_get_flags(ident, &idf);\r
-    }\r
-\r
-    selected = o->flags & KHUI_CW_O_SELECTED;\r
-\r
-    {\r
-        HBRUSH hbr;\r
-\r
-        if(selected) {\r
-            if ((o->flags & CW_EXPSTATE_MASK) == CW_EXPSTATE_EXPIRED)\r
-                hbr = tbl->hb_hdr_bg_exp_s;\r
-            else if ((o->flags & CW_EXPSTATE_MASK) == CW_EXPSTATE_CRITICAL)\r
-                hbr = tbl->hb_hdr_bg_crit_s;\r
-            else if ((o->flags & CW_EXPSTATE_MASK) == CW_EXPSTATE_WARN)\r
-                hbr = tbl->hb_hdr_bg_warn_s;\r
-            else if (idf & KCDB_IDENT_FLAG_DEFAULT)\r
-                hbr = tbl->hb_hdr_bg_def_s;\r
-            else\r
-                hbr = tbl->hb_hdr_bg_s;\r
-        } else {\r
-            if ((o->flags & CW_EXPSTATE_MASK) == CW_EXPSTATE_EXPIRED)\r
-                hbr = tbl->hb_hdr_bg_exp;\r
-            else if ((o->flags & CW_EXPSTATE_MASK) == CW_EXPSTATE_CRITICAL)\r
-                hbr = tbl->hb_hdr_bg_crit;\r
-            else if ((o->flags & CW_EXPSTATE_MASK) == CW_EXPSTATE_WARN)\r
-                hbr = tbl->hb_hdr_bg_warn;\r
-            else if (idf & KCDB_IDENT_FLAG_DEFAULT)\r
-                hbr = tbl->hb_hdr_bg_def;\r
-            else\r
-                hbr = tbl->hb_hdr_bg;\r
-        }\r
-\r
-        FillRect(hdc, r, hbr);\r
-    }\r
-\r
-    /* draw the background */\r
-    pl = CreatePen(PS_SOLID, 0, tbl->cr_hdr_outline);\r
-    pold = SelectObject(hdc, pl);\r
-    MoveToEx(hdc, r->left, r->bottom - 1, NULL);\r
-    LineTo(hdc,r->right,r->bottom - 1);\r
-    SelectObject(hdc, pold);\r
-    DeleteObject(pl);\r
-\r
-    if (!(o->flags & KHUI_CW_O_NOOUTLINE) &&\r
-        !(o->flags & KHUI_CW_O_EMPTY)) {\r
-        if((tbl->mouse_state & CW_MOUSE_WOUTLINE) && \r
-           tbl->mouse_row == row) {\r
-            if(o->flags & KHUI_CW_O_EXPAND) {\r
-                khui_ilist_draw_id(tbl->ilist, IDB_WDG_EXPAND_HI,\r
-                                   hdc, r->left,\r
-                                   (r->top + r->bottom - KHUI_SMICON_CY) / 2, 0);\r
-            } else {\r
-                khui_ilist_draw_id(tbl->ilist, IDB_WDG_COLLAPSE_HI,\r
-                                   hdc, r->left,\r
-                                   (r->top + r->bottom - KHUI_SMICON_CY) / 2, 0);\r
-            }\r
-        } else {\r
-            if(o->flags & KHUI_CW_O_EXPAND) {\r
-                khui_ilist_draw_id(tbl->ilist, IDB_WDG_EXPAND,\r
-                                   hdc, r->left,\r
-                                   (r->top + r->bottom - KHUI_SMICON_CY) / 2, 0);\r
-            } else {\r
-                khui_ilist_draw_id(tbl->ilist, IDB_WDG_COLLAPSE,\r
-                                   hdc, r->left,\r
-                                   (r->top + r->bottom - KHUI_SMICON_CY) / 2, 0);\r
-            }\r
-        }\r
-\r
-        r->left += KHUI_SMICON_CX * 3 / 2;\r
-    } else if (!(o->flags & KHUI_CW_O_NOOUTLINE)) {\r
-        r->left += KHUI_SMICON_CX * 3 / 2;\r
-    }\r
-\r
-    /* try to draw the icon, if there is one */\r
-    if(colattr == KCDB_ATTR_ID_NAME) {\r
-\r
-        khui_ilist_draw_id(tbl->ilist,\r
-                           (((tbl->mouse_state & CW_MOUSE_WSTICKY) &&\r
-                             tbl->mouse_row == row)?\r
-                            ((idf & KCDB_IDENT_FLAG_STICKY)?\r
-                             IDB_WDG_STUCK_HI:\r
-                             IDB_WDG_STICK_HI):\r
-                            ((idf & KCDB_IDENT_FLAG_STICKY)?\r
-                             IDB_WDG_STUCK:\r
-                             IDB_WDG_STICK)),\r
-                           hdc,\r
-                           r->left,\r
-                           (r->top + r->bottom - KHUI_SMICON_CY) / 2, 0);\r
-\r
-        r->left += KHUI_SMICON_CX * 3 / 2;\r
-\r
-        /* the TRUE part of the 'if' is for drawing large icons.  It's\r
-           disabled for now until we have new icons. */\r
-        if ((cr->flags & KHUI_CW_ROW_EXPVIEW) && FALSE) {\r
-            int cx = GetSystemMetrics(SM_CXICON);\r
-            int cy = GetSystemMetrics(SM_CYICON);\r
-\r
-            DrawIcon(hdc, r->left, (r->top + r->bottom - cy) / 2, tbl->hi_lg_ident);\r
-\r
-            r->left += cx + KHUI_SMICON_CX / 2;\r
-\r
-        } else {\r
-            khui_ilist_draw_id(tbl->ilist, \r
-                               ((o->flags & KHUI_CW_O_EMPTY)?\r
-                                IDB_ID_DIS_SM:\r
-                                IDB_ID_SM), \r
-                               hdc,\r
-                               r->left,\r
-                               (r->top + r->bottom - KHUI_SMICON_CY) / 2, 0);\r
-            r->left += KHUI_SMICON_CX * 3 / 2 ;\r
-        }\r
-    }\r
-\r
-\r
-    if (!(cr->flags & KHUI_CW_ROW_EXPVIEW)) {\r
-\r
-        SetTextAlign(hdc, TA_BOTTOM | TA_LEFT);\r
-\r
-        if(selected)\r
-            SetTextColor(hdc, tbl->cr_hdr_s);\r
-        else\r
-            SetTextColor(hdc, tbl->cr_hdr_normal);\r
-\r
-        TextOut(hdc, r->left, r->bottom - tbl->vpad, o->header, (int) wcslen(o->header));\r
-\r
-        if (colattr == KCDB_ATTR_ID_NAME &&\r
-            (idf & KCDB_IDENT_FLAG_DEFAULT)) {\r
-            wchar_t defstr[64];\r
-            SIZE size;\r
-\r
-            LoadString(khm_hInstance, IDS_CW_DEFAULT,\r
-                       defstr, ARRAYLENGTH(defstr));\r
-\r
-            GetTextExtentPoint32(hdc, o->header, (int) wcslen(o->header),\r
-                                 &size);\r
-\r
-            r->left += size.cx + KHUI_SMICON_CX * 2;\r
-\r
-            TextOut(hdc, r->left, r->bottom - tbl->vpad, \r
-                    defstr, (int) wcslen(defstr));\r
-        }\r
-    } else {\r
-\r
-        RECT tr;\r
-        int len;\r
-        wchar_t typestr[128];\r
-        int cx_id;\r
-        SIZE size;\r
-        khui_credwnd_ident * cwi;\r
-\r
-        /* expanded view */\r
-#ifdef DEBUG\r
-        assert(colattr == KCDB_ATTR_ID_NAME);\r
-#endif\r
-\r
-        cwi = cw_find_ident(tbl, o->data);\r
-\r
-        CopyRect(&tr, r);\r
-        tr.bottom -= (tr.bottom - tr.top) / 2; /* drawing two lines of text */\r
-\r
-        if (selected)\r
-            SetTextColor(hdc, tbl->cr_hdr_s);\r
-        else\r
-            SetTextColor(hdc, tbl->cr_hdr_normal);\r
-\r
-        len = (int) wcslen(o->header);\r
-        DrawText(hdc, o->header, len, &tr, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);\r
-        GetTextExtentPoint32(hdc, o->header, (int) len, &size);\r
-        cx_id = size.cx;\r
-\r
-        typestr[0] = L'\0';\r
-\r
-        if ((idf & KCDB_IDENT_FLAG_DEFAULT)) {\r
-            if (cwi && cwi->credtype_name[0]) {\r
-                wchar_t fmt[64];\r
-\r
-                LoadString(khm_hInstance, IDS_CW_DEFAULTTF,\r
-                           fmt, ARRAYLENGTH(fmt));\r
-                StringCbPrintf(typestr, sizeof(typestr), fmt,\r
-                               cwi->credtype_name);\r
-            } else {\r
-                LoadString(khm_hInstance, IDS_CW_DEFAULT,\r
-                           typestr, ARRAYLENGTH(typestr));\r
-            }\r
-        } else if (cwi && cwi->credtype_name[0]) {\r
-            wchar_t fmt[64];\r
-\r
-            LoadString(khm_hInstance, IDS_CW_TYPEF,\r
-                       fmt, ARRAYLENGTH(fmt));\r
-            StringCbPrintf(typestr, sizeof(typestr), fmt,\r
-                           cwi->credtype_name);\r
-        }\r
-\r
-        if (typestr[0]) {\r
-            int cx_str;\r
-\r
-            len = (int) wcslen(typestr);\r
-            GetTextExtentPoint32(hdc, typestr, (int) len, &size);\r
-            cx_str = size.cx + KHUI_SMICON_CX / 2;\r
-\r
-            tr.left = max(tr.right - cx_str, tr.left + cx_id + KHUI_SMICON_CX * 2);\r
-            if (selected)\r
-                SetTextColor(hdc, tbl->cr_hdr_gray_s);\r
-            else\r
-                SetTextColor(hdc, tbl->cr_hdr_gray);\r
-            DrawText(hdc, typestr, len, &tr, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);\r
-        }\r
-\r
-        CopyRect(&tr, r);\r
-        tr.top += (tr.bottom - tr.top) / 2;\r
-\r
-        if (1) {\r
-            wchar_t buf[128];\r
-            khui_credwnd_ident * cwi;\r
-\r
-            buf[0] = L'\0';\r
-            cwi = cw_find_ident(tbl, o->data);\r
-\r
-            if (cwi) {\r
-#ifdef SHOW_CREDENTIAL_COUNTS\r
-                if (cwi->credcount == 0)\r
-                    LoadString(khm_hInstance, IDS_IDEXPDISP_NOCRED,\r
-                               buf, ARRAYLENGTH(buf));\r
-                else if (cwi->credcount == 1)\r
-                    LoadString(khm_hInstance, IDS_IDEXPDISP_1CRED,\r
-                               buf, ARRAYLENGTH(buf));\r
-                else {\r
-                    wchar_t fmt[128];\r
-                    LoadString(khm_hInstance, IDS_IDEXPDISP_NCRED,\r
-                               fmt, ARRAYLENGTH(fmt));\r
-                    StringCbPrintf(buf, sizeof(buf), fmt, (int) cwi->credcount);\r
-                }\r
-#else\r
-                if (FtToInt(&cwi->ft_expire) != 0) {\r
-                    FILETIME ft_now;\r
-\r
-                    GetSystemTimeAsFileTime(&ft_now);\r
-                    if (CompareFileTime(&cwi->ft_expire, &ft_now) > 0) {\r
-                        wchar_t fmt[64];\r
-                        wchar_t intstr[128];\r
-                        FILETIME ft;\r
-                        khm_size cb;\r
-\r
-                        ft = FtSub(&cwi->ft_expire, &ft_now);\r
-                        intstr[0] = L'\0';\r
-                        cb = sizeof(intstr);\r
-                        FtIntervalToString(&ft, intstr, &cb);\r
-\r
-                        LoadString(khm_hInstance, IDS_CW_EXPIREF,\r
-                                   fmt, ARRAYLENGTH(fmt));\r
-                        StringCbPrintf(buf, sizeof(buf), fmt, intstr);\r
-                    } else {\r
-                        LoadString(khm_hInstance, IDS_CW_EXPIRED,\r
-                                   buf, ARRAYLENGTH(buf));\r
-                    }\r
-                }\r
-#endif\r
-\r
-                len = (int) wcslen(buf);\r
-\r
-                if (selected)\r
-                    SetTextColor(hdc, tbl->cr_hdr_gray_s);\r
-                else\r
-                    SetTextColor(hdc, tbl->cr_hdr_gray);\r
-                DrawText(hdc, buf, len, &tr, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);\r
-            }\r
-        }\r
-    }\r
-}\r
-\r
-LRESULT \r
-cw_handle_header_msg(khui_credwnd_tbl * tbl, LPNMHEADER ph) {\r
-    RECT r;\r
-    HDITEM hi;\r
-\r
-    switch(ph->hdr.code) {\r
-        /*TODO:Make it track smoother */\r
-    case HDN_BEGINTRACK:\r
-        {\r
-            ZeroMemory(&hi, sizeof(hi));\r
-            hi.mask = HDI_ORDER;\r
-            Header_GetItem(tbl->hwnd_header, ph->iItem, &hi);\r
-\r
-            if(tbl->cols[hi.iOrder].flags & KHUI_CW_COL_FIXED_WIDTH)\r
-                return TRUE;\r
-            else\r
-                return FALSE;\r
-        }\r
-\r
-    case HDN_TRACK:\r
-        return FALSE;\r
-\r
-    case HDN_ENDTRACK:\r
-        {\r
-            int width;\r
-            hi.mask = HDI_ORDER;\r
-            Header_GetItem(ph->hdr.hwndFrom, ph->iItem, &hi);\r
-            Header_GetItemRect(ph->hdr.hwndFrom, ph->iItem, &r);\r
-            width = r.right - r.left;\r
-            if(width != tbl->cols[hi.iOrder].width) {\r
-                tbl->cols[hi.iOrder].width = width;\r
-                cw_update_extents(tbl, TRUE);\r
-                InvalidateRect(tbl->hwnd, NULL, FALSE);\r
-            }\r
-        }\r
-        break;\r
-\r
-    case HDN_BEGINDRAG:\r
-        {\r
-\r
-            ZeroMemory(&hi, sizeof(hi));\r
-            hi.mask = HDI_ORDER;\r
-            Header_GetItem(tbl->hwnd_header, ph->iItem, &hi);\r
-\r
-            if (tbl->cols[hi.iOrder].flags & KHUI_CW_COL_FIXED_POS) {\r
-                return TRUE;\r
-            } else {\r
-                return FALSE;\r
-            }\r
-        }\r
-        break;\r
-\r
-    case HDN_ENDDRAG:\r
-        {\r
-            int drag_start_index;\r
-            int drag_end_index;\r
-            int i;\r
-            khui_credwnd_col tcol;\r
-            int sort_index = 0;\r
-            khm_int32 old_flags;\r
-\r
-            if (ph->pitem == NULL)\r
-                return TRUE;\r
-\r
-            hi.mask = HDI_ORDER;\r
-            Header_GetItem(tbl->hwnd_header, ph->iItem, &hi);\r
-            drag_start_index = hi.iOrder;\r
-            drag_end_index = ph->pitem->iOrder;\r
-\r
-            /* the user dragged the column which was at drag_start_index\r
-               to drag_end_index. */\r
-\r
-            if (drag_end_index == drag_start_index)\r
-                return TRUE;\r
-\r
-            /* we don't allow dragging in to the "fixed" area. */\r
-            for (i=0; i < tbl->n_cols; i++) {\r
-                if (!(tbl->cols[i].flags & KHUI_CW_COL_FIXED_POS))\r
-                    break;\r
-            }\r
-\r
-            if (drag_end_index <= i)\r
-                return TRUE;\r
\r
-            tcol = tbl->cols[drag_start_index];\r
-            if (drag_end_index < drag_start_index) {\r
-                MoveMemory(&tbl->cols[drag_end_index + 1],\r
-                           &tbl->cols[drag_end_index],\r
-                           sizeof(tbl->cols[0]) *\r
-                           (drag_start_index - drag_end_index));\r
-            } else {\r
-                MoveMemory(&tbl->cols[drag_start_index],\r
-                           &tbl->cols[drag_start_index + 1],\r
-                           sizeof(tbl->cols[0]) *\r
-                           (drag_end_index - drag_start_index));\r
-            }\r
-            tbl->cols[drag_end_index] = tcol;\r
-\r
-            old_flags = tbl->cols[drag_end_index].flags;\r
-\r
-            if (drag_end_index < tbl->n_cols - 1) {\r
-                khm_int32 tflags = tbl->cols[drag_end_index + 1].flags;\r
-\r
-                if (tflags & KHUI_CW_COL_GROUP) {\r
-                    tbl->cols[drag_end_index].flags |= KHUI_CW_COL_GROUP;\r
-                }\r
-\r
-                if ((tflags & (KHUI_CW_COL_SORT_INC | KHUI_CW_COL_SORT_DEC)) &&\r
-                    !(old_flags & (KHUI_CW_COL_SORT_INC | KHUI_CW_COL_SORT_DEC)))\r
-                    tbl->cols[drag_end_index].flags |= KHUI_CW_COL_SORT_INC;\r
-            }\r
-\r
-            if (drag_end_index > 0) {\r
-                khm_int32 tflags = tbl->cols[drag_end_index - 1].flags;\r
-\r
-                if (!(tflags & KHUI_CW_COL_GROUP))\r
-                    tbl->cols[drag_end_index].flags &= ~KHUI_CW_COL_GROUP;\r
-\r
-                if (!(tflags & (KHUI_CW_COL_SORT_INC | KHUI_CW_COL_SORT_DEC)))\r
-                    tbl->cols[drag_end_index].flags &=\r
-                        ~(KHUI_CW_COL_SORT_INC | KHUI_CW_COL_SORT_DEC);\r
-            }\r
-\r
-            if (old_flags != tbl->cols[drag_end_index].flags) {\r
-                cw_hditem_from_tbl_col(&tbl->cols[drag_end_index], &hi);\r
-                hi.mask = HDI_FORMAT;\r
-                Header_SetItem(tbl->hwnd_header, ph->iItem, &hi);\r
-            }\r
-\r
-            if ((old_flags ^ tbl->cols[drag_end_index].flags) &\r
-                KHUI_CW_COL_GROUP)\r
-                tbl->flags |= KHUI_CW_TBL_COL_DIRTY;\r
-\r
-            for (i=0; i < tbl->n_cols; i++) {\r
-                if (tbl->cols[i].attr_id < 0)\r
-                    continue;\r
-\r
-                if (tbl->cols[i].flags &\r
-                    (KHUI_CW_COL_GROUP |\r
-                     KHUI_CW_COL_SORT_INC |\r
-                     KHUI_CW_COL_SORT_DEC))\r
-                    tbl->cols[i].sort_index = sort_index++;\r
-                else\r
-                    break;\r
-            }\r
-\r
-            tbl->flags |= KHUI_CW_TBL_CUSTVIEW;\r
-\r
-            cw_update_creds(tbl);\r
-            cw_update_outline(tbl);\r
-            cw_update_extents(tbl, TRUE);\r
-\r
-            InvalidateRect(tbl->hwnd, NULL, FALSE);\r
-\r
-            return FALSE;\r
-        }\r
-        break;\r
-\r
-    case HDN_ITEMCLICK:\r
-        {\r
-            int idx;\r
-            int hidx;\r
-\r
-            hi.mask = HDI_ORDER;\r
-            Header_GetItem(tbl->hwnd_header, ph->iItem, &hi);\r
-            idx = hi.iOrder;\r
-\r
-            if (idx < 0 || idx >= tbl->n_cols)\r
-                return FALSE;\r
-\r
-            if (tbl->cols[idx].flags & KHUI_CW_COL_META)\r
-                return FALSE;\r
-\r
-            if (tbl->cols[idx].flags &\r
-                (KHUI_CW_COL_SORT_INC | KHUI_CW_COL_SORT_DEC)) {\r
-\r
-                tbl->cols[idx].flags ^=\r
-                    (KHUI_CW_COL_SORT_INC | KHUI_CW_COL_SORT_DEC);\r
-\r
-                cw_hditem_from_tbl_col(&tbl->cols[idx], &hi);\r
-                hi.mask = HDI_FORMAT;\r
-                Header_SetItem(tbl->hwnd_header, ph->iItem, &hi);\r
-\r
-            } else {\r
-                int i;\r
-                int sort_idx = 0;\r
-\r
-                for (i=0; i <= idx; i++) {\r
-                    if (tbl->cols[i].attr_id < 0)\r
-                        continue;\r
-\r
-                    if (!(tbl->flags &\r
-                          (KHUI_CW_COL_SORT_INC | KHUI_CW_COL_SORT_DEC))) {\r
-                        tbl->cols[i].flags |= KHUI_CW_COL_SORT_INC;\r
-\r
-                        cw_hditem_from_tbl_col(&tbl->cols[i], &hi);\r
-                        hi.mask = HDI_FORMAT;\r
-                        hidx = Header_OrderToIndex(tbl->hwnd_header, i);\r
-                        Header_SetItem(tbl->hwnd_header, hidx, &hi);\r
-                    }\r
-\r
-                    tbl->cols[i].sort_index = sort_idx++;\r
-                }\r
-            }\r
-\r
-            tbl->flags |= KHUI_CW_TBL_CUSTVIEW;\r
-\r
-            cw_update_creds(tbl);\r
-            cw_update_outline(tbl);\r
-            cw_update_extents(tbl, TRUE);\r
-\r
-            InvalidateRect(tbl->hwnd, NULL, FALSE);\r
-\r
-        }\r
-        break;\r
-\r
-    case HDN_ITEMDBLCLICK:\r
-        {\r
-            int idx;\r
-            int hidx;\r
-\r
-            hi.mask = HDI_ORDER;\r
-            Header_GetItem(tbl->hwnd_header, ph->iItem, &hi);\r
-            idx = hi.iOrder;\r
-\r
-            if (idx == 0 || idx >= tbl->n_cols)\r
-                return FALSE;\r
-\r
-            if (tbl->cols[idx].flags & KHUI_CW_COL_GROUP) {\r
-                /* we are removing grouping from this level */\r
-\r
-                int i;\r
-\r
-                for (i=idx; i < tbl->n_cols; i++) {\r
-                    if (!(tbl->cols[i].flags & KHUI_CW_COL_GROUP))\r
-                        break;\r
-\r
-                    tbl->cols[i].flags &= ~KHUI_CW_COL_GROUP;\r
-\r
-                    cw_hditem_from_tbl_col(&tbl->cols[idx], &hi);\r
-                    hi.mask = HDI_FORMAT;\r
-                    hidx = Header_OrderToIndex(tbl->hwnd_header, i);\r
-                    Header_SetItem(tbl->hwnd_header, hidx, &hi);\r
-                }\r
-\r
-#if 0\r
-            } else if (tbl->cols[idx].flags &\r
-                       (KHUI_CW_COL_SORT_INC |\r
-                        KHUI_CW_COL_SORT_DEC)) {\r
-                int i;\r
-\r
-                /* remove the sort condition from a column */\r
-\r
-                for (i=idx; i < tbl->n_cols; i++) {\r
-                    if (!tbl->cols[i].flags &\r
-                        (KHUI_CW_COL_SORT_INC |\r
-                         KHUI_CW_COL_SORT_DEC))\r
-                        break;\r
-\r
-                    tbl->cols[i].flags &=\r
-                        ~(KHUI_CW_COL_SORT_INC |\r
-                          KHUI_CW_COL_SORT_DEC);\r
-\r
-                    cw_hditem_from_tbl_col(&tbl->cols[idx], &hi);\r
-                    hi.mask = HDI_FORMAT;\r
-                    hidx = Header_OrderToIndex(tbl->hwnd_header, i);\r
-                    Header_SetItem(tbl->hwnd_header, hidx, &hi);\r
-                }\r
-#endif\r
-            } else {\r
-                int i;\r
-                int sort_index = 0;\r
-\r
-                for (i=0; i <= idx; i++) {\r
-                    if (tbl->cols[i].attr_id < 0)\r
-                        continue;\r
-\r
-                    if (!(tbl->cols[i].flags & KHUI_CW_COL_GROUP)) {\r
-                        tbl->cols[i].flags |= KHUI_CW_COL_GROUP;\r
-\r
-                        if (!(tbl->cols[i].flags &\r
-                              (KHUI_CW_COL_SORT_INC |\r
-                               KHUI_CW_COL_SORT_DEC)))\r
-                            tbl->cols[i].flags |= KHUI_CW_COL_SORT_INC;\r
-\r
-                        cw_hditem_from_tbl_col(&tbl->cols[i], &hi);\r
-                        hi.mask = HDI_FORMAT;\r
-                        hidx = Header_OrderToIndex(tbl->hwnd_header, i);\r
-                        Header_SetItem(tbl->hwnd_header, hidx, &hi);\r
-                    }\r
-\r
-                    tbl->cols[i].sort_index = sort_index++;\r
-                }\r
-            }\r
-\r
-            tbl->flags |= KHUI_CW_TBL_COL_DIRTY;\r
-            tbl->flags |= KHUI_CW_TBL_CUSTVIEW;\r
-\r
-            cw_update_creds(tbl);\r
-            cw_update_outline(tbl);\r
-            cw_update_extents(tbl, TRUE);\r
-\r
-            InvalidateRect(tbl->hwnd, NULL, FALSE);\r
-        }\r
-        break;\r
-\r
-    case NM_CUSTOMDRAW:\r
-        {\r
-            LPNMCUSTOMDRAW cd;\r
-            int idx;\r
-\r
-            cd = (LPNMCUSTOMDRAW) ph;\r
-            switch(cd->dwDrawStage) {\r
-            case CDDS_PREPAINT:\r
-                return CDRF_NOTIFYITEMDRAW;\r
-\r
-            case CDDS_ITEMPREPAINT:\r
-                return CDRF_NOTIFYPOSTPAINT;\r
-\r
-            case CDDS_ITEMPOSTPAINT:\r
-                if(cd->lItemlParam == CW_CA_FLAGS)\r
-                    idx = IDB_WDG_FLAG;\r
-                else if(cd->lItemlParam == CW_CA_TYPEICON)\r
-                    idx = IDB_WDG_CREDTYPE;\r
-                else\r
-                    idx = -1;\r
-\r
-                khui_ilist_draw_id(tbl->ilist, idx, cd->hdc, cd->rc.left, cd->rc.top, 0);\r
-                return 0;\r
-            }\r
-        }\r
-        break;\r
-    }\r
-    return 0;\r
-}\r
-\r
-LRESULT \r
-cw_wm_create(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
-{\r
-    khui_credwnd_tbl * tbl;\r
-\r
-    kmq_subscribe_hwnd(KMSG_CRED, hwnd);\r
-    kmq_subscribe_hwnd(KMSG_KCDB, hwnd);\r
-    kmq_subscribe_hwnd(KMSG_KMM, hwnd);\r
-\r
-    /* freed in cw_wm_destroy  */\r
-    tbl = PMALLOC(sizeof(*tbl));\r
-    ZeroMemory(tbl, sizeof(*tbl));\r
-\r
-    /* some versions of VC generate portability warnings for\r
-       SetWindowLongPtr */\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-    SetWindowLongPtr(hwnd, 0, (LONG_PTR) tbl);\r
-#pragma warning(pop)\r
-\r
-    cw_refresh_attribs(hwnd);\r
-\r
-    tbl->hwnd_header = CreateWindowEx(\r
-        0,\r
-        WC_HEADER,\r
-        (LPWSTR) NULL,\r
-        WS_CHILD | HDS_BUTTONS |\r
-        HDS_FULLDRAG | HDS_HORZ | HDS_HOTTRACK |\r
-        HDS_DRAGDROP\r
-#if (_WIN32_WINNT >= 0x501)\r
-        | ((IS_COMMCTL6())?HDS_FLAT:0)\r
-#endif\r
-        ,\r
-        0,0,0,0,hwnd, (HMENU) 0, khm_hInstance, NULL);\r
-\r
-    cw_load_view(tbl, NULL /* default view */, hwnd);\r
-    cw_insert_header_cols(tbl);\r
-\r
-    cw_update_creds(tbl);\r
-    cw_update_outline(tbl);\r
-    cw_update_selection_state(tbl);\r
-    cw_update_extents(tbl, FALSE);\r
-\r
-    {\r
-        RECT rect;\r
-        WINDOWPOS pw;\r
-        HDLAYOUT hdl;\r
-\r
-        hdl.prc = &rect;\r
-        hdl.pwpos = &pw;\r
-        GetClientRect(hwnd, &rect);\r
-\r
-        Header_Layout(tbl->hwnd_header, &hdl);\r
-\r
-        SetWindowPos(\r
-            tbl->hwnd_header, \r
-            pw.hwndInsertAfter, \r
-            pw.x, \r
-            pw.y, \r
-            pw.cx, \r
-            pw.cy, \r
-            pw.flags | SWP_SHOWWINDOW);\r
-    }\r
-\r
-    return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
-}\r
-\r
-LRESULT\r
-cw_wm_destroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
-{\r
-    khui_credwnd_tbl * tbl;\r
-\r
-    kmq_unsubscribe_hwnd(KMSG_CRED, hwnd);\r
-    kmq_unsubscribe_hwnd(KMSG_KCDB, hwnd);\r
-    kmq_unsubscribe_hwnd(KMSG_KMM, hwnd);\r
-\r
-    tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-\r
-    cw_save_view(tbl, NULL);\r
-\r
-    cw_unload_view(tbl);\r
-\r
-    PFREE(tbl);\r
-    return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
-}\r
-\r
-/* handles WM_PAINT and WM_PRINTCLIENT */\r
-LRESULT \r
-cw_wm_paint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
-{\r
-    khui_credwnd_tbl * tbl;\r
-    HDC hdc;\r
-    PAINTSTRUCT ps;\r
-    RECT r,rh;\r
-    HFONT hf_old = NULL;\r
-    int row_s, row_e;\r
-    int col_s, col_e;\r
-    int i,j,x,y,xs,xe,ys,ye;\r
-    int flag_col = -1;\r
-    int d_x = -1;\r
-    int selected = 0;\r
-    int rowheight = 0;\r
-    BOOL has_dc = FALSE;\r
-\r
-    tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-\r
-    if (wParam != 0) {\r
-        /* we assume that if wParam != 0, then that contains a device\r
-           context for us to draw in.  Otherwise, we have to call\r
-           BeginPaint() to get one. */\r
-        hdc = (HDC) wParam;\r
-        has_dc = TRUE;\r
-    }\r
-\r
-    if(!has_dc && !GetUpdateRect(hwnd, &r, FALSE)) {\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-        goto _exit;\r
-    }\r
-\r
-    if (!has_dc)\r
-        hdc = BeginPaint(hwnd, &ps);\r
-\r
-    if(tbl->hf_normal)\r
-        hf_old = SelectFont(hdc, tbl->hf_normal);\r
-    SetTextAlign(hdc, TA_LEFT | TA_TOP | TA_NOUPDATECP);\r
-    SetBkMode(hdc, TRANSPARENT);\r
-\r
-    GetClientRect(hwnd,&r);\r
-    r.top += tbl->header_height;\r
-\r
-    if(tbl->n_rows) {\r
-        /* remove the notification window if there is one */\r
-        if(tbl->hwnd_notif) {\r
-            DestroyWindow(tbl->hwnd_notif);\r
-            tbl->hwnd_notif = NULL;\r
-        }\r
-        /* we compute the visible area in terms of rows and columns */\r
-        /* row_s : first visible row */\r
-        /* col_s : first visible column */\r
-        /* row_e : last visible row */\r
-        /* col_e : last visible column */\r
-        /* ys    : top edge of first visible row */\r
-        /* xs    : left edge of first visible column */\r
-\r
-        /* We *NEED* all the meta columns to be on the left */\r
-\r
-        row_s = 0;\r
-        ys = 0;\r
-        row_e = (int) tbl->n_rows;\r
-        x = 0;\r
-        col_s = -1;\r
-        col_e = -1;\r
-        xs = 0;\r
-        for(i=0; i < (int) tbl->n_cols; i++) {\r
-            if(col_e == -1 && x >= tbl->scr_left + (r.right - r.left)) {\r
-                col_e = i;\r
-            }\r
-            if(tbl->cols[i].attr_id == CW_CA_FLAGS)\r
-                flag_col = i;\r
-            if(d_x == -1 && !cw_is_custom_attr(tbl->cols[i].attr_id))\r
-                d_x = x;\r
-            x += tbl->cols[i].width;\r
-            if(col_s == -1 && x > tbl->scr_left) {\r
-                col_s = i;\r
-                xs = tbl->cols[i].x;\r
-            }\r
-        }\r
-\r
-        if(col_e == -1)\r
-            col_e = i;\r
-\r
-        if(col_s == -1)\r
-            col_s = i;\r
-\r
-        if(d_x != -1)\r
-            d_x += r.left - tbl->scr_left;\r
-\r
-        xs += r.left - tbl->scr_left;\r
-        ys += r.top - tbl->scr_top;\r
-        xe = r.left + tbl->ext_width - tbl->scr_left;\r
-        ye = r.top + tbl->ext_height - tbl->scr_top;\r
-\r
-        /* now draw */\r
-        y = ys;\r
-        for(i=row_s; i < row_e; i++) {\r
-            selected = tbl->rows[i].flags & KHUI_CW_ROW_SELECTED;\r
-            rowheight = (tbl->rows[i].flags & KHUI_CW_ROW_EXPVIEW)? tbl->cell_height * CW_EXP_ROW_MULT : tbl->cell_height;\r
-\r
-            if(tbl->cursor_row == i) {\r
-                if (tbl->rows[i].flags & KHUI_CW_ROW_HEADER)\r
-                    SelectFont(hdc, tbl->hf_bold_header);\r
-                else\r
-                    SelectFont(hdc, tbl->hf_bold);\r
-            } else if (tbl->rows[i].flags & KHUI_CW_ROW_HEADER) {\r
-                SelectFont(hdc, tbl->hf_header);\r
-            }\r
-\r
-            x = xs;\r
-            if(tbl->rows[i].flags & KHUI_CW_ROW_HEADER) {\r
-                rh.left = xs;\r
-                rh.right = xs;\r
-                for(j=col_s; j < tbl->rows[i].col; j++)\r
-                    rh.right += tbl->cols[j].width;\r
-                rh.top = y;\r
-                rh.bottom = y + rowheight;\r
-                if(rh.right > rh.left) {\r
-                    cw_erase_rect(hdc, tbl, &r, &rh, (selected)?CW_ER_SEL:CW_ER_BLANK);\r
-                }\r
-                rh.left = rh.right;\r
-                rh.right = xe;\r
-\r
-                cw_draw_header(hdc, tbl, i, &rh);\r
-            }\r
-\r
-            if(selected)\r
-                SetTextColor(hdc, tbl->cr_s);\r
-            else\r
-                SetTextColor(hdc, tbl->cr_normal);\r
-\r
-            x = xs;\r
-            rh.top = y;\r
-            rh.bottom = y + rowheight;\r
-            for(j=col_s; j < col_e; x += tbl->cols[j++].width) {\r
-                wchar_t buf[256];\r
-                khm_size cbbuf;\r
-\r
-                rh.left = x;\r
-                rh.right = x + tbl->cols[j].width;\r
-\r
-                if(!cw_is_custom_attr(tbl->cols[j].attr_id)) {\r
-                    if(!(tbl->rows[i].flags & KHUI_CW_ROW_HEADER)) {\r
-                        cw_erase_rect(hdc, tbl, &r, &rh, (selected)?CW_ER_SEL:CW_ER_BLANK);\r
-\r
-                        if(j > tbl->rows[i].col) {\r
-                            cbbuf = sizeof(buf);\r
-                            if(KHM_FAILED(kcdb_cred_get_attr_string((khm_handle) tbl->rows[i].data,\r
-                                                                    tbl->cols[j].attr_id, buf,\r
-                                                                    &cbbuf, KCDB_TS_SHORT)))\r
-                                continue;\r
-\r
-                            rh.left += tbl->hpad;\r
-                            rh.right -= tbl->hpad;\r
-\r
-                            SetTextAlign(hdc, 0);\r
-                            DrawText(hdc, buf, (int)((cbbuf / sizeof(wchar_t)) - 1), &rh,\r
-                                     DT_LEFT | DT_VCENTER | DT_NOCLIP | DT_SINGLELINE | DT_END_ELLIPSIS);\r
-                        }\r
-                    }\r
-                } else {\r
-                    cw_erase_rect(hdc, tbl, &r, &rh, (selected)?CW_ER_SEL:CW_ER_BLANK);\r
-\r
-                    if(tbl->cols[j].attr_id == CW_CA_FLAGS) {\r
-                        khui_credwnd_outline * o;\r
-                        khm_int32 flag;\r
-\r
-                        if(tbl->rows[i].flags & KHUI_CW_ROW_HEADER) {\r
-                            o = ((khui_credwnd_outline *) tbl->rows[i].data);\r
-                            if(o->flags & KHUI_CW_O_SHOWFLAG)\r
-                                flag = o->flags;\r
-                            else\r
-                                flag = 0;\r
-                        } else {\r
-                            flag = tbl->rows[i].flags;\r
-                        }\r
-\r
-                        flag &= CW_EXPSTATE_MASK;\r
-\r
-                        if(flag == CW_EXPSTATE_WARN) {\r
-                            khui_ilist_draw_id(tbl->ilist, IDB_FLAG_WARN, hdc, x, y, 0);\r
-                        } else if(flag == CW_EXPSTATE_CRITICAL) {\r
-                            khui_ilist_draw_id(tbl->ilist, IDB_FLAG_CRITICAL, hdc, x, y, 0);\r
-                        } else if(flag == CW_EXPSTATE_EXPIRED) {\r
-                            khui_ilist_draw_id(tbl->ilist, IDB_FLAG_EXPIRED, hdc, x, y, 0);\r
-                        } else if(!(tbl->rows[i].flags & KHUI_CW_ROW_HEADER)) {\r
-                            khm_int32 flags;\r
-\r
-                            if (KHM_SUCCEEDED(kcdb_cred_get_flags((khm_handle) tbl->rows[i].data, &flags)) &&\r
-                                (flags & KCDB_CRED_FLAG_RENEWABLE)) {\r
-                                khui_ilist_draw_id(tbl->ilist,\r
-                                                   IDB_FLAG_RENEW,\r
-                                                   hdc,\r
-                                                   x, y, 0);\r
-                            } else {\r
-                                khui_ilist_draw_id(tbl->ilist,\r
-                                                   IDB_TK_SM,\r
-                                                   hdc,\r
-                                                   x, y, 0);\r
-                            }\r
-                        }\r
-                    }\r
-                }\r
-            }\r
-\r
-            if(tbl->cursor_row == i) {\r
-                rh.left = tbl->scr_left;\r
-                rh.right = tbl->scr_left + tbl->ext_width;\r
-                DrawFocusRect(hdc, &rh);\r
-            }\r
-\r
-            if (tbl->cursor_row == i ||\r
-                (tbl->rows[i].flags & KHUI_CW_ROW_HEADER)) {\r
-                SelectFont(hdc, tbl->hf_normal);\r
-            }\r
-\r
-            y += rowheight;\r
-\r
-        }\r
-\r
-        if(xe < r.right) {\r
-            rh.left = xe;\r
-            rh.right = r.right;\r
-            rh.top = r.top;\r
-            rh.bottom = r.bottom;\r
-\r
-            cw_erase_rect(hdc, tbl, &r, &rh, CW_ER_BLANK);\r
-        }\r
-\r
-        if(ye < r.bottom) {\r
-            rh.left = r.left;\r
-            rh.right = (xe < r.right)?xe:r.right;\r
-            rh.top = ye;\r
-            rh.bottom = r.bottom;\r
-\r
-            cw_erase_rect(hdc, tbl, &r, &rh, CW_ER_BLANK);\r
-        }\r
-\r
-    } else {\r
-        wchar_t buf[512];\r
-        cw_erase_rect(hdc, tbl, &r, &r, CW_ER_BLANK);\r
-\r
-        if(tbl->hwnd_notif == NULL) {\r
-            LoadString(khm_hInstance, IDS_NO_CREDS, buf, sizeof(buf)/sizeof(buf[0]));\r
-            tbl->hwnd_notif = khm_create_htwnd(\r
-                tbl->hwnd,\r
-                buf,\r
-                r.left,r.top,r.right - r.left,tbl->cell_height * 4,\r
-                0,              /* This can be WS_EX_TRANSPARENT, but\r
-                                   we don't fully support it yet. */\r
-                WS_VISIBLE);\r
-            if(tbl->hwnd_notif) {\r
-                SendMessage(tbl->hwnd_notif, WM_SETFONT, (WPARAM) tbl->hf_normal, (LPARAM) FALSE);\r
-                ShowWindow(tbl->hwnd_notif, SW_SHOW);\r
-            }\r
-        }\r
-    }\r
-\r
-    if(tbl->hf_normal)\r
-        SelectFont(hdc, hf_old);\r
-\r
-    if (!has_dc)\r
-        EndPaint(hwnd,&ps);\r
-\r
- _exit:\r
-    return TRUE;\r
-}\r
-\r
-LRESULT \r
-cw_wm_size(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
-{\r
-    RECT rect;\r
-    khui_credwnd_tbl * tbl;\r
-\r
-    tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-\r
-    cw_update_extents(tbl, TRUE);\r
-\r
-    GetClientRect(hwnd, &rect);\r
-\r
-    if(tbl->hwnd_notif) {\r
-        SetWindowPos(\r
-            tbl->hwnd_notif,\r
-            tbl->hwnd_header,\r
-            rect.left,\r
-            tbl->header_height,\r
-            rect.right - rect.left,\r
-            tbl->cell_height * 4,\r
-            0);\r
-    }\r
-    return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
-}\r
-\r
-LRESULT \r
-cw_wm_notify(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
-{\r
-    khui_credwnd_tbl * tbl;\r
-    LPNMHDR pnmh;\r
-    tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-    pnmh = (LPNMHDR) lParam;\r
-    if(pnmh->hwndFrom == tbl->hwnd_header) {\r
-        LPNMHEADER ph;\r
-        ph = (LPNMHEADER) lParam;\r
-        return cw_handle_header_msg(tbl, ph);\r
-    }\r
-\r
-    return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
-}\r
-\r
-static void cw_pp_begin(khui_property_sheet * s);\r
-static void cw_pp_precreate(khui_property_sheet * s);\r
-static void cw_pp_end(khui_property_sheet * s);\r
-static void cw_pp_destroy(khui_property_sheet *ps);\r
-\r
-LRESULT \r
-cw_kmq_wm_dispatch(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
-{\r
-    kmq_message * m;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    khui_credwnd_tbl * tbl;\r
-\r
-    tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); \r
-\r
-    kmq_wm_begin(lParam, &m);\r
-\r
-    if(m->type == KMSG_CRED) {\r
-        switch (m->subtype) {\r
-        case KMSG_CRED_ROOTDELTA:\r
-            cw_update_creds(tbl);\r
-            cw_update_outline(tbl);\r
-            cw_update_extents(tbl, TRUE);\r
-            InvalidateRect(hwnd, NULL, FALSE);\r
-            break;\r
-\r
-        case KMSG_CRED_PP_BEGIN:\r
-            cw_pp_begin((khui_property_sheet *) m->vparam);\r
-            break;\r
-\r
-        case KMSG_CRED_PP_PRECREATE:\r
-            cw_pp_precreate((khui_property_sheet *) m->vparam);\r
-            break;\r
-\r
-        case KMSG_CRED_PP_END:\r
-            cw_pp_end((khui_property_sheet *) m->vparam);\r
-            break;\r
-\r
-        case KMSG_CRED_PP_DESTROY:\r
-            cw_pp_destroy((khui_property_sheet *) m->vparam);\r
-            break;\r
-        }\r
-    } else if (m->type == KMSG_KCDB) {\r
-        if (m->subtype == KMSG_KCDB_IDENT &&\r
-            m->uparam == KCDB_OP_MODIFY) {\r
-\r
-            cw_update_outline(tbl);\r
-            cw_update_extents(tbl, TRUE);\r
-            InvalidateRect(hwnd, NULL, FALSE);\r
-\r
-        }\r
-        else if (m->subtype == KMSG_KCDB_IDENT && \r
-                 m->uparam == KCDB_OP_NEW_DEFAULT) {\r
-\r
-            cw_update_outline(tbl);\r
-            cw_update_extents(tbl, TRUE);\r
-            InvalidateRect(hwnd, NULL, FALSE);\r
-\r
-        }\r
-        else if (m->subtype == KMSG_KCDB_ATTRIB &&\r
-                 (m->uparam == KCDB_OP_INSERT ||\r
-                  m->uparam == KCDB_OP_DELETE)) {\r
-\r
-            cw_refresh_attribs(hwnd);\r
-\r
-        }\r
-    } else if (m->type == KMSG_KMM &&\r
-               m->subtype == KMSG_KMM_I_DONE) {\r
-\r
-        if (tbl->flags & KHUI_CW_TBL_COLSKIP) {\r
-            wchar_t cname[KCONF_MAXCCH_NAME];\r
-            khm_size cb;\r
-\r
-            cname[0] = L'\0';\r
-\r
-            if (tbl->csp_view) {\r
-                cb = sizeof(cname);\r
-                khc_get_config_space_name(tbl->csp_view,\r
-                                          cname,\r
-                                          &cb);\r
-            }\r
-\r
-            cw_unload_view(tbl);\r
-\r
-            cw_load_view(tbl, ((cname[0])?cname: NULL), hwnd);\r
-            cw_insert_header_cols(tbl);\r
-\r
-            cw_update_creds(tbl);\r
-            cw_update_outline(tbl);\r
-            cw_update_selection_state(tbl);\r
-            cw_update_extents(tbl, TRUE);\r
-\r
-            InvalidateRect(tbl->hwnd, NULL, TRUE);\r
-        }\r
-\r
-    } else if (m->type == KMSG_ACT &&\r
-               m->subtype == KMSG_ACT_ACTIVATE) {\r
-        /* a column selector menu item was activated */\r
-\r
-        khm_int32 attr_id;\r
-        khm_int32 action;\r
-        khui_action * paction;\r
-        int i;\r
-        int first_non_fixed = -1;\r
-\r
-        action = m->uparam;\r
-        paction = khui_find_action(action);\r
-\r
-        if (paction == NULL)\r
-            goto _skip_action;\r
-\r
-        attr_id = (khm_int32)(INT_PTR) paction->data;\r
-\r
-        if (attr_id < 0 || attr_id > KCDB_ATTR_MAX_ID)\r
-            goto _skip_action;\r
-\r
-        for (i=0; i < tbl->n_cols; i++) {\r
-            if (tbl->cols[i].attr_id >= 0 &&\r
-                first_non_fixed == -1)\r
-                first_non_fixed = i;\r
-\r
-            if (tbl->cols[i].attr_id == attr_id)\r
-                break;\r
-        }\r
-\r
-        if (first_non_fixed == i &&\r
-            i == tbl->n_cols - 1) {\r
-            /* this is the only non-fixed column.  We don't allow\r
-               deleting it, althoguh there's nothing wrong with doing\r
-               so other than not being very useful. */\r
-            goto _skip_action;\r
-        }\r
-\r
-        if (i < tbl->n_cols) {\r
-            khm_int32 sort_index;\r
-\r
-            /* we need to remove a column */\r
-\r
-            Header_DeleteItem(tbl->hwnd_header, i);\r
-            sort_index = tbl->cols[i].sort_index;\r
-\r
-            if (tbl->cols[i].title)\r
-                PFREE(tbl->cols[i].title);\r
-            tbl->cols[i].title = NULL;\r
-\r
-            if (i < tbl->n_cols - 1) {\r
-                MoveMemory(&tbl->cols[i], &tbl->cols[i+1],\r
-                           sizeof(tbl->cols[0]) * (tbl->n_cols - (i + 1)));\r
-            }\r
-            tbl->n_cols--;\r
-\r
-            /* fix the sort index */\r
-            if (sort_index >= 0) {\r
-                for (i=0; i < tbl->n_cols; i++) {\r
-                    if (tbl->cols[i].sort_index > sort_index)\r
-                        tbl->cols[i].sort_index--;\r
-                }\r
-            }\r
-\r
-            tbl->flags |= KHUI_CW_TBL_COL_DIRTY;\r
-\r
-            cw_update_creds(tbl);\r
-            cw_update_outline(tbl);\r
-            cw_update_extents(tbl, TRUE);\r
-\r
-            InvalidateRect(tbl->hwnd, NULL, TRUE);\r
-\r
-            khui_check_action(attr_to_action[attr_id], FALSE);\r
-\r
-            tbl->flags |= KHUI_CW_TBL_CUSTVIEW;\r
-\r
-        } else {\r
-            /* we need to add a column */\r
-            wchar_t buf[KCDB_MAXCCH_SHORT_DESC];\r
-            khm_size cb;\r
-            khm_int32 idx = tbl->n_cols;\r
-            HDITEM hi;\r
-\r
-            /* for now, we only allow KHUI_CW_COL_INITIAL columns */\r
-            if (tbl->n_rows == tbl->n_total_rows)\r
-                goto _skip_action;\r
-\r
-            cb = sizeof(buf);\r
-            if (KHM_FAILED(kcdb_attrib_describe(attr_id,\r
-                                                buf,\r
-                                                &cb,\r
-                                                KCDB_TS_SHORT)))\r
-                goto _skip_action;\r
-\r
-            tbl->cols[idx].attr_id = attr_id;\r
-            tbl->cols[idx].width = 100;\r
-            tbl->cols[idx].x = -1;\r
-            tbl->cols[idx].flags = 0;\r
-            tbl->cols[idx].sort_index = -1;\r
-            tbl->cols[idx].title = PMALLOC(cb);\r
-#ifdef DEBUG\r
-            assert(tbl->cols[idx].title);\r
-#endif\r
-            if (!tbl->cols[idx].title)\r
-                goto _skip_action;\r
-\r
-            StringCbCopy(tbl->cols[idx].title,\r
-                         cb,\r
-                         buf);\r
-\r
-            tbl->n_cols++;\r
-\r
-            cw_hditem_from_tbl_col(&(tbl->cols[idx]), &hi);\r
-            Header_InsertItem(tbl->hwnd_header, 512, &hi);\r
-\r
-            tbl->flags |= KHUI_CW_TBL_COL_DIRTY;\r
-\r
-            cw_update_creds(tbl);\r
-            cw_update_outline(tbl);\r
-            cw_update_extents(tbl, TRUE);\r
-\r
-            InvalidateRect(tbl->hwnd, NULL, TRUE);\r
-\r
-            khui_check_action(attr_to_action[attr_id], TRUE);\r
-\r
-            tbl->flags |= KHUI_CW_TBL_CUSTVIEW;\r
-        }\r
-\r
-        kmq_post_message(KMSG_ACT, KMSG_ACT_REFRESH, 0, 0);\r
-\r
-    _skip_action:\r
-        ;\r
-    }\r
-\r
-    return kmq_wm_end(m, rv);\r
-}\r
-\r
-static void \r
-cw_select_outline_level(khui_credwnd_outline * o,\r
-                        BOOL select)\r
-{\r
-    while(o) {\r
-        if (select)\r
-            o->flags |= KHUI_CW_O_SELECTED;\r
-        else\r
-            o->flags &= ~KHUI_CW_O_SELECTED;\r
-        cw_select_outline_level(TFIRSTCHILD(o), select);\r
-        o = LNEXT(o);\r
-    }\r
-}\r
-\r
-static void\r
-cw_select_outline(khui_credwnd_outline * o,\r
-                  BOOL select)\r
-{\r
-    if (select)\r
-        o->flags |= KHUI_CW_O_SELECTED;\r
-    else\r
-        o->flags &= ~KHUI_CW_O_SELECTED;\r
-}\r
-\r
-static void\r
-cw_select_row_creds(khui_credwnd_tbl * tbl, int row, int selected) {\r
-\r
-    khm_size j;\r
-    khm_size idx_start, idx_end;\r
-\r
-#ifdef DEBUG\r
-    assert(row >= 0 && row < tbl->n_rows);\r
-#endif\r
-\r
-    if (row >= tbl->n_rows)\r
-        return;\r
-\r
-    if (tbl->rows[row].flags & KHUI_CW_ROW_HEADER) {\r
-        khui_credwnd_outline * o;\r
-\r
-        o = (khui_credwnd_outline *) tbl->rows[row].data;\r
-        if (o->col == tbl->n_cols - 1) {\r
-            /* this is a special case where the outline column is the\r
-               last displayed column.  In this case, the credentials\r
-               do not occupy any rows, and this header row acts as a\r
-               group credential row. */\r
-            idx_start = o->idx_start;\r
-            idx_end = o->idx_end;\r
-        } else {\r
-            return;\r
-        }\r
-    } else {\r
-        idx_start = tbl->rows[row].idx_start;\r
-        idx_end = tbl->rows[row].idx_end;\r
-    }\r
-\r
-    if (idx_start == -1 || idx_end == -1)\r
-        return;\r
-\r
-    for (j = idx_start; j <= idx_end; j++) {\r
-        khm_handle cred = NULL;\r
-\r
-        kcdb_credset_get_cred(tbl->credset, (khm_int32) j, &cred);\r
-\r
-        if (cred) {\r
-            kcdb_cred_set_flags(cred, ((selected)?KCDB_CRED_FLAG_SELECTED:0),\r
-                                KCDB_CRED_FLAG_SELECTED);\r
-            kcdb_cred_release(cred);\r
-        }\r
-    }\r
-}\r
-\r
-static void \r
-cw_unselect_all(khui_credwnd_tbl * tbl)\r
-{\r
-    int i;\r
-\r
-    for(i=0; i<tbl->n_rows; i++) {\r
-        tbl->rows[i].flags &= ~KHUI_CW_ROW_SELECTED;\r
-\r
-        cw_select_row_creds(tbl, i, FALSE);\r
-    }\r
-\r
-    cw_select_outline_level(tbl->outline, FALSE);\r
-}\r
-\r
-static void\r
-cw_update_outline_selection_state(khui_credwnd_tbl * tbl,\r
-                                  khui_credwnd_outline * o)\r
-{\r
-    BOOL select = TRUE;\r
-    int j;\r
-\r
-    for (j = o->start + 1; j < o->start + o->length; j++) {\r
-        if (tbl->rows[j].flags & KHUI_CW_ROW_HEADER) {\r
-            cw_update_outline_selection_state(tbl,\r
-                                              (khui_credwnd_outline *)\r
-                                              tbl->rows[j].data);\r
-        }\r
-\r
-        if (!(tbl->rows[j].flags & KHUI_CW_ROW_SELECTED)) {\r
-            select = FALSE;\r
-        }\r
-\r
-        if (tbl->rows[j].flags & KHUI_CW_ROW_HEADER) {\r
-            j += ((khui_credwnd_outline *) tbl->rows[j].data)->length - 1;\r
-        }\r
-    }\r
-\r
-    /* special case : the header has been collapsed and we are just\r
-       using one row.  In this case, the for loop above will do\r
-       nothing. */\r
-\r
-    if (o->length == 1) {\r
-        select = (tbl->rows[o->start].flags & KHUI_CW_ROW_SELECTED);\r
-    }\r
-\r
-    cw_select_outline(o, select);\r
-\r
-    if (select) {\r
-        tbl->rows[o->start].flags |= KHUI_CW_ROW_SELECTED;\r
-    } else {\r
-        tbl->rows[o->start].flags &= ~KHUI_CW_ROW_SELECTED;\r
-    }\r
-}\r
-\r
-static void \r
-cw_update_selection_state(khui_credwnd_tbl * tbl)\r
-{\r
-    int i;\r
-\r
-    cw_select_outline_level(tbl->outline, FALSE);\r
-\r
-    for (i=0; i < tbl->n_rows; i++) {\r
-        if (tbl->rows[i].flags & KHUI_CW_ROW_HEADER) {\r
-            khui_credwnd_outline * o;\r
-\r
-            o = (khui_credwnd_outline *) tbl->rows[i].data;\r
-\r
-            cw_update_outline_selection_state(tbl, o);\r
-\r
-            i += o->length - 1;\r
-        }\r
-    }\r
-}\r
-\r
-/* Examine the current row and set the UI context */\r
-static void \r
-cw_set_row_context(khui_credwnd_tbl * tbl, int row)\r
-{\r
-    khui_credwnd_outline * o;\r
-    BOOL set_context = TRUE;\r
-\r
-    if (row < 0 || row >= (int) tbl->n_rows) {\r
-        if (tbl->n_rows > 0)\r
-            row = 0;\r
-        else {\r
-            khui_context_reset();\r
-            return;\r
-        }\r
-    }\r
-\r
-    if (tbl->rows[row].flags & KHUI_CW_ROW_HEADER) {\r
-\r
-        o = (khui_credwnd_outline *) tbl->rows[row].data;\r
-\r
-        if (tbl->cols[o->col].attr_id == KCDB_ATTR_ID_NAME) {\r
-            if (TPARENT(o) != NULL) {\r
-                khui_credwnd_outline * op;\r
-\r
-                op = TPARENT(o);\r
-\r
-                if (tbl->cols[op->col].attr_id == KCDB_ATTR_TYPE_NAME &&\r
-                    TPARENT(op) == NULL) {\r
-                    /* selected a credential type */\r
-                    khui_context_set(KHUI_SCOPE_CREDTYPE,\r
-                                     (khm_handle) o->data,\r
-                                     (khm_int32) (DWORD_PTR) op->data,\r
-                                     NULL,\r
-                                     NULL,\r
-                                     0,\r
-                                     tbl->credset);\r
-                } else {\r
-                    /* we can't narrow it down using the standard set\r
-                       of scopes.  We consider this to be an identity\r
-                       selection because the user right-clicked on an\r
-                       identity header. */\r
-                    khui_context_set(KHUI_SCOPE_IDENT,\r
-                                     (khm_handle) o->data,\r
-                                     KCDB_CREDTYPE_INVALID,\r
-                                     NULL,\r
-                                     NULL,\r
-                                     0,\r
-                                     tbl->credset);\r
-                }\r
-            } else {\r
-                /* The user clicked on an identity header.  Even\r
-                   though not all credentials belonging to the\r
-                   identity maybe within the scope right now, we still\r
-                   consider this to be an identity scope. */\r
-                khui_context_set(KHUI_SCOPE_IDENT,\r
-                                 (khm_handle) o->data,\r
-                                 KCDB_CREDTYPE_INVALID,\r
-                                 NULL,\r
-                                 NULL,\r
-                                 0,\r
-                                 tbl->credset);\r
-            }\r
-        } else if (tbl->cols[o->col].attr_id == KCDB_ATTR_TYPE_NAME) {\r
-            if (TPARENT(o) == NULL) {\r
-                /* selected an entire cred type */\r
-                khui_context_set(KHUI_SCOPE_CREDTYPE,\r
-                                 NULL,\r
-                                 (khm_int32) (DWORD_PTR) o->data,\r
-                                 NULL,\r
-                                 NULL,\r
-                                 0,\r
-                                 tbl->credset);\r
-            } else {\r
-                khui_credwnd_outline * op;\r
-\r
-                op = TPARENT(o);\r
-                if (tbl->cols[op->col].attr_id == KCDB_ATTR_ID_NAME) {\r
-                    /* credtype under an identity.  Even though not\r
-                       all the credentials of this credtype belonging\r
-                       to this identity might be within the scope, we\r
-                       still consider this to be a type selection\r
-                       under a specific identity. */\r
-                    khui_context_set(KHUI_SCOPE_CREDTYPE,\r
-                                     (khm_handle) op->data,\r
-                                     (khm_int32) (DWORD_PTR) o->data,\r
-                                     NULL,\r
-                                     NULL,\r
-                                     0,\r
-                                     tbl->credset);\r
-                } else {\r
-                    set_context = FALSE;\r
-                }\r
-            }\r
-        } else {\r
-            set_context = FALSE;\r
-        }\r
-\r
-        if (!set_context) {\r
-            /* woohoo. cred group. yay. */\r
-            khui_header headers[KHUI_MAX_HEADERS];\r
-            khm_size n_headers = 0;\r
-\r
-            do {\r
-                headers[n_headers].attr_id =\r
-                    o->attr_id;\r
-                if (tbl->cols[o->col].attr_id == \r
-                    KCDB_ATTR_ID_NAME) {\r
-                    headers[n_headers].data = &(o->data);\r
-                    headers[n_headers].cb_data = sizeof(khm_handle);\r
-                } else if (tbl->cols[o->col].attr_id == \r
-                           KCDB_ATTR_TYPE_NAME) {\r
-                    headers[n_headers].data = &(o->data);\r
-                    headers[n_headers].cb_data = sizeof(khm_int32);\r
-                } else {\r
-                    headers[n_headers].data = o->data;\r
-                    headers[n_headers].cb_data = o->cb_data;\r
-                }\r
-\r
-                n_headers++;\r
-\r
-                o = TPARENT(o);\r
-            } while(o);\r
-\r
-            khui_context_set(KHUI_SCOPE_GROUP,\r
-                             NULL,\r
-                             KCDB_CREDTYPE_INVALID,\r
-                             NULL,\r
-                             headers,\r
-                             n_headers,\r
-                             tbl->credset);\r
-        }\r
-\r
-    } else {\r
-        khm_handle cred;\r
-\r
-        cred = (khm_handle) tbl->rows[row].data;\r
-\r
-        khui_context_set(KHUI_SCOPE_CRED,\r
-                         NULL,\r
-                         KCDB_CREDTYPE_INVALID,\r
-                         cred,\r
-                         NULL,\r
-                         0,\r
-                         tbl->credset);\r
-    }\r
-}\r
-\r
-static void\r
-cw_select_all(khui_credwnd_tbl * tbl)\r
-{\r
-    int i;\r
-\r
-    for(i=0; i<tbl->n_rows; i++) {\r
-        tbl->rows[i].flags |= KHUI_CW_ROW_SELECTED;\r
-        cw_select_row_creds(tbl, i, TRUE);\r
-    }\r
-\r
-    cw_select_outline_level(tbl->outline, TRUE);\r
-\r
-    cw_update_selection_state(tbl);\r
-\r
-    cw_set_row_context(tbl, tbl->cursor_row);\r
-\r
-    InvalidateRect(tbl->hwnd, NULL, FALSE);\r
-}\r
-\r
-static void \r
-cw_select_row(khui_credwnd_tbl * tbl, int row, WPARAM wParam)\r
-{\r
-    int i;\r
-    BOOL toggle;\r
-    BOOL extend;\r
-    int group_begin;\r
-    int group_end;\r
-\r
-    if (wParam & MK_CONTROL) {\r
-        toggle = TRUE;\r
-        extend = FALSE;\r
-    } else if (wParam & MK_SHIFT) {\r
-        toggle = FALSE;\r
-        extend = TRUE;\r
-    } else {\r
-        toggle = FALSE;\r
-        extend = FALSE;\r
-    }\r
-\r
-    if (row < 0 || row >= (int) tbl->n_rows)\r
-        return;\r
-\r
-    if (tbl->rows[row].flags & KHUI_CW_ROW_HEADER) {\r
-        khui_credwnd_outline * o;\r
-\r
-        o = (khui_credwnd_outline *) tbl->rows[row].data;\r
-\r
-        group_begin = o->start;\r
-        group_end = o->start + o->length - 1;\r
-    } else {\r
-        group_begin = row;\r
-        group_end = row;\r
-    }\r
-\r
-    if (!toggle && !extend) {\r
-        /* selecting a single row */\r
-        cw_unselect_all(tbl);\r
-\r
-        tbl->cursor_row = row;\r
-        tbl->anchor_row = row;\r
-\r
-        for (i = group_begin; i <= group_end; i++) {\r
-            tbl->rows[i].flags |= KHUI_CW_ROW_SELECTED;\r
-            cw_select_row_creds(tbl, i, TRUE);\r
-        }\r
-    } else if (toggle) {\r
-        BOOL select;\r
-\r
-        tbl->cursor_row = row;\r
-        tbl->anchor_row = row;\r
-\r
-        select = !(tbl->rows[row].flags & KHUI_CW_ROW_SELECTED);\r
-\r
-        for (i = group_begin; i <= group_end; i++) {\r
-            if (select)\r
-                tbl->rows[i].flags |= KHUI_CW_ROW_SELECTED;\r
-            else\r
-                tbl->rows[i].flags &= ~KHUI_CW_ROW_SELECTED;\r
-\r
-            cw_select_row_creds(tbl, i, select);\r
-        }\r
-    } else if (extend) {\r
-        int range_begin;\r
-        int range_end;\r
-\r
-        cw_unselect_all(tbl);\r
-\r
-        range_begin = min(row, tbl->anchor_row);\r
-        range_end = max(row, tbl->anchor_row);\r
-\r
-        for (i = range_begin; i <= range_end; i++) {\r
-            tbl->rows[i].flags |= KHUI_CW_ROW_SELECTED;\r
-\r
-            cw_select_row_creds(tbl, i, TRUE);\r
-        }\r
-\r
-        tbl->cursor_row = row;\r
-    }\r
-\r
-    cw_update_selection_state(tbl);\r
-\r
-    cw_set_row_context(tbl, tbl->cursor_row);\r
-\r
-    InvalidateRect(tbl->hwnd, NULL, FALSE);\r
-}\r
-\r
-static void\r
-cw_toggle_outline_state(khui_credwnd_tbl * tbl,\r
-                        khui_credwnd_outline * o) {\r
-\r
-    int old_range_begin;\r
-    int old_range_end;\r
-    int new_range_begin;\r
-    int new_range_end;\r
-\r
-    old_range_begin = o->start;\r
-    old_range_end = o->start + o->length - 1;\r
-\r
-    o->flags ^= KHUI_CW_O_EXPAND;\r
-\r
-    cw_update_outline(tbl);\r
-    cw_update_extents(tbl, TRUE);\r
-\r
-    new_range_begin = o->start;\r
-    new_range_end = o->start + o->length - 1;\r
-\r
-    if (tbl->cursor_row > old_range_end) {\r
-        tbl->cursor_row -= old_range_end - new_range_end;\r
-    } else if (tbl->cursor_row >= old_range_begin &&\r
-               tbl->cursor_row <= old_range_end) {\r
-        tbl->cursor_row = new_range_begin;\r
-    }\r
-\r
-    if (tbl->anchor_row > old_range_end) {\r
-        tbl->anchor_row -= old_range_end - new_range_end;\r
-    } else if (tbl->anchor_row >= old_range_begin &&\r
-               tbl->anchor_row <= old_range_end) {\r
-        tbl->anchor_row = new_range_begin;\r
-    }\r
-\r
-    InvalidateRect(tbl->hwnd, NULL, TRUE);\r
-\r
-}\r
-\r
-LRESULT cw_properties(HWND hwnd);\r
-\r
-LRESULT \r
-cw_wm_mouse(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
-{\r
-    khui_credwnd_tbl * tbl;\r
-    int x,y;\r
-    RECT r;\r
-    int row;\r
-    int col;\r
-    int i;\r
-    int nm_state,nm_row,nm_col;\r
-\r
-    tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-\r
-    /* we are basically trying to capture events where the mouse is\r
-       hovering over one of the 'hotspots'.  There are two kinds of\r
-       hotspots one is the little widget thinggy that you click on to\r
-       expand or collapse an outline.  The other is a text cell that\r
-       is partially concealed. */\r
-\r
-    x = GET_X_LPARAM(lParam);\r
-    y = GET_Y_LPARAM(lParam);\r
-    x += tbl->scr_left;\r
-    y += tbl->scr_top - tbl->header_height;\r
-\r
-    row = -1;\r
-\r
-    for (i=0; i < tbl->n_rows; i++) {\r
-        if (y >= tbl->rows[i].r_ext.top &&\r
-            y < tbl->rows[i].r_ext.bottom) {\r
-            row = i;\r
-            break;\r
-        }\r
-    }\r
-\r
-    col = -1;\r
-    nm_state = CW_MOUSE_NONE;\r
-    nm_row = nm_col = -1;\r
-\r
-    for(i=0; i < (int) tbl->n_cols; i++) {\r
-        if(x >= tbl->cols[i].x &&\r
-           x < tbl->cols[i].x + tbl->cols[i].width) {\r
-            col = i;\r
-            break;\r
-        }\r
-    }\r
-\r
-    if(wParam & MK_LBUTTON)\r
-        nm_state = CW_MOUSE_LDOWN;\r
-\r
-    if(row >= 0 && row < (int) tbl->n_rows) {\r
-        nm_state |= CW_MOUSE_ROW;\r
-        nm_row = row;\r
-        nm_col = col;\r
-        if(tbl->rows[row].flags & KHUI_CW_ROW_HEADER) {\r
-            khui_credwnd_outline * o;\r
-\r
-            o = (khui_credwnd_outline *) tbl->rows[row].data;\r
-\r
-            /* are we on a widget then? */\r
-            x -= tbl->cols[o->col].x;\r
-\r
-            if (!(o->flags & KHUI_CW_O_NOOUTLINE)) {\r
-                if(x >= 0 && x < KHUI_SMICON_CX) /* hit */ {\r
-                    nm_state |= CW_MOUSE_WOUTLINE | CW_MOUSE_WIDGET;\r
-                } else if (tbl->cols[tbl->rows[row].col].attr_id == \r
-                           KCDB_ATTR_ID_NAME &&\r
-                           col == tbl->rows[row].col &&\r
-                           x >= KHUI_SMICON_CX * 3 / 2 &&\r
-                           x < KHUI_SMICON_CX * 5 / 2){\r
-                    nm_state |= CW_MOUSE_WSTICKY | CW_MOUSE_WIDGET;\r
-                } else if (tbl->cols[tbl->rows[row].col].attr_id ==\r
-                           KCDB_ATTR_ID_NAME &&\r
-                           col == tbl->rows[row].col &&\r
-                           x >= KHUI_SMICON_CX * 3 &&\r
-                           x < KHUI_SMICON_CX * 4) {\r
-                    nm_state |= CW_MOUSE_WICON | CW_MOUSE_WIDGET;\r
-                }\r
-            } else if (tbl->cols[o->col].attr_id == KCDB_ATTR_ID_NAME) {\r
-                if (col == tbl->rows[row].col &&\r
-                    x >= 0 &&\r
-                    x < KHUI_SMICON_CX){\r
-\r
-                    nm_state |= CW_MOUSE_WSTICKY | CW_MOUSE_WIDGET;\r
-\r
-                } else if (col == tbl->rows[row].col &&\r
-                           x >= KHUI_SMICON_CX * 3 / 2 &&\r
-                           x < KHUI_SMICON_CX * 5 / 2) {\r
-                    nm_state |= CW_MOUSE_WICON | CW_MOUSE_WIDGET;\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-    /* did the user drag the cursor off the current row? */\r
-    if((tbl->mouse_state & CW_MOUSE_LDOWN) &&\r
-       (nm_row != tbl->mouse_row)) {\r
-        nm_state &= ~CW_MOUSE_WMASK;\r
-    }\r
-\r
-    if(!(nm_state & CW_MOUSE_LDOWN) && \r
-       (tbl->mouse_state & CW_MOUSE_LDOWN) &&\r
-       tbl->mouse_row == nm_row) {\r
-\r
-        if((nm_state & CW_MOUSE_WOUTLINE) &&\r
-           (tbl->mouse_state & CW_MOUSE_WOUTLINE)) {\r
-            /* click on an outline widget */\r
-            khui_credwnd_outline * o;\r
-\r
-            o = (khui_credwnd_outline *) tbl->rows[nm_row].data;\r
-            tbl->mouse_state = CW_MOUSE_WIDGET | CW_MOUSE_WOUTLINE;\r
-\r
-            cw_toggle_outline_state(tbl, o);\r
-\r
-            return 0;\r
-        } else if ((nm_state & CW_MOUSE_WSTICKY) &&\r
-                   (tbl->mouse_state & CW_MOUSE_WSTICKY)) {\r
-\r
-            khui_credwnd_outline * o;\r
-            khm_handle ident;\r
-            khm_int32 idf = 0;\r
-\r
-            o = tbl->rows[nm_row].data;\r
-            ident = o->data;\r
-\r
-            kcdb_identity_get_flags(ident, &idf);\r
-            idf &= KCDB_IDENT_FLAG_STICKY;\r
-            kcdb_identity_set_flags(ident, (idf ^ KCDB_IDENT_FLAG_STICKY),\r
-                                    KCDB_IDENT_FLAG_STICKY);\r
-\r
-            tbl->mouse_state = CW_MOUSE_WIDGET | CW_MOUSE_WSTICKY;\r
-\r
-            return 0;\r
-        } else if ((nm_state & CW_MOUSE_WICON) &&\r
-                   (tbl->mouse_state & CW_MOUSE_WICON)) {\r
-            /* click on an row icon */\r
-            cw_select_row(tbl, nm_row, wParam);\r
-            cw_properties(hwnd);\r
-        } else {\r
-            /* click on a row */\r
-            cw_select_row(tbl, nm_row, wParam);\r
-\r
-            if (tbl->mouse_col == nm_col &&\r
-                nm_col >= 0 &&\r
-                tbl->cols[nm_col].attr_id == CW_CA_FLAGS &&\r
-                !(tbl->rows[nm_row].flags & KHUI_CW_ROW_HEADER)) {\r
-                /* clicked on a cred icon */\r
-\r
-                cw_properties(hwnd);\r
-            }\r
-        }\r
-    }\r
-\r
-    /* ok, now if we are changing state, we need to invalidate a few\r
-       regions */\r
-    if (((tbl->mouse_state ^ nm_state) & (CW_MOUSE_WIDGET |\r
-                                          CW_MOUSE_WOUTLINE |\r
-                                          CW_MOUSE_WSTICKY)) ||\r
-        tbl->mouse_row != nm_row) {\r
-\r
-        if(tbl->mouse_state & CW_MOUSE_WOUTLINE) {\r
-            r.left = tbl->cols[tbl->mouse_col].x - tbl->scr_left;\r
-            r.top = tbl->mouse_row * tbl->cell_height + \r
-                tbl->header_height - tbl->scr_top;\r
-            r.right = r.left + KHUI_SMICON_CX;\r
-            r.bottom = r.top + tbl->cell_height;\r
-            InvalidateRect(tbl->hwnd, &r, TRUE);\r
-        }\r
-        if(tbl->mouse_state & CW_MOUSE_WSTICKY) {\r
-            if (tbl->flags & KHUI_CW_TBL_EXPIDENT) {\r
-\r
-                if (tbl->mouse_row >= 0 && tbl->mouse_row < tbl->n_rows) {\r
-                    r = tbl->rows[tbl->mouse_row].r_ext;\r
-                    OffsetRect(&r, -tbl->scr_left, tbl->header_height - tbl->scr_top);\r
-                    r.right = r.left + KHUI_SMICON_CX;\r
-                    InvalidateRect(tbl->hwnd, &r, TRUE);\r
-                }\r
-\r
-            } else {\r
-                r.left = KHUI_SMICON_CX * 3 / 2 + \r
-                    tbl->cols[tbl->mouse_col].x - tbl->scr_left;\r
-                r.top = tbl->mouse_row * tbl->cell_height + \r
-                    tbl->header_height - tbl->scr_top;\r
-                r.right = r.left + KHUI_SMICON_CX;\r
-                r.bottom = r.top + tbl->cell_height;\r
-            }\r
-            InvalidateRect(tbl->hwnd, &r, TRUE);\r
-        }\r
-\r
-        if ((tbl->mouse_state & nm_state) & CW_MOUSE_LDOWN) {\r
-            if (tbl->mouse_row == nm_row)\r
-                tbl->mouse_col = nm_col;\r
-        } else {\r
-            tbl->mouse_col = nm_col;\r
-            tbl->mouse_row = nm_row;\r
-        }\r
-        tbl->mouse_state = nm_state;\r
-\r
-        /* same code block as above */\r
-        if(tbl->mouse_state & CW_MOUSE_WOUTLINE) {\r
-            r.left = tbl->cols[tbl->mouse_col].x - tbl->scr_left;\r
-            r.top = tbl->mouse_row * tbl->cell_height + \r
-                tbl->header_height - tbl->scr_top;\r
-            r.right = r.left + KHUI_SMICON_CX;\r
-            r.bottom = r.top + tbl->cell_height;\r
-            InvalidateRect(tbl->hwnd, &r, TRUE);\r
-        }\r
-        if(tbl->mouse_state & CW_MOUSE_WSTICKY) {\r
-            if (tbl->flags & KHUI_CW_TBL_EXPIDENT) {\r
-\r
-                if (tbl->mouse_row >= 0 && tbl->mouse_row < tbl->n_rows) {\r
-                    r = tbl->rows[tbl->mouse_row].r_ext;\r
-                    OffsetRect(&r, -tbl->scr_left, tbl->header_height - tbl->scr_top);\r
-                    r.right = r.left + KHUI_SMICON_CX;\r
-                    InvalidateRect(tbl->hwnd, &r, TRUE);\r
-                }\r
-\r
-            } else {\r
-                r.left = KHUI_SMICON_CX * 3 / 2 + \r
-                    tbl->cols[tbl->mouse_col].x - tbl->scr_left;\r
-                r.top = tbl->mouse_row * tbl->cell_height + \r
-                    tbl->header_height - tbl->scr_top;\r
-                r.right = r.left + KHUI_SMICON_CX;\r
-                r.bottom = r.top + tbl->cell_height;\r
-            }\r
-            InvalidateRect(tbl->hwnd, &r, TRUE);\r
-        }\r
-    } else if(tbl->mouse_state != nm_state) {\r
-\r
-        if ((tbl->mouse_state & nm_state) & CW_MOUSE_LDOWN) {\r
-            if (tbl->mouse_row == nm_row) {\r
-                tbl->mouse_col = nm_col;\r
-                tbl->mouse_state = nm_state;\r
-            }\r
-        } else {\r
-            tbl->mouse_col = nm_col;\r
-            tbl->mouse_row = nm_row;\r
-            tbl->mouse_state = nm_state;\r
-        }\r
-    }\r
-\r
-    /* if it was a double click, also show the property\r
-       window */\r
-    if (uMsg == WM_LBUTTONDBLCLK) {\r
-        cw_properties(hwnd);\r
-    }\r
-\r
-    return 0;\r
-}\r
-\r
-LRESULT \r
-cw_wm_hscroll(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
-{\r
-    khui_credwnd_tbl * tbl;\r
-    SCROLLINFO si;\r
-    RECT cr;\r
-    RECT lr;\r
-    RECT sr;\r
-    int dx;\r
-    int newpos;\r
-\r
-    tbl = (khui_credwnd_tbl *) (LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-    GetClientRect(hwnd, &cr);\r
-    dx = tbl->scr_left;\r
-\r
-    switch(LOWORD(wParam)) {\r
-        case SB_LEFT:\r
-            newpos = 0;\r
-            break;\r
-\r
-        case SB_RIGHT:\r
-            newpos = tbl->ext_width;\r
-            break;\r
-\r
-        case SB_LINELEFT:\r
-            newpos = tbl->scr_left - (tbl->ext_width / 12);\r
-            break;\r
-\r
-        case SB_LINERIGHT:\r
-            newpos = tbl->scr_left + (tbl->ext_width / 12);\r
-            break;\r
-\r
-        case SB_PAGELEFT:\r
-            newpos = tbl->scr_left - (cr.right - cr.left);\r
-            break;\r
-\r
-        case SB_PAGERIGHT:\r
-            newpos = tbl->scr_left + (cr.right - cr.left);\r
-            break;\r
-\r
-        case SB_THUMBTRACK:\r
-        case SB_THUMBPOSITION:\r
-            ZeroMemory(&si, sizeof(si));\r
-            si.cbSize = sizeof(si);\r
-            si.fMask = SIF_TRACKPOS;\r
-            GetScrollInfo(hwnd, SB_HORZ, &si);\r
-\r
-            newpos = si.nTrackPos;\r
-            break;\r
-\r
-        default:\r
-            return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
-    }\r
-\r
-    //cr.top += tbl->header_height;\r
-    tbl->scr_left = newpos;\r
-    cw_update_extents(tbl, TRUE);\r
-\r
-    dx -= tbl->scr_left;\r
-\r
-    /* exclude the watermark */\r
-    lr.bottom = cr.bottom;\r
-    lr.right = cr.right;\r
-    lr.top = max(cr.bottom - tbl->kbm_logo_shade.cy, cr.top);\r
-    lr.left = max(cr.right - tbl->kbm_logo_shade.cx, cr.left);\r
-\r
-    if(cr.top < lr.top && cr.left < cr.right) {\r
-        sr.left = cr.left;\r
-        sr.right = cr.right;\r
-        sr.top = cr.top;\r
-        sr.bottom = lr.top;\r
-        ScrollWindowEx(\r
-            hwnd, \r
-            dx, \r
-            0, \r
-            &sr, \r
-            &sr, \r
-            NULL, \r
-            NULL, \r
-            SW_INVALIDATE | SW_SCROLLCHILDREN);\r
-    }\r
-\r
-    if(cr.left < lr.left && lr.top < lr.bottom) {\r
-        sr.left = cr.left;\r
-        sr.right = lr.left;\r
-        sr.top = lr.top;\r
-        sr.bottom = lr.bottom;\r
-        ScrollWindowEx(\r
-            hwnd, \r
-            dx, \r
-            0, \r
-            &sr, \r
-            &sr, \r
-            NULL, \r
-            NULL, \r
-            SW_INVALIDATE | SW_SCROLLCHILDREN);\r
-    }\r
-\r
-    if(lr.top < lr.bottom && lr.left < lr.right) {\r
-        InvalidateRect(hwnd, &lr, FALSE);\r
-    }\r
-\r
-    return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
-}\r
-\r
-static void\r
-cw_vscroll_to_pos(HWND hwnd, khui_credwnd_tbl * tbl, int newpos) {\r
-    RECT cr;\r
-    RECT sr;\r
-    RECT lr;\r
-    int dy;\r
-\r
-    GetClientRect(hwnd, &cr);\r
-    cr.top += tbl->header_height;\r
-    dy = tbl->scr_top;\r
-\r
-    tbl->scr_top = newpos;\r
-    cw_update_extents(tbl, TRUE);\r
-\r
-    dy -= tbl->scr_top;\r
-\r
-    /* exclude watermark */\r
-    lr.bottom = cr.bottom;\r
-    lr.right = cr.right;\r
-    lr.top = max(cr.bottom - tbl->kbm_logo_shade.cy, cr.top);\r
-    lr.left = max(cr.right - tbl->kbm_logo_shade.cx, cr.left);\r
-\r
-    if(cr.left < lr.left && cr.top < cr.bottom) {\r
-        sr.left = cr.left;\r
-        sr.right = lr.left;\r
-        sr.top = cr.top;\r
-        sr.bottom = cr.bottom;\r
-        ScrollWindowEx(\r
-            hwnd, \r
-            0, \r
-            dy, \r
-            &sr, \r
-            &sr, \r
-            NULL, \r
-            NULL, \r
-            SW_INVALIDATE);\r
-    }\r
-\r
-    if(lr.left < lr.right && cr.top < lr.top) {\r
-        sr.left = lr.left;\r
-        sr.right = lr.right;\r
-        sr.top = cr.top;\r
-        sr.bottom = lr.top;\r
-        ScrollWindowEx(\r
-            hwnd, \r
-            0, \r
-            dy, \r
-            &sr, \r
-            &sr, \r
-            NULL, \r
-            NULL, \r
-            SW_INVALIDATE);\r
-    }\r
-\r
-    if(lr.top < lr.bottom && lr.left < lr.right) {\r
-        InvalidateRect(hwnd, &lr, FALSE);\r
-    }\r
-}\r
-\r
-LRESULT \r
-cw_wm_vscroll(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
-{\r
-    khui_credwnd_tbl * tbl;\r
-    SCROLLINFO si;\r
-    int newpos;\r
-    RECT cr;\r
-\r
-    tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-\r
-    GetClientRect(hwnd, &cr);\r
-    cr.top += tbl->header_height;\r
-\r
-    switch(LOWORD(wParam)) {\r
-        case SB_LEFT:\r
-            newpos = 0;\r
-            break;\r
-\r
-        case SB_BOTTOM:\r
-            newpos = tbl->ext_height;\r
-            break;\r
-\r
-        case SB_LINEUP:\r
-            newpos = tbl->scr_top - (tbl->ext_height / 12);\r
-            break;\r
-\r
-        case SB_LINEDOWN:\r
-            newpos = tbl->scr_top + (tbl->ext_height / 12);\r
-            break;\r
-\r
-        case SB_PAGEUP:\r
-            newpos = tbl->scr_top - (cr.bottom - cr.top);\r
-            break;\r
-\r
-        case SB_PAGEDOWN:\r
-            newpos = tbl->scr_top + (cr.bottom - cr.top);\r
-            break;\r
-\r
-        case SB_THUMBTRACK:\r
-        case SB_THUMBPOSITION:\r
-            ZeroMemory(&si, sizeof(si));\r
-            si.cbSize = sizeof(si);\r
-            si.fMask = SIF_TRACKPOS;\r
-            GetScrollInfo(hwnd, SB_VERT, &si);\r
-\r
-            newpos = si.nTrackPos;\r
-            break;\r
-\r
-        default:\r
-            return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
-    }\r
-\r
-    cw_vscroll_to_pos(hwnd, tbl, newpos);\r
-\r
-    return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
-}\r
-\r
-static void\r
-cw_ensure_row_visible(HWND hwnd, khui_credwnd_tbl * tbl, int row) {\r
-    RECT r;\r
-    int newpos;\r
-\r
-    if (row < 0)\r
-        row = 0;\r
-    else if (row >= (int) tbl->n_rows)\r
-        row = (int) tbl->n_rows - 1;\r
-\r
-    GetClientRect(hwnd, &r);\r
-    r.top += tbl->header_height;\r
-\r
-    if (row * tbl->cell_height < tbl->scr_top) {\r
-        newpos = row * tbl->cell_height;\r
-    } else if ((row + 1) * tbl->cell_height\r
-             > tbl->scr_top + (r.bottom - r.top)) {\r
-        newpos = ((row + 1) * tbl->cell_height) - (r.bottom - r.top);\r
-    } else\r
-        return;\r
-\r
-    cw_vscroll_to_pos(hwnd, tbl, newpos);\r
-}\r
-\r
-static INT_PTR CALLBACK \r
-cw_pp_ident_proc(HWND hwnd,\r
-                 UINT uMsg,\r
-                 WPARAM wParam,\r
-                 LPARAM lParam)\r
-{\r
-    khui_property_sheet * s;\r
-\r
-    switch(uMsg) {\r
-    case WM_INITDIALOG:\r
-        {\r
-            PROPSHEETPAGE * p;\r
-            khm_handle ident;\r
-            wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-            khm_size t;\r
-            khm_int32 i;\r
-\r
-            p = (PROPSHEETPAGE *) lParam;\r
-            s = (khui_property_sheet *) p->lParam;\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-            SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) s);\r
-#pragma warning(pop)\r
-\r
-            ident = s->identity;\r
-\r
-            t = sizeof(idname);\r
-            kcdb_identity_get_name(ident, idname, &t);\r
-            SetDlgItemText(hwnd, IDC_PP_IDNAME, idname);\r
-\r
-            kcdb_identity_get_flags(ident, &i);\r
-\r
-            CheckDlgButton(hwnd, IDC_PP_IDDEF,\r
-                           ((i & KCDB_IDENT_FLAG_DEFAULT)?BST_CHECKED:\r
-                            BST_UNCHECKED));\r
-\r
-            /* if it's default, you can't change it further */\r
-            if (i & KCDB_IDENT_FLAG_DEFAULT) {\r
-                EnableWindow(GetDlgItem(hwnd, IDC_PP_IDDEF), FALSE);\r
-            }\r
-\r
-            CheckDlgButton(hwnd, IDC_PP_IDSEARCH,\r
-                           ((i & KCDB_IDENT_FLAG_SEARCHABLE)?BST_CHECKED:\r
-                            BST_UNCHECKED));\r
-\r
-            CheckDlgButton(hwnd, IDC_PP_STICKY,\r
-                           ((i & KCDB_IDENT_FLAG_STICKY)?BST_CHECKED:\r
-                            BST_UNCHECKED));\r
-\r
-            khui_property_wnd_set_record(GetDlgItem(hwnd, IDC_PP_PROPLIST),\r
-                                         ident);\r
-        }\r
-        return TRUE;\r
-\r
-    case WM_COMMAND:\r
-        s = (khui_property_sheet *) (LONG_PTR) \r
-            GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-        switch(wParam) {\r
-        case MAKEWPARAM(IDC_PP_IDDEF, BN_CLICKED):\r
-            /* fallthrough */\r
-        case MAKEWPARAM(IDC_PP_STICKY, BN_CLICKED):\r
-\r
-            if (s->status != KHUI_PS_STATUS_NONE)\r
-                PropSheet_Changed(s->hwnd, hwnd);\r
-            return TRUE;\r
-\r
-        case MAKEWPARAM(IDC_PP_CONFIG, BN_CLICKED):\r
-            {\r
-                khui_config_node cfg_id = NULL;\r
-                khui_config_node cfg_ids = NULL;\r
-                wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-                khm_size cb;\r
-                khm_int32 rv;\r
-\r
-                khm_refresh_config();\r
-\r
-                rv = khui_cfg_open(NULL,\r
-                                   L"KhmIdentities",\r
-                                   &cfg_ids);\r
-\r
-                if (KHM_FAILED(rv))\r
-                    return TRUE;\r
-\r
-                cb = sizeof(idname);\r
-                if (KHM_SUCCEEDED(kcdb_identity_get_name(s->identity,\r
-                                                         idname,\r
-                                                         &cb))) {\r
-                    rv = khui_cfg_open(cfg_ids,\r
-                                       idname,\r
-                                       &cfg_id);\r
-                }\r
-\r
-                if (cfg_id)\r
-                    khm_show_config_pane(cfg_id);\r
-                else\r
-                    khm_show_config_pane(cfg_ids);\r
-\r
-                if (cfg_ids)\r
-                    khui_cfg_release(cfg_ids);\r
-                if (cfg_id)\r
-                    khui_cfg_release(cfg_id);\r
-            }\r
-            return TRUE;\r
-        }\r
-        return FALSE;\r
-\r
-    case WM_NOTIFY:\r
-        {\r
-            LPPSHNOTIFY lpp;\r
-            khm_int32 flags;\r
-\r
-            lpp = (LPPSHNOTIFY) lParam;\r
-            s = (khui_property_sheet *) (LONG_PTR) \r
-                GetWindowLongPtr(hwnd, DWLP_USER);\r
-\r
-            switch(lpp->hdr.code) {\r
-            case PSN_APPLY:\r
-                flags = 0;\r
-                if (IsDlgButtonChecked(hwnd, IDC_PP_STICKY) == BST_CHECKED)\r
-                    flags |= KCDB_IDENT_FLAG_STICKY;\r
-                if (IsDlgButtonChecked(hwnd, IDC_PP_IDDEF) == BST_CHECKED)\r
-                    flags |= KCDB_IDENT_FLAG_DEFAULT;\r
-\r
-                kcdb_identity_set_flags(s->identity, flags,\r
-                                        KCDB_IDENT_FLAG_STICKY |\r
-                                        KCDB_IDENT_FLAG_DEFAULT);\r
-                return TRUE;\r
-\r
-            case PSN_RESET:\r
-                kcdb_identity_get_flags(s->identity, &flags);\r
-\r
-                CheckDlgButton(hwnd, \r
-                               IDC_PP_IDDEF, \r
-                               ((flags & KCDB_IDENT_FLAG_DEFAULT)?BST_CHECKED:\r
-                                BST_UNCHECKED));\r
-\r
-                /* if it's default, you can't change it further */\r
-                if (flags & KCDB_IDENT_FLAG_DEFAULT) {\r
-                    EnableWindow(GetDlgItem(hwnd, IDC_PP_IDDEF), FALSE);\r
-                }\r
-\r
-                CheckDlgButton(hwnd, IDC_PP_IDSEARCH,\r
-                               ((flags & KCDB_IDENT_FLAG_SEARCHABLE)?BST_CHECKED:BST_UNCHECKED));\r
-\r
-                CheckDlgButton(hwnd, IDC_PP_STICKY,\r
-                               ((flags & KCDB_IDENT_FLAG_STICKY)?BST_CHECKED:BST_UNCHECKED));\r
-                return TRUE;\r
-            }\r
-        }\r
-        break;\r
-    }\r
-    return FALSE;\r
-}\r
-\r
-static INT_PTR CALLBACK \r
-cw_pp_cred_proc(HWND hwnd,\r
-                UINT uMsg,\r
-                WPARAM wParam,\r
-                LPARAM lParam\r
-                )\r
-{\r
-    switch(uMsg) {\r
-        case WM_INITDIALOG:\r
-            {\r
-                khui_property_sheet * s;\r
-                PROPSHEETPAGE * p;\r
-                khm_handle cred;\r
-\r
-                p = (PROPSHEETPAGE *) lParam;\r
-                s = (khui_property_sheet *) p->lParam;\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-                SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) s);\r
-#pragma warning(pop)\r
-\r
-                cred = s->cred;\r
-\r
-                khui_property_wnd_set_record(\r
-                    GetDlgItem(hwnd, IDC_PP_CPROPLIST),\r
-                    cred);\r
-            }\r
-            return TRUE;\r
-    }\r
-    return FALSE;\r
-}\r
-\r
-static void \r
-cw_pp_begin(khui_property_sheet * s)\r
-{\r
-    PROPSHEETPAGE *p;\r
-\r
-    if(s->identity) {\r
-        p = PMALLOC(sizeof(*p));\r
-        ZeroMemory(p, sizeof(*p));\r
-\r
-        p->dwSize = sizeof(*p);\r
-        p->dwFlags = 0;\r
-        p->hInstance = khm_hInstance;\r
-        p->pszTemplate = MAKEINTRESOURCE(IDD_PP_IDENT);\r
-        p->pfnDlgProc = cw_pp_ident_proc;\r
-        p->lParam = (LPARAM) s;\r
-\r
-        khui_ps_add_page(s, KHUI_PPCT_IDENTITY, 129, p, NULL);\r
-    }\r
-\r
-    if(s->cred) {\r
-        p = PMALLOC(sizeof(*p));\r
-        ZeroMemory(p, sizeof(*p));\r
-\r
-        p->dwSize = sizeof(*p);\r
-        p->dwFlags = 0;\r
-        p->hInstance = khm_hInstance;\r
-        p->pszTemplate = MAKEINTRESOURCE(IDD_PP_CRED);\r
-        p->pfnDlgProc = cw_pp_cred_proc;\r
-        p->lParam = (LPARAM) s;\r
-\r
-        khui_ps_add_page(s, KHUI_PPCT_CREDENTIAL, 128, p, NULL);\r
-    }\r
-}\r
-\r
-static void \r
-cw_pp_precreate(khui_property_sheet * s)\r
-{\r
-    khui_ps_show_sheet(khm_hwnd_main, s);\r
-\r
-    khm_add_property_sheet(s);\r
-}\r
-\r
-static void \r
-cw_pp_end(khui_property_sheet * s)\r
-{\r
-    khui_property_page * p = NULL;\r
-\r
-    khui_ps_find_page(s, KHUI_PPCT_IDENTITY, &p);\r
-    if(p) {\r
-        PFREE(p->p_page);\r
-        p->p_page = NULL;\r
-    }\r
-\r
-    p = NULL;\r
-\r
-    khui_ps_find_page(s, KHUI_PPCT_CREDENTIAL, &p);\r
-    if(p) {\r
-        PFREE(p->p_page);\r
-        p->p_page = NULL;\r
-    }\r
-}\r
-\r
-static void \r
-cw_pp_destroy(khui_property_sheet *ps)\r
-{\r
-    if(ps->ctx.scope == KHUI_SCOPE_CRED) {\r
-        if(ps->header.pszCaption)\r
-            PFREE((LPWSTR) ps->header.pszCaption);\r
-    }\r
-\r
-    khui_context_release(&ps->ctx);\r
-\r
-    khui_ps_destroy_sheet(ps);\r
-\r
-    /* this is pretty weird because ps gets freed when\r
-       khui_ps_destroy_sheet() is called.  However, since destroying\r
-       ps involves sending a WM_DESTROY message to the property sheet,\r
-       we still need to keep it on the property sheet chain (or else\r
-       the messages will not be delivered).  This is only safe because\r
-       we are not relinquishing the thread in-between destroying ps\r
-       and removing it from the chain. */\r
-\r
-    /* TODO: fix this */\r
-    khm_del_property_sheet(ps);\r
-}\r
-\r
-LRESULT\r
-cw_properties(HWND hwnd)\r
-{\r
-    /* show a property sheet of some sort */\r
-    khui_action_context ctx;\r
-    khui_property_sheet * ps;\r
-    khui_credwnd_tbl * tbl;\r
-\r
-    khui_context_get(&ctx);\r
-    tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-\r
-    if(ctx.scope == KHUI_SCOPE_NONE) {\r
-        khui_context_release(&ctx);\r
-        return FALSE;\r
-\r
-        /* While it seems like a good idea, doing this is not */\r
-#if 0\r
-        /* try to establish a context based on the current cursor\r
-           position */\r
-        if(tbl->cursor_row >= 0 && tbl->cursor_row < (int) tbl->n_rows) {\r
-            if(tbl->rows[tbl->cursor_row].flags & KHUI_CW_ROW_HEADER) {\r
-                if(tbl->cols[tbl->rows[tbl->cursor_row].col].attr_id == KCDB_ATTR_ID_NAME) {\r
-                    /* identity context */\r
-                    ctx.ctx = KHUI_SCOPE_IDENT;\r
-                    ctx.identity = (khm_handle) \r
-                        ((khui_credwnd_outline *) tbl->rows[tbl->cursor_row].data)->data;\r
-                } else if(tbl->cols[tbl->rows[tbl->cursor_row].col].attr_id == KCDB_ATTR_TYPE_NAME) {\r
-                    ctx.ctx = KHUI_SCOPE_CREDTYPE;\r
-                    ctx.cred_type = (khm_int32) (DWORD_PTR) \r
-                        ((khui_credwnd_outline *) tbl->rows[tbl->cursor_row].data)->data;\r
-                } else {\r
-                    ctx.ctx = KHUI_SCOPE_GROUP;\r
-                    //ctx.parm = (khm_lparm) tbl->rows[tbl->cursor_row].data;\r
-                    /* TODO: Figure out method of establishing a credgroup */\r
-                }\r
-            } else {\r
-                /* a credential context */\r
-                ctx.ctx = KHUI_SCOPE_CRED;\r
-                ctx.cred = (khm_handle) tbl->rows[tbl->cursor_row].data;\r
-            }\r
-        }\r
-#endif\r
-    }\r
-\r
-    /* if still no context, then we can't show a property sheet */\r
-    if(ctx.scope == KHUI_SCOPE_NONE) {\r
-        khui_context_release(&ctx);\r
-        return FALSE;\r
-    }\r
-\r
-    khui_ps_create_sheet(&ps);\r
-\r
-    if(ctx.scope == KHUI_SCOPE_IDENT) {\r
-        khm_handle ident;\r
-        khm_size t;\r
-\r
-        ident = ctx.identity;\r
-\r
-        ps->header.hInstance = khm_hInstance;\r
-        ps->header.pszIcon = MAKEINTRESOURCE(IDI_MAIN_APP);\r
-\r
-        kcdb_identity_get_name(ident, NULL, &t);\r
-\r
-        if(t > 0) {\r
-            ps->header.pszCaption = PMALLOC(t);\r
-            kcdb_identity_get_name(ident,\r
-                                   (wchar_t *) ps->header.pszCaption, &t);\r
-        } else {\r
-            ps->header.pszCaption = NULL;\r
-        }\r
-\r
-        ps->ctx = ctx;\r
-        ps->identity = ident;\r
-        ps->credtype = KCDB_CREDTYPE_INVALID;\r
-\r
-        kmq_post_message(KMSG_CRED, KMSG_CRED_PP_BEGIN, 0, (void *) ps);\r
-\r
-    } else if(ctx.scope == KHUI_SCOPE_CREDTYPE) {\r
-        khm_size t = 0;\r
-        khm_int32 cred_type;\r
-\r
-        if (ctx.identity == NULL) {\r
-            /* currently, we can't show a property sheet at this point\r
-               since most credentials providers don't provide a\r
-               property sheet that works without an identity. */\r
-\r
-            khui_context_release(&ctx);\r
-            khui_ps_destroy_sheet(ps);\r
-            return TRUE;\r
-        }\r
-\r
-        cred_type = ctx.cred_type;\r
-\r
-        ps->header.hInstance = khm_hInstance;\r
-        ps->header.pszIcon = MAKEINTRESOURCE(IDI_MAIN_APP);\r
-\r
-        ps->ctx = ctx;\r
-        ps->credtype = cred_type;\r
-\r
-        if(ctx.identity) {\r
-            ps->identity = ctx.identity;\r
-            /* also, if there is an associated identity, we assume that\r
-               the properties are for the specified credentials type\r
-               specific to the identity.  Hence we change the title to\r
-               something else */\r
-            kcdb_identity_get_name(ctx.identity, NULL, &t);\r
-            if (t > 0) {\r
-                ps->header.pszCaption = PMALLOC(t);\r
-                kcdb_identity_get_name(ctx.identity, (wchar_t *) ps->header.pszCaption, &t);\r
-            } else {\r
-                ps->header.pszCaption = NULL;\r
-            }\r
-        } else {\r
-            /* we don't actually reach here since we handle this case\r
-               above */\r
-            kcdb_credtype_describe(cred_type, NULL, &t, KCDB_TS_LONG);\r
-            if(t > 0) {\r
-                ps->header.pszCaption = PMALLOC(t);\r
-                kcdb_credtype_describe(cred_type, (wchar_t *) ps->header.pszCaption, &t, KCDB_TS_LONG);\r
-            } else {\r
-                ps->header.pszCaption = NULL;\r
-            }\r
-        }\r
-\r
-        kmq_post_message(KMSG_CRED, KMSG_CRED_PP_BEGIN, 0, (void *) ps);\r
-    } else if(ctx.scope == KHUI_SCOPE_CRED) {\r
-        khm_handle cred;\r
-        khm_size t;\r
-\r
-        cred = ctx.cred;\r
-\r
-        ps->header.hInstance = khm_hInstance;\r
-        ps->header.pszIcon = MAKEINTRESOURCE(IDI_MAIN_APP);\r
-        ps->ctx = ctx;\r
-\r
-        kcdb_cred_get_name(cred, NULL, &t);\r
-        ps->header.pszCaption = PMALLOC(t);\r
-        kcdb_cred_get_name(cred, (LPWSTR) ps->header.pszCaption, &t);\r
-\r
-        kcdb_cred_get_identity(cred, &ps->identity);\r
-        kcdb_cred_get_type(cred, &ps->credtype);\r
-        ps->cred = cred;\r
-\r
-        kmq_post_message(KMSG_CRED, KMSG_CRED_PP_BEGIN, 0, (void *) ps);\r
-    } else {\r
-        khui_context_release(&ctx);\r
-        khui_ps_destroy_sheet(ps);\r
-    }\r
-\r
-    /* by the way, if we are actually opening a property sheet, we\r
-       leave ctx held (which is now copied to ps->ctx).  it will be\r
-       released when the property sheet is destroyed */\r
-\r
-    return TRUE;\r
-}\r
-\r
-LRESULT \r
-cw_wm_command(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
-{\r
-    khui_credwnd_tbl * tbl;\r
-\r
-    tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-\r
-    if(HIWORD(wParam) == BN_CLICKED && \r
-       LOWORD(wParam) == KHUI_HTWND_CTLID) {\r
-\r
-        wchar_t wid[256];\r
-        /* a hyperlink was activated */\r
-        khui_htwnd_link * l;\r
-        l = (khui_htwnd_link *) lParam;\r
-        StringCchCopyN(wid, ARRAYLENGTH(wid), l->id, l->id_len);\r
-        wid[l->id_len] = 0;\r
-\r
-        if(!wcscmp(wid, L"NewCreds")) {\r
-            PostMessage(khm_hwnd_main, WM_COMMAND, \r
-                        MAKEWPARAM(KHUI_ACTION_NEW_CRED,0), 0);\r
-        }\r
-        return TRUE;\r
-    }\r
-\r
-    switch(LOWORD(wParam)) \r
-    {\r
-    case KHUI_PACTION_ENTER:\r
-        /* enter key is a synonym for the default action, on the\r
-        context, which is to lauch a property sheet */\r
-        /* fallthrough */\r
-    case KHUI_ACTION_PROPERTIES:\r
-        {\r
-            return cw_properties(hwnd);\r
-        }\r
-        break;\r
-\r
-    case KHUI_ACTION_LAYOUT_RELOAD:\r
-        {\r
-            wchar_t cname[KCONF_MAXCCH_NAME];\r
-            khm_size cb;\r
-\r
-            cname[0] = L'\0';\r
-\r
-            if (tbl->csp_view) {\r
-                cb = sizeof(cname);\r
-                khc_get_config_space_name(tbl->csp_view,\r
-                                          cname,\r
-                                          &cb);\r
-            }\r
-\r
-            cw_unload_view(tbl);\r
-\r
-            cw_load_view(tbl, ((cname[0])?cname: NULL), hwnd);\r
-            cw_insert_header_cols(tbl);\r
-\r
-            cw_update_creds(tbl);\r
-            cw_update_outline(tbl);\r
-            cw_update_selection_state(tbl);\r
-            cw_update_extents(tbl, TRUE);\r
-\r
-            InvalidateRect(tbl->hwnd, NULL, TRUE);\r
-        }\r
-        break;\r
-\r
-    case KHUI_ACTION_LAYOUT_ID:\r
-        {\r
-            cw_save_view(tbl, NULL);\r
-            cw_unload_view(tbl);\r
-\r
-            cw_load_view(tbl, L"ByIdentity", hwnd);\r
-            cw_insert_header_cols(tbl);\r
-\r
-            cw_update_creds(tbl);\r
-            cw_update_outline(tbl);\r
-            cw_update_selection_state(tbl);\r
-            cw_update_extents(tbl, TRUE);\r
-\r
-            InvalidateRect(tbl->hwnd, NULL, TRUE);\r
-\r
-        }\r
-        break;\r
-\r
-    case KHUI_ACTION_LAYOUT_LOC:\r
-        {\r
-            cw_save_view(tbl, NULL);\r
-            cw_unload_view(tbl);\r
-\r
-            cw_load_view(tbl, L"ByLocation", hwnd);\r
-            cw_insert_header_cols(tbl);\r
-\r
-            cw_update_creds(tbl);\r
-            cw_update_outline(tbl);\r
-            cw_update_selection_state(tbl);\r
-            cw_update_extents(tbl, TRUE);\r
-\r
-            InvalidateRect(tbl->hwnd, NULL, TRUE);\r
-\r
-        }\r
-        break;\r
-\r
-    case KHUI_ACTION_LAYOUT_TYPE:\r
-        {\r
-            cw_save_view(tbl, NULL);\r
-            cw_unload_view(tbl);\r
-\r
-            cw_load_view(tbl, L"ByType", hwnd);\r
-            cw_insert_header_cols(tbl);\r
-\r
-            cw_update_creds(tbl);\r
-            cw_update_outline(tbl);\r
-            cw_update_selection_state(tbl);\r
-            cw_update_extents(tbl, TRUE);\r
-\r
-            InvalidateRect(tbl->hwnd, NULL, TRUE);\r
-\r
-        }\r
-        break;\r
-\r
-    case KHUI_ACTION_LAYOUT_CUST:\r
-        {\r
-            cw_save_view(tbl, NULL);\r
-            cw_unload_view(tbl);\r
-\r
-            cw_load_view(tbl, L"Custom_0", hwnd);\r
-            cw_insert_header_cols(tbl);\r
-\r
-            cw_update_creds(tbl);\r
-            cw_update_outline(tbl);\r
-            cw_update_selection_state(tbl);\r
-            cw_update_extents(tbl, TRUE);\r
-\r
-            InvalidateRect(tbl->hwnd, NULL, TRUE);\r
-\r
-        }\r
-        break;\r
-\r
-    case KHUI_ACTION_LAYOUT_MINI:\r
-        {\r
-            cw_save_view(tbl, NULL);\r
-            cw_unload_view(tbl);\r
-\r
-            cw_load_view(tbl, NULL, hwnd);\r
-            cw_insert_header_cols(tbl);\r
-\r
-            cw_update_creds(tbl);\r
-            cw_update_outline(tbl);\r
-            cw_update_selection_state(tbl);\r
-            cw_update_extents(tbl, TRUE);\r
-\r
-            InvalidateRect(tbl->hwnd, NULL, TRUE);\r
-        }\r
-        break;\r
-\r
-    case KHUI_PACTION_UP:\r
-    case KHUI_PACTION_UP_EXTEND:\r
-    case KHUI_PACTION_UP_TOGGLE:\r
-        { /* cursor up */\r
-            khm_int32 new_row;\r
-            WPARAM wp = 0;\r
-\r
-            new_row = tbl->cursor_row - 1;\r
-\r
-            /* checking both bounds.  we make no assumption about the\r
-               value of cursor_row before this message */\r
-            if(new_row < 0)\r
-                new_row = 0;\r
-            if(new_row >= (int) tbl->n_rows)\r
-                new_row = (int) tbl->n_rows - 1;\r
-\r
-            if (LOWORD(wParam) == KHUI_PACTION_UP)\r
-                wp = 0;\r
-            else if (LOWORD(wParam) == KHUI_PACTION_UP_EXTEND)\r
-                wp = MK_SHIFT;\r
-            else if (LOWORD(wParam) == KHUI_PACTION_UP_TOGGLE)\r
-                wp = 0; //MK_CONTROL;\r
-            else {\r
-#ifdef DEBUG\r
-                assert(FALSE);\r
-#endif\r
-            }\r
-\r
-            cw_select_row(tbl, new_row, wp);\r
-            cw_ensure_row_visible(hwnd, tbl, new_row);\r
-        }\r
-        break;\r
-\r
-    case KHUI_PACTION_PGUP_EXTEND:\r
-    case KHUI_PACTION_PGUP:\r
-        {\r
-            khm_int32 new_row;\r
-            WPARAM wp;\r
-            RECT r;\r
-\r
-            if (LOWORD(wParam) == KHUI_PACTION_PGUP_EXTEND)\r
-                wp = MK_SHIFT;\r
-            else\r
-                wp = 0;\r
-\r
-            GetClientRect(hwnd, &r);\r
-\r
-            new_row = tbl->cursor_row -\r
-                ((r.bottom - r.top) - tbl->header_height) / tbl->cell_height;\r
-\r
-            if (new_row < 0)\r
-                new_row = 0;\r
-            if (new_row >= (int) tbl->n_rows)\r
-                new_row = (int) tbl->n_rows - 1;\r
-\r
-            cw_select_row(tbl, new_row, wp);\r
-            cw_ensure_row_visible(hwnd, tbl, new_row);\r
-        }\r
-        break;\r
-\r
-    case KHUI_PACTION_DOWN:\r
-    case KHUI_PACTION_DOWN_EXTEND:\r
-    case KHUI_PACTION_DOWN_TOGGLE:\r
-        { /* cursor down */\r
-            khm_int32 new_row;\r
-            WPARAM wp = 0;\r
-\r
-            new_row = tbl->cursor_row + 1;\r
-\r
-            /* checking both bounds.  we make no assumption about the\r
-               value of cursor_row before this message */\r
-            if(new_row < 0)\r
-                new_row = 0;\r
-            if(new_row >= (int) tbl->n_rows)\r
-                new_row = (int) tbl->n_rows - 1;\r
-\r
-            if (LOWORD(wParam) == KHUI_PACTION_DOWN)\r
-                wp = 0;\r
-            else if (LOWORD(wParam) == KHUI_PACTION_DOWN_EXTEND)\r
-                wp = MK_SHIFT;\r
-            else if (LOWORD(wParam) == KHUI_PACTION_DOWN_TOGGLE)\r
-                wp = 0; //MK_CONTROL;\r
-            else {\r
-#ifdef DEBUG\r
-                assert(FALSE);\r
-#endif\r
-            }\r
-\r
-            cw_select_row(tbl, new_row, wp);\r
-            cw_ensure_row_visible(hwnd, tbl, new_row);\r
-        }\r
-        break;\r
-\r
-    case KHUI_PACTION_PGDN_EXTEND:\r
-    case KHUI_PACTION_PGDN:\r
-        {\r
-            khm_int32 new_row;\r
-            RECT r;\r
-            WPARAM wp;\r
-\r
-            if (LOWORD(wParam) == KHUI_PACTION_PGDN_EXTEND)\r
-                wp = MK_SHIFT;\r
-            else\r
-                wp = 0;\r
-\r
-            GetClientRect(hwnd, &r);\r
-\r
-            new_row = tbl->cursor_row +\r
-                ((r.bottom - r.top) - tbl->header_height) / tbl->cell_height;\r
-\r
-            if (new_row < 0)\r
-                new_row = 0;\r
-            if (new_row >= (int) tbl->n_rows)\r
-                new_row = (int) tbl->n_rows - 1;\r
-\r
-            cw_select_row(tbl, new_row, wp);\r
-            cw_ensure_row_visible(hwnd, tbl, new_row);\r
-        }\r
-        break;\r
-\r
-    case KHUI_PACTION_SELALL:\r
-        {\r
-            cw_select_all(tbl);\r
-        }\r
-        break;\r
-\r
-    case KHUI_PACTION_LEFT:\r
-        { /* collapse and up*/\r
-            khui_credwnd_outline * o;\r
-            int r;\r
-\r
-            if(tbl->cursor_row < 0 || tbl->cursor_row >= (int) tbl->n_rows) {\r
-                cw_select_row(tbl, 0, 0);\r
-                break;\r
-            }\r
-\r
-            for(r = tbl->cursor_row; \r
-                (r >= 0 && !(tbl->rows[r].flags & KHUI_CW_ROW_HEADER));\r
-                r--);\r
-            \r
-            if(r < 0)\r
-                break;\r
-\r
-            /* If we were not on a header, we collapse the innermost\r
-               outline. Otherwise, we collpase up to the parent\r
-               outline level */\r
-\r
-            if(r != tbl->cursor_row) {\r
-                o = (khui_credwnd_outline *) tbl->rows[r].data;\r
-\r
-                cw_toggle_outline_state(tbl, o);\r
-            } else {\r
-                o = (khui_credwnd_outline *) tbl->rows[r].data;\r
-\r
-                if(o->flags & KHUI_CW_O_EXPAND) {\r
-                    cw_toggle_outline_state(tbl, o);\r
-                } else {\r
-                    o = TPARENT(o);\r
-                    if(o) {\r
-                        cw_toggle_outline_state(tbl, o);\r
-                        r = o->start;\r
-                    } else if(r > 0)\r
-                        r--;\r
-                }\r
-            }\r
-\r
-            cw_select_row(tbl, r, 0);\r
-        }\r
-        break;\r
-\r
-    case KHUI_PACTION_RIGHT:\r
-        { /* expand and down*/\r
-            khui_credwnd_outline * o;\r
-            int r;\r
-\r
-            if(tbl->cursor_row < 0 || \r
-               tbl->cursor_row >= (int) tbl->n_rows) {\r
-                cw_select_row(tbl, 0, 0);\r
-                break;\r
-            }\r
-\r
-            r = tbl->cursor_row;\r
-\r
-            if(tbl->rows[r].flags & KHUI_CW_ROW_HEADER) {\r
-                o = (khui_credwnd_outline *) tbl->rows[r].data;\r
-                if(!(o->flags & KHUI_CW_O_EXPAND)) {\r
-                    cw_toggle_outline_state(tbl, o);\r
-                }\r
-            }\r
-\r
-            r++;\r
-            if (r >= (int) tbl->n_rows)\r
-                r = (int)tbl->n_rows - 1;\r
-\r
-            cw_select_row(tbl, r, 0);\r
-        }\r
-        break;\r
-    }\r
-    return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
-}\r
-\r
-LRESULT \r
-cw_wm_contextmenu(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
-{\r
-    RECT r;\r
-    int x,y;\r
-    int row;\r
-    khui_credwnd_tbl * tbl;\r
-\r
-    tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-\r
-    GetWindowRect(hwnd, &r);\r
-\r
-    x = GET_X_LPARAM(lParam);\r
-    y = GET_Y_LPARAM(lParam);\r
-\r
-    x += tbl->scr_left - r.left;\r
-    y += tbl->scr_top - tbl->header_height - r.top;\r
-\r
-    if (y < 0) {\r
-        /* context menu for header control */\r
-        khm_menu_show_panel(KHUI_MENU_CWHEADER_CTX,\r
-                            GET_X_LPARAM(lParam),\r
-                            GET_Y_LPARAM(lParam));\r
-\r
-        return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
-    }\r
-\r
-    if (tbl->flags & KHUI_CW_TBL_EXPIDENT) {\r
-        int i, yt;\r
-\r
-        yt = 0;\r
-        for (i=0; i < tbl->n_rows && yt < y; i++) {\r
-            if (tbl->rows[i].flags & KHUI_CW_ROW_EXPVIEW)\r
-                yt += tbl->cell_height * CW_EXP_ROW_MULT;\r
-            else\r
-                yt += tbl->cell_height;\r
-            if (yt > y)\r
-                break;\r
-        }\r
-\r
-        row = i;\r
-\r
-    } else {\r
-        row = y / tbl->cell_height;\r
-    }\r
-\r
-    if(row < 0 || row >= (int) tbl->n_rows)\r
-        return FALSE;\r
-\r
-    cw_set_row_context(tbl, row);\r
-\r
-    khm_menu_show_panel(KHUI_MENU_IDENT_CTX, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));\r
-\r
-#if 0\r
-    /* calling cw_set_row_context() should take care of enabling or\r
-       disabling actions as appropriate.  We don't need to\r
-       differentiate between IDENT_CTX and TOK_CTX here. */\r
-    if((tbl->rows[row].flags & KHUI_CW_ROW_HEADER) &&\r
-       (tbl->cols[tbl->rows[row].col].attr_id == KCDB_ATTR_ID_NAME)) {\r
-        khm_menu_show_panel(KHUI_MENU_IDENT_CTX, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));\r
-        //khui_context_reset();\r
-    } else {\r
-        khm_menu_show_panel(KHUI_MENU_TOK_CTX, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));\r
-        //khui_context_reset();\r
-    }\r
-#endif\r
-\r
-    return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
-}\r
-\r
-/* copy and paste template */\r
-#if 0\r
-LRESULT \r
-cw_wm_msg(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
-{\r
-    return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
-}\r
-#endif\r
-\r
-LRESULT CALLBACK \r
-khm_credwnd_proc(HWND hwnd,\r
-                  UINT uMsg,\r
-                  WPARAM wParam,\r
-                  LPARAM lParam) \r
-{\r
-    switch(uMsg) {\r
-    case WM_COMMAND:\r
-        return cw_wm_command(hwnd, uMsg, wParam, lParam);\r
-\r
-    case WM_CREATE:\r
-        return cw_wm_create(hwnd, uMsg, wParam, lParam);\r
-\r
-    case WM_DESTROY:\r
-        return cw_wm_destroy(hwnd, uMsg, wParam, lParam);\r
-\r
-    case WM_ERASEBKGND:\r
-        /* we don't bother wasting cycles erasing the background\r
-           because the foreground elements completely cover the\r
-           client area */\r
-        return FALSE;\r
-\r
-    case WM_PAINT:\r
-        return cw_wm_paint(hwnd, uMsg, wParam, lParam);\r
-\r
-    case WM_PRINTCLIENT:\r
-        return cw_wm_paint(hwnd, uMsg, wParam, lParam);\r
-\r
-    case WM_SIZE:\r
-        return cw_wm_size(hwnd, uMsg, wParam, lParam);\r
-\r
-    case WM_NOTIFY:\r
-        return cw_wm_notify(hwnd, uMsg, wParam, lParam);\r
-\r
-    case WM_HSCROLL:\r
-        return cw_wm_hscroll(hwnd, uMsg, wParam, lParam);\r
-\r
-    case WM_VSCROLL:\r
-        return cw_wm_vscroll(hwnd, uMsg, wParam, lParam);\r
-\r
-    case KMQ_WM_DISPATCH:\r
-        return cw_kmq_wm_dispatch(hwnd, uMsg, wParam, lParam);\r
-\r
-    case WM_LBUTTONDBLCLK:\r
-    case WM_LBUTTONDOWN:\r
-    case WM_MOUSEMOVE:\r
-    case WM_LBUTTONUP:\r
-        return cw_wm_mouse(hwnd, uMsg, wParam, lParam);\r
-\r
-    case WM_CONTEXTMENU:\r
-        return cw_wm_contextmenu(hwnd, uMsg, wParam, lParam);\r
-    }\r
-\r
-    return DefWindowProc(hwnd,uMsg,wParam,lParam);\r
-}\r
-\r
-void \r
-khm_register_credwnd_class(void) {\r
-    WNDCLASSEX wcx;\r
-    kcdb_attrib attrib;\r
-    khm_int32 attr_id;\r
-\r
-    wcx.cbSize = sizeof(wcx);\r
-    wcx.style = CS_DBLCLKS | CS_OWNDC;\r
-    wcx.lpfnWndProc = khm_credwnd_proc;\r
-    wcx.cbClsExtra = 0;\r
-    wcx.cbWndExtra = sizeof(LONG_PTR);\r
-    wcx.hInstance = khm_hInstance;\r
-    wcx.hIcon = NULL;\r
-    wcx.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);\r
-    wcx.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);\r
-    wcx.lpszMenuName = NULL;\r
-    wcx.lpszClassName = KHUI_CREDWND_CLASS_NAME;\r
-    wcx.hIconSm = NULL;\r
-\r
-    khui_credwnd_cls = RegisterClassEx(&wcx);\r
-\r
-    /* while we are at it, register the credwnd attribute type as well, and\r
-    obtain the type ID */\r
-    if(KHM_FAILED(kcdb_attrib_get_id(KHUI_CREDWND_FLAG_ATTRNAME, &attr_id))) {\r
-        ZeroMemory(&attrib, sizeof(attrib));\r
-        attrib.id = KCDB_ATTR_INVALID;\r
-        attrib.flags = KCDB_ATTR_FLAG_HIDDEN;\r
-        attrib.type = KCDB_TYPE_INT32;\r
-        attrib.name = KHUI_CREDWND_FLAG_ATTRNAME;\r
-\r
-        kcdb_attrib_register(&attrib, &attr_id);\r
-    }\r
-\r
-    khui_cw_flag_id = attr_id;\r
-}\r
-\r
-void \r
-khm_unregister_credwnd_class(void) {\r
-    UnregisterClass(MAKEINTATOM(khui_credwnd_cls), khm_hInstance);\r
-}\r
-\r
-HWND \r
-khm_create_credwnd(HWND parent) {\r
-    RECT r;\r
-    HWND hwnd;\r
-\r
-    ZeroMemory(attr_to_action, sizeof(attr_to_action));\r
-\r
-    GetClientRect(parent, &r);\r
-\r
-    hwnd = CreateWindowEx\r
-        (0,\r
-         MAKEINTATOM(khui_credwnd_cls),\r
-         L"",\r
-         WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,\r
-         r.left,\r
-         r.top,\r
-         r.right - r.left,\r
-         r.bottom - r.top,\r
-         parent,\r
-         NULL,\r
-         khm_hInstance,\r
-         NULL);\r
-\r
-    return hwnd;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 attr_to_action[KCDB_ATTR_MAX_ID + 1];
+
+void
+khm_set_cw_element_font(wchar_t * name, LOGFONT * pfont) {
+    khm_handle csp_cw = NULL;
+    wchar_t * element_name;
+
+    if (name == NULL)
+        element_name = L"FontBase";
+    else
+        element_name = name;
+
+    if (KHM_FAILED(khc_open_space(NULL, L"CredWindow", KHM_PERM_WRITE,
+                                  &csp_cw)))
+        return;
+
+    khc_write_binary(csp_cw, element_name, pfont, sizeof(LOGFONT));
+
+    khc_close_space(csp_cw);
+}
+
+void
+khm_get_cw_element_font(HDC hdc, wchar_t * name, BOOL use_default, LOGFONT * pfont) {
+    khm_handle csp_cw = NULL;
+    khm_size cb;
+    wchar_t * element_name;
+    khm_boolean try_derive = FALSE;
+
+    if (name == NULL)
+        element_name = L"FontBase";
+    else
+        element_name = name;
+
+    if (use_default)
+        goto _use_defaults;
+
+    if (KHM_FAILED(khc_open_space(NULL, L"CredWindow", 0,
+                                  &csp_cw)))
+        goto _use_defaults;
+
+    cb = sizeof(LOGFONT);
+    if (KHM_FAILED(khc_read_binary(csp_cw, element_name, pfont,
+                                   &cb)) ||
+        cb != sizeof(LOGFONT)) {
+        try_derive = TRUE;
+    }
+
+    if (try_derive) {
+        cb = sizeof(LOGFONT);
+        if (!name ||
+            KHM_FAILED(khc_read_binary(csp_cw, L"FontBase", pfont,
+                                       &cb)) ||
+            cb != sizeof(LOGFONT)) {
+            khc_close_space(csp_cw);
+            goto _use_defaults;
+        }
+
+        if (!wcscmp(name, L"FontHeaderBold") ||
+            !wcscmp(name, L"FontBold")) {
+
+            pfont->lfWeight = FW_BOLD;
+
+        }
+    }
+
+    khc_close_space(csp_cw);
+
+    return;
+
+ _use_defaults:
+
+    ZeroMemory(pfont, sizeof(*pfont));
+
+    if (name == NULL) {
+        LOGFONT lf = {
+            -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"};
+
+        *pfont = lf;
+
+    } else if (!wcscmp(name, L"FontHeader")) {
+        LOGFONT lf = {
+            -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"};
+
+        *pfont = lf;
+
+    } else if (!wcscmp(name, L"FontHeaderBold")) {
+        LOGFONT lf = {
+            -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"};
+
+        *pfont = lf;
+
+    } else if (!wcscmp(name, L"FontNormal")) {
+        LOGFONT lf = {
+            -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"};
+
+        *pfont = lf;
+
+    } else if (!wcscmp(name, L"FontBold")) {
+        LOGFONT lf = {
+            -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"};
+
+        *pfont = lf;
+
+    } else {
+#ifdef DEBUG
+        assert(FALSE);
+#endif
+    }
+}
+
+void
+cw_refresh_attribs(HWND hwnd) {
+    khm_int32 act;
+    kcdb_attrib * attrib;
+    khui_menu_def * menu;
+    khm_int32 i;
+
+    menu = khui_find_menu(KHUI_MENU_COLUMNS);
+#ifdef DEBUG
+    assert(menu);
+#endif
+
+    for (i=0; i <= KCDB_ATTR_MAX_ID; i++) {
+        if (KHM_FAILED(kcdb_attrib_get_info(i, &attrib))) {
+            if (attr_to_action[i] != 0) {
+                /* the action should be removed */
+                khui_menu_remove_action(menu, attr_to_action[i]);
+                khui_action_delete(attr_to_action[i]);
+                attr_to_action[i] = 0;
+            }
+        } else {
+            if (attr_to_action[i] == 0 &&
+                !(attrib->flags & KCDB_ATTR_FLAG_HIDDEN) &&
+                (attrib->short_desc || attrib->long_desc)) {
+                /* new action */
+                khm_handle sub = NULL;
+
+                kmq_create_hwnd_subscription(hwnd, &sub);
+
+                act = khui_action_create(attrib->name,
+                                         (attrib->short_desc?
+                                          attrib->short_desc: attrib->long_desc),
+                                         NULL,
+                                         (void *)(UINT_PTR) i,
+                                         KHUI_ACTIONTYPE_TOGGLE,
+                                         sub);
+
+                attr_to_action[i] = act;
+
+                khui_menu_insert_action(menu, 5000, act, 0);
+            }
+
+            kcdb_attrib_release_info(attrib);
+        }
+    }
+}
+
+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;
+}
+
+const wchar_t *
+cw_get_custom_attr_string(khm_int32 attr_id)
+{
+    if (attr_id == CW_CA_FLAGS)
+        return CW_CANAME_FLAGS;
+    if (attr_id == CW_CA_TYPEICON)
+        return CW_CANAME_TYPEICON;
+    return NULL;
+}
+
+void
+cw_save_view(khui_credwnd_tbl * tbl, wchar_t * view_name) {
+    wchar_t * col_list = NULL;
+    khm_size cb_col_list;
+    khm_handle csp_cw = NULL;
+    khm_handle csp_views = NULL;
+    khm_handle csp_view = NULL;
+    khm_handle csp_cols = NULL;
+    khm_size cb;
+    int i;
+
+    if (tbl->n_cols == 0)
+        return;
+
+    cb_col_list = (KCONF_MAXCB_NAME + 1) * tbl->n_cols;
+
+    col_list = PMALLOC(cb_col_list);
+#ifdef DEBUG
+    assert(col_list);
+#endif
+
+    if (!col_list)
+        goto _cleanup;
+
+    multi_string_init(col_list, cb_col_list);
+
+    /* if we aren't saving to a specific view, and the view has been
+       customized, then we save it to "Custom_0", unless we are in the
+       mini mode, in which case we save it to "Custom_1" */
+    if (!view_name && (tbl->flags & KHUI_CW_TBL_CUSTVIEW)) {
+        if (!(tbl->flags & KHUI_CW_TBL_EXPIDENT)) {
+            view_name = L"Custom_0";
+        } else {
+            view_name = L"Custom_1";
+        }
+    }
+
+    if (view_name) {
+        if (KHM_FAILED(khc_open_space(NULL, L"CredWindow",
+                                      KHM_PERM_READ | KHM_PERM_WRITE, &csp_cw)))
+            goto _cleanup;
+
+        if (KHM_FAILED(khc_open_space(csp_cw, L"Views", KHM_PERM_READ, &csp_views)))
+            goto _cleanup;
+
+        if (KHM_FAILED(khc_open_space(csp_views, view_name,
+                                      KHM_PERM_WRITE | KHM_FLAG_CREATE,
+                                      &csp_view)))
+            goto _cleanup;
+
+        /* if we are switching to a custom view, then we should mark
+           that as the default. */
+        if (tbl->flags & KHUI_CW_TBL_CUSTVIEW) {
+            khc_write_string(csp_cw, ((!(tbl->flags & KHUI_CW_TBL_EXPIDENT))?
+                                      L"DefaultView":
+                                      L"DefaultViewMini"), view_name);
+        }
+
+    } else {
+        csp_view = tbl->csp_view;
+    }
+
+    if (!csp_view)
+        goto _cleanup;
+
+    if (tbl->flags & KHUI_CW_TBL_EXPIDENT) {
+        khc_write_int32(csp_view, L"ExpandedIdentity", 1);
+    } else {
+        khm_int32 t;
+        if (KHM_SUCCEEDED(khc_read_int32(csp_view, L"ExpandedIdentity", &t)) && t)
+            khc_write_int32(csp_view, L"ExpandedIdentity", 0);
+    }
+
+    if (tbl->flags & KHUI_CW_TBL_NOHEADER) {
+        khc_write_int32(csp_view, L"NoHeader", 1);
+    } else {
+        khm_int32 t;
+        if (KHM_SUCCEEDED(khc_read_int32(csp_view, L"NoHeader", &t)) && t)
+            khc_write_int32(csp_view, L"NoHeader", 0);
+    }
+
+    if (KHM_FAILED(khc_open_space(csp_view, L"Columns",
+                                  KHM_PERM_WRITE | KHM_FLAG_CREATE,
+                                  &csp_cols)))
+        goto _cleanup;
+
+    for (i=0; i < tbl->n_cols; i++) {
+        const wchar_t * attr_name;
+        kcdb_attrib * attrib = NULL;
+        khm_handle csp_col = NULL;
+
+        if (tbl->cols[i].attr_id < 0) {
+            attr_name = cw_get_custom_attr_string(tbl->cols[i].attr_id);
+        } else {
+            if (KHM_FAILED(kcdb_attrib_get_info(tbl->cols[i].attr_id,
+                                                &attrib))) {
+#ifdef DEBUG
+                assert(FALSE);
+#endif
+                goto _clean_col;
+            }
+
+            attr_name = attrib->name;
+        }
+#ifdef DEBUG
+        assert(attr_name);
+#endif
+
+        cb = cb_col_list;
+        multi_string_append(col_list, &cb, attr_name);
+
+        if (KHM_FAILED(khc_open_space(csp_cols, attr_name,
+                                      KHM_PERM_WRITE | KHM_FLAG_CREATE,
+                                      &csp_col)))
+            goto _clean_col;
+
+        khc_write_int32(csp_col, L"Width", tbl->cols[i].width);
+        khc_write_int32(csp_col, L"SortIndex", tbl->cols[i].sort_index);
+        khc_write_int32(csp_col, L"Flags", tbl->cols[i].flags);
+
+    _clean_col:
+
+        if (csp_col)
+            khc_close_space(csp_col);
+
+        if (attrib)
+            kcdb_attrib_release_info(attrib);
+    }
+
+    khc_write_multi_string(csp_view, L"ColumnList", col_list);
+
+    {
+        khm_version v = app_version;
+
+        khc_write_binary(csp_view, L"_AppVersion", &v, sizeof(v));
+    }
+
+ _cleanup:
+
+    if (view_name) {
+        if (csp_view)
+            khc_close_space(csp_view);
+
+        if (csp_views)
+            khc_close_space(csp_views);
+
+        if (csp_cw)
+            khc_close_space(csp_cw);
+    }
+
+    if (csp_cols)
+        khc_close_space(csp_cols);
+
+    if (col_list)
+        PFREE(col_list);
+}
+
+static COLORREF
+cw_mix_colors(COLORREF c1, COLORREF c2, int alpha) {
+    int r = (GetRValue(c1) * alpha + GetRValue(c2) * (255 - alpha)) / 255;
+    int g = (GetGValue(c1) * alpha + GetGValue(c2) * (255 - alpha)) / 255;
+    int b = (GetBValue(c1) * alpha + GetBValue(c2) * (255 - alpha)) / 255;
+
+#ifdef DEBUG
+    assert(alpha >= 0 && alpha < 256);
+#endif
+
+    return RGB(r,g,b);
+}
+
+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 * iter = NULL;
+    int i;
+    HDC hdc;
+    LOGFONT log_font;
+    khm_int32 t;
+    const wchar_t * viewval;
+    khm_boolean reopen_csp = FALSE;
+
+    tbl->hwnd = hwnd;
+
+    if (khm_main_wnd_mode == KHM_MAIN_WND_MINI)
+        viewval = L"DefaultViewMini";
+    else
+        viewval = L"DefaultView";
+
+    if(KHM_FAILED(khc_open_space(NULL, L"CredWindow", KHM_PERM_READ | KHM_PERM_WRITE,
+                                 &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, viewval, buf, &cbsize)))
+            goto _exit;
+        view = buf;
+    } else {
+        khc_write_string(hc_cw, viewval, view);
+    }
+
+        /* in addition, if we are loading the default view, we should
+           also check the appropriate menu item */
+
+    if (!wcscmp(view, L"ByIdentity"))
+        khui_check_radio_action(khui_find_menu(KHUI_MENU_LAYOUT),
+                                KHUI_ACTION_LAYOUT_ID);
+    else if (!wcscmp(view, L"ByLocation"))
+        khui_check_radio_action(khui_find_menu(KHUI_MENU_LAYOUT),
+                                KHUI_ACTION_LAYOUT_LOC);
+    else if (!wcscmp(view, L"ByType"))
+        khui_check_radio_action(khui_find_menu(KHUI_MENU_LAYOUT),
+                                KHUI_ACTION_LAYOUT_TYPE);
+    else if (!wcscmp(view, L"Custom_0"))
+        khui_check_radio_action(khui_find_menu(KHUI_MENU_LAYOUT),
+                                KHUI_ACTION_LAYOUT_CUST);
+    else {
+        /* do nothing */
+    }
+
+    kmq_post_message(KMSG_ACT, KMSG_ACT_REFRESH, 0, 0);
+
+    if(KHM_FAILED(khc_open_space(hc_vs, view, 0, &hc_v)))
+        goto _exit;
+
+    /* view data is very sensitive to version changes.  We need to
+       check if this configuration data was created with this version
+       of NetIDMgr.  If not, we switch to using a schema handle. */
+    {
+        khm_version this_v = app_version;
+        khm_version cfg_v;
+
+        cbsize = sizeof(cfg_v);
+        if (KHM_FAILED(khc_read_binary(hc_v, L"_AppVersion", &cfg_v, &cbsize)) ||
+            khm_compare_version(&cfg_v, &this_v) != 0) {
+
+            khc_close_space(hc_v);
+
+            if (KHM_FAILED(khc_open_space(hc_vs, view, KCONF_FLAG_SCHEMA,
+                                          &hc_v)) &&
+                (wcscmp(view, L"Custom_1") ||
+                 KHM_FAILED(khc_open_space(hc_vs, L"CompactIdentity",
+                                           KCONF_FLAG_SCHEMA, &hc_v)))) {
+                goto _exit;
+            }
+
+            reopen_csp = TRUE;
+        }
+    }
+
+    tbl->csp_view = hc_v;
+
+    if(KHM_FAILED(khc_open_space(hc_v, L"Columns",
+                                 KHM_PERM_READ | (reopen_csp ? KCONF_FLAG_SCHEMA : 0),
+                                 &hc_cs)))
+        goto _exit;
+
+    cbsize = 0;
+    if(khc_read_multi_string(hc_v, L"ColumnList", NULL, &cbsize) != KHM_ERROR_TOO_LONG)
+        goto _exit;
+
+    /* temporary */
+    clist = PMALLOC(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 = PMALLOC(sizeof(khui_credwnd_col) * tbl->n_total_cols);
+    ZeroMemory(tbl->cols, sizeof(khui_credwnd_col) * tbl->n_total_cols);
+
+    tbl->flags &= ~(KHUI_CW_TBL_CUSTVIEW | KHUI_CW_TBL_COLSKIP);
+
+    if (KHM_SUCCEEDED(khc_read_int32(hc_v, L"ExpandedIdentity", &t)) && t) {
+        tbl->flags |= KHUI_CW_TBL_EXPIDENT;
+    } else {
+        tbl->flags &= ~KHUI_CW_TBL_EXPIDENT;
+    }
+
+    if (KHM_SUCCEEDED(khc_read_int32(hc_v, L"NoHeader", &t)) && t) {
+        tbl->flags |= KHUI_CW_TBL_NOHEADER;
+    } else {
+        tbl->flags &= ~KHUI_CW_TBL_NOHEADER;
+    }
+
+    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))) {
+                tbl->flags |= KHUI_CW_TBL_COLSKIP;
+                goto _skip_col;
+            }
+
+            if(kcdb_attrib_describe(attr_id, NULL,
+                                    &cbsize, KCDB_TS_SHORT) != KHM_ERROR_TOO_LONG ||
+               cbsize == 0) {
+                tbl->flags |= KHUI_CW_TBL_COLSKIP;
+                goto _skip_col;
+            }
+
+            tbl->cols[i].title = PMALLOC(cbsize);
+            kcdb_attrib_describe(attr_id, tbl->cols[i].title, &cbsize, KCDB_TS_SHORT);
+
+            if (attr_id >= 0 &&
+                attr_id <= KCDB_ATTR_MAX_ID &&
+                attr_to_action[attr_id]) {
+                khui_check_action(attr_to_action[attr_id], TRUE);
+            }
+
+        } 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 | (reopen_csp ? KCONF_FLAG_SCHEMA : 0),
+                                        &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 = 100;
+            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);
+    }
+
+    /* refresh the menus since we checked a few items */
+    kmq_post_message(KMSG_ACT, KMSG_ACT_REFRESH, 0, 0);
+
+    /* 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);
+
+    khm_get_cw_element_font(hdc, L"FontHeader", FALSE, &log_font);
+    tbl->hf_header = CreateFontIndirect(&log_font);
+
+    if(tbl->hf_header && tbl->hwnd_header)
+        SendMessage(tbl->hwnd_header, WM_SETFONT, (WPARAM) tbl->hf_header, 0);
+
+    khm_get_cw_element_font(hdc, L"FontHeaderBold", FALSE, &log_font);
+    tbl->hf_bold_header = CreateFontIndirect(&log_font);
+
+
+    khm_get_cw_element_font(hdc, L"FontNormal", FALSE, &log_font);
+    tbl->hf_normal = CreateFontIndirect(&log_font);
+
+    khm_get_cw_element_font(hdc, L"FontBold", FALSE, &log_font);
+    tbl->hf_bold = CreateFontIndirect(&log_font);
+
+    ReleaseDC(hwnd, hdc);
+
+    khui_bitmap_from_hbmp(&(tbl->kbm_logo_shade),
+                          LoadImage(khm_hInstance,
+                                    MAKEINTRESOURCE(IDB_LOGO_SHADE),
+                                    IMAGE_BITMAP,
+                                    0,
+                                    0,
+                                    LR_DEFAULTCOLOR));
+
+    {
+#define SEL_ALPHA 50
+
+        COLORREF bg_s = GetSysColor(COLOR_HIGHLIGHT);
+        COLORREF bg_normal = GetSysColor(COLOR_WINDOW);
+        COLORREF bg_gray = RGB(240,240,240);
+        COLORREF bg_hdr = RGB(240,240,240);
+        COLORREF bg_hdr_warn = RGB(235,235,134);
+        COLORREF bg_hdr_crit = RGB(235,184,134);
+        COLORREF bg_hdr_exp = RGB(235,134,134);
+        COLORREF bg_hdr_def = RGB(184,235,134);
+
+        tbl->cr_normal =       GetSysColor(COLOR_WINDOWTEXT);
+        tbl->cr_s =            GetSysColor(COLOR_WINDOWTEXT);
+        tbl->cr_hdr_outline =  RGB(0,0,0);
+        tbl->cr_hdr_normal =   GetSysColor(COLOR_WINDOWTEXT);
+        tbl->cr_hdr_s =        GetSysColor(COLOR_WINDOWTEXT);
+        tbl->cr_hdr_gray =     GetSysColor(COLOR_GRAYTEXT);
+        tbl->cr_hdr_gray_s =   GetSysColor(COLOR_HIGHLIGHTTEXT);
+
+        if (khm_main_wnd_mode == KHM_MAIN_WND_MINI) {
+            bg_hdr = bg_normal;
+            tbl->cr_hdr_outline = bg_gray;
+        }
+
+        tbl->hb_normal =      CreateSolidBrush(bg_normal);
+        tbl->hb_grey =        CreateSolidBrush(bg_gray);
+        tbl->hb_s =           CreateSolidBrush(cw_mix_colors(bg_s, bg_normal, SEL_ALPHA));
+
+        tbl->hb_hdr_bg =      CreateSolidBrush(bg_hdr);
+        tbl->hb_hdr_bg_warn = CreateSolidBrush(bg_hdr_warn);
+        tbl->hb_hdr_bg_crit = CreateSolidBrush(bg_hdr_crit);
+        tbl->hb_hdr_bg_exp =  CreateSolidBrush(bg_hdr_exp);
+        tbl->hb_hdr_bg_def =  CreateSolidBrush(bg_hdr_def);
+
+        tbl->hb_hdr_bg_s =      CreateSolidBrush(cw_mix_colors(bg_s, bg_hdr, SEL_ALPHA));
+        tbl->hb_hdr_bg_warn_s = CreateSolidBrush(cw_mix_colors(bg_s, bg_hdr_warn, SEL_ALPHA));
+        tbl->hb_hdr_bg_crit_s = CreateSolidBrush(cw_mix_colors(bg_s, bg_hdr_crit, SEL_ALPHA));
+        tbl->hb_hdr_bg_exp_s =  CreateSolidBrush(cw_mix_colors(bg_s, bg_hdr_exp, SEL_ALPHA));
+        tbl->hb_hdr_bg_def_s =  CreateSolidBrush(cw_mix_colors(bg_s, bg_hdr_def, SEL_ALPHA));
+    }
+
+    tbl->ilist = khui_create_ilist(KHUI_SMICON_CX, KHUI_SMICON_CY-1, 20, 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);
+        ADD_BITMAP(IDB_FLAG_RENEW);
+        ADD_BITMAP(IDB_WDG_STUCK);
+        ADD_BITMAP(IDB_WDG_STUCK_HI);
+
+        ADD_BITMAP(IDB_WDG_STICK);
+        ADD_BITMAP(IDB_WDG_STICK_HI);
+        ADD_BITMAP(IDB_TK_SM);
+
+#undef ADD_BITMAP
+    }
+
+    if (tbl->flags & KHUI_CW_TBL_EXPIDENT) {
+        tbl->hi_lg_ident = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_MAIN_APP),
+                                     IMAGE_ICON,
+                                     GetSystemMetrics(SM_CXICON),
+                                     GetSystemMetrics(SM_CYICON),
+                                     LR_DEFAULTCOLOR);
+    }
+
+    tbl->cursor_row = -1;
+    tbl->scr_left = 0;
+    tbl->scr_top = 0;
+    tbl->ext_height = 0;
+    tbl->ext_width = 0;
+
+    if (reopen_csp) {
+        khc_close_space(hc_v);
+
+        hc_v = NULL;
+
+        khc_open_space(hc_vs, view, 0, &hc_v);
+
+        tbl->csp_view = hc_v;
+    }
+
+_exit:
+    if(hc_cw)
+        khc_close_space(hc_cw);
+    if(hc_vs)
+        khc_close_space(hc_vs);
+    if(hc_cs)
+        khc_close_space(hc_cs);
+    if(clist)
+        PFREE(clist);
+    /* we leave hc_v held, because tbl->csp_view is the same handle.
+       We keep that open until the view is unloaded. */
+}
+
+khui_credwnd_ident *
+cw_find_ident(khui_credwnd_tbl * tbl, khm_handle ident) {
+    khm_size i;
+
+    for (i=0; i < tbl->n_idents; i++) {
+        if (kcdb_identity_is_equal(ident, tbl->idents[i].ident))
+            break;
+    }
+
+    if (i < tbl->n_idents)
+        return &tbl->idents[i];
+    else
+        return NULL;
+}
+
+khm_int32 KHMAPI
+cw_credset_iter_func(khm_handle cred, void * rock) {
+    khui_credwnd_tbl * tbl = (khui_credwnd_tbl *) rock;
+    khm_handle ident = NULL;
+    khm_size i;
+    khui_credwnd_ident * cwi = NULL;
+    khm_int32 cred_credtype = KCDB_CREDTYPE_INVALID;
+    khm_int32 cred_flags = 0;
+
+    kcdb_cred_get_identity(cred, &ident);
+
+    if (ident == NULL)
+        goto _cleanup;
+
+    for (i=0; i < tbl->n_idents; i++) {
+        if (kcdb_identity_is_equal(ident, tbl->idents[i].ident))
+            break;
+    }
+
+    if (i >= tbl->n_idents) {
+        khm_size cb;
+
+        /* need to add this one */
+        if (tbl->n_idents == tbl->nc_idents) {
+            tbl->nc_idents = UBOUNDSS(tbl->n_idents + 1,
+                                      CW_IDENT_ALLOC_INCR,
+                                      CW_IDENT_ALLOC_INCR);
+#ifdef DEBUG
+            assert(tbl->nc_idents > tbl->n_idents);
+#endif
+            tbl->idents = PREALLOC(tbl->idents, sizeof(tbl->idents[0]) * tbl->nc_idents);
+#ifdef DEBUG
+            assert(tbl->idents);
+#endif
+            ZeroMemory(&tbl->idents[tbl->n_idents],
+                       sizeof(tbl->idents[0]) * (tbl->nc_idents - tbl->n_idents));
+        }
+
+        i = tbl->n_idents;
+        cwi = &tbl->idents[tbl->n_idents++];
+
+        ZeroMemory(cwi, sizeof(*cwi));
+
+        cwi->ident = ident;
+        kcdb_identity_hold(ident);
+
+        cb = sizeof(cwi->name);
+        kcdb_identity_get_name(ident, cwi->name, &cb);
+    }
+
+    cwi = &tbl->idents[i];
+
+    /* this is the first time we are seeing this identity. */
+    if (cwi->credcount == 0) {
+        khm_size cb;
+
+        cb = sizeof(cwi->credtype);
+        if (KHM_SUCCEEDED(kcdb_identity_get_attr(ident, KCDB_ATTR_TYPE, NULL,
+                                                 &cwi->credtype, &cb))) {
+            cwi->credtype_name[0] = L'\0';
+
+            cb = sizeof(cwi->credtype_name);
+            if (KHM_FAILED(kcdb_identity_get_attr(ident, KCDB_ATTR_TYPE_NAME, NULL,
+                                                  &cwi->credtype, &cb))) {
+                cb = sizeof(cwi->credtype_name);
+                kcdb_credtype_describe(cwi->credtype, cwi->credtype_name,
+                                       &cb, KCDB_TS_SHORT);
+            }
+        } else {
+            cwi->credtype = KCDB_CREDTYPE_INVALID;
+            cwi->credtype_name[0] = L'\0';
+        }
+
+        cb = sizeof(cwi->ft_expire);
+        if (KHM_FAILED(kcdb_identity_get_attr(ident, KCDB_ATTR_EXPIRE, NULL,
+                                              &cwi->ft_expire, &cb))) {
+            cwi->ft_expire = IntToFt(0);
+        }
+
+        kcdb_identity_get_flags(cwi->ident, &cwi->ident_flags);
+    }
+
+    cwi->credcount++;
+
+    kcdb_cred_get_type(cred, &cred_credtype);
+    if (cred_credtype >= 0 && cred_credtype == cwi->credtype) {
+        cwi->id_credcount++;
+
+        kcdb_cred_get_flags(cred, &cred_flags);
+        if (cred_flags & KCDB_CRED_FLAG_INITIAL) {
+            cwi->init_credcount++;
+        }
+    }
+
+ _cleanup:
+    if (ident)
+        kcdb_identity_release(ident);
+
+    return KHM_ERROR_SUCCESS;
+}
+
+void 
+cw_update_creds(khui_credwnd_tbl * tbl)
+{
+    kcdb_cred_comp_field * fields;
+    kcdb_cred_comp_order comp_order;
+    int 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 = PMALLOC(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< (int) 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);
+        }
+    }
+
+    /* refresh the per-identity information */
+    for (i=0; i < (int) tbl->n_idents; i++) {
+        tbl->idents[i].credcount = 0;
+        tbl->idents[i].id_credcount = 0;
+        tbl->idents[i].init_credcount = 0;
+    }
+
+    kcdb_credset_apply(tbl->credset, cw_credset_iter_func, (void *) tbl);
+
+    if (fields)
+        PFREE(fields);
+}
+
+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)
+        PFREE(o->header);
+
+    if ((o->flags & KHUI_CW_O_DATAALLOC) &&
+        o->data)
+        PFREE(o->data);
+
+    if ((o->flags & KHUI_CW_O_RELIDENT) &&
+        o->data)
+        kcdb_identity_release((khm_handle) o->data);
+
+    LPOP(&(o->children), &c);
+    while(c) {
+        cw_del_outline(c);
+        LPOP(&(o->children), &c);
+    }
+
+    ZeroMemory(o, sizeof(*o));
+    PFREE(o);
+}
+
+khui_credwnd_outline * 
+cw_new_outline_node(wchar_t * heading) {
+    khui_credwnd_outline * o;
+    size_t cblen;
+
+    o = PMALLOC(sizeof(khui_credwnd_outline));
+    ZeroMemory(o, sizeof(khui_credwnd_outline));
+    
+    if(SUCCEEDED(StringCbLength(heading, KHUI_MAXCB_HEADING, &cblen))) {
+        cblen += sizeof(wchar_t);
+        o->header = PMALLOC(cblen);
+        StringCbCopy(o->header, cblen, heading);
+    }
+    o->start = -1;
+
+    return o;
+}
+
+/* buf is a handle to a credential or an identity.  the kcdb_buf_*
+   functions work with either. */
+khm_int32 
+cw_get_buf_exp_flags(khui_credwnd_tbl * tbl, khm_handle buf)
+{
+    khm_int32 flags;
+    long s;
+    FILETIME ft_expire;
+    FILETIME ft_current;
+    FILETIME ft_difference;
+    khm_size cbsize;
+
+    cbsize = sizeof(ft_expire);
+    if(KHM_FAILED(kcdb_buf_get_attr(buf, KCDB_ATTR_EXPIRE, NULL,
+                                    &ft_expire, &cbsize)))
+        return 0;
+
+    GetSystemTimeAsFileTime(&ft_current);
+    ft_difference = FtSub(&ft_expire, &ft_current);
+
+    s = FtIntervalToSeconds(&ft_difference);
+
+    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);
+
+static void 
+cw_update_selection_state(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;
+    int nr;
+    long ms;
+    FILETIME ft;
+    khm_size cbsize;
+    int timer_set = 0;
+
+    KillTimer(hwnd, idEvent);
+
+    tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);
+    r = (khui_credwnd_row *) idEvent;
+    r->flags &= ~KHUI_CW_ROW_TIMERSET;
+
+    nr = (int)(r - tbl->rows);
+
+    if(nr < 0 || nr >= tbl->n_rows)
+        return;
+
+    if(r->flags & KHUI_CW_ROW_CRED) {
+
+        nflags = cw_get_buf_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 rc,rr,ri;
+
+            GetClientRect(tbl->hwnd, &rc);
+            rc.top += tbl->header_height;
+
+            rr = r->r_ext;
+            OffsetRect(&rr, 0, tbl->header_height - tbl->scr_top);
+
+            if(IntersectRect(&ri, &rc, &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);
+                    timer_set = 1;
+                }
+            }
+
+            if (timer_set)
+                r->flags |= KHUI_CW_ROW_TIMERSET;
+        }
+    } else {
+        khui_credwnd_outline * o;
+        khui_credwnd_ident * cwi;
+        FILETIME ft_now;
+
+        o = (khui_credwnd_outline *) r->data;
+#ifdef DEBUG
+        assert(r->flags & KHUI_CW_ROW_EXPVIEW);
+        assert(o->attr_id == KCDB_ATTR_ID);
+        assert(tbl->flags & KHUI_CW_TBL_EXPIDENT);
+#endif
+
+        nflags = cw_get_buf_exp_flags(tbl, (khm_handle) o->data);
+        if ((o->flags & CW_EXPSTATE_MASK) != nflags) {
+            cw_update_outline(tbl);
+            InvalidateRect(tbl->hwnd, NULL, FALSE);
+        } else {
+            RECT rc, rr, ri;
+
+            GetClientRect(tbl->hwnd, &rc);
+            rc.top += tbl->header_height;
+
+            rr = r->r_ext;
+            OffsetRect(&rr, 0, tbl->header_height - tbl->scr_top);
+
+            if (IntersectRect(&ri, &rc, &rr))
+                InvalidateRect(tbl->hwnd, &ri, FALSE);
+
+            cwi = cw_find_ident(tbl, o->data);
+
+            GetSystemTimeAsFileTime(&ft_now);
+            if (CompareFileTime(&cwi->ft_expire, &ft_now) > 0) {
+                ft = FtSub(&cwi->ft_expire, &ft_now);
+                ms = FtIntervalMsToRepChange(&ft);
+                if (ms > 0) {
+                    SetTimer(tbl->hwnd, (UINT_PTR) r, ms + 100, cw_timer_proc);
+                    timer_set = 1;
+                }
+            }
+
+            if (timer_set)
+                r->flags |= KHUI_CW_ROW_TIMERSET;
+        }
+    }
+}
+
+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;
+        int newsize;
+
+        newsize = UBOUNDSS(row+1,KHUI_CW_ROW_INITIAL, KHUI_CW_ROW_INCREMENT);
+        newrows = PMALLOC(sizeof(khui_credwnd_row) * newsize);
+        memcpy(newrows, tbl->rows, sizeof(khui_credwnd_row) * tbl->n_rows);
+        PFREE(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;
+        int newsize;
+
+        newsize = UBOUNDSS(row+1,KHUI_CW_ROW_INITIAL, KHUI_CW_ROW_INCREMENT);
+        newrows = PMALLOC(sizeof(khui_credwnd_row) * newsize);
+        memcpy(newrows, tbl->rows, sizeof(khui_credwnd_row) * tbl->n_rows);
+        PFREE(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;
+
+    /* if we are showing expanded identity information, we need to set
+       a timer so that we can update the identity row when the
+       identity changes. */
+    if ((tbl->flags & KHUI_CW_TBL_EXPIDENT) &&
+        tbl->cols[col].attr_id == KCDB_ATTR_ID_NAME) {
+
+        khui_credwnd_ident * cwi;
+
+        tbl->rows[row].flags |= KHUI_CW_ROW_EXPVIEW;
+
+        cwi = cw_find_ident(tbl, o->data);
+        if (cwi && FtToInt(&cwi->ft_expire) != 0) {
+            FILETIME ft;
+            FILETIME ft_now;
+
+            ft = cwi->ft_expire;
+            GetSystemTimeAsFileTime(&ft_now);
+
+            if (CompareFileTime(&ft, &ft_now) > 0) {
+                long ms;
+
+                ft = FtSub(&ft, &ft_now);
+                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;
+                }
+            }
+        }
+    }
+}
+
+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 = 0;
+    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;
+    khm_int32 expstate = 0;
+
+    /*  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 = PMALLOC(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 = PMALLOC(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 && n_grouping > 0) {
+            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 = PMALLOC(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->length = 0;
+            ol->idx_start = i;
+            ol->idx_end = i;
+            ol->flags &= ~(CW_EXPSTATE_MASK |
+                           KHUI_CW_O_SHOWFLAG |
+                           KHUI_CW_O_STICKY |
+                           KHUI_CW_O_EMPTY);
+
+            /* if the outline node is for an identity, then we have to
+               check the expiration state for the identity. */
+
+            if (ol->attr_id == KCDB_ATTR_ID) {
+                khm_handle ident = (khm_handle) ol->data;
+
+                flags = cw_get_buf_exp_flags(tbl, ident);
+
+                if (flags) {
+                    ol->flags |= flags;
+                    ol->flags |= KHUI_CW_O_SHOWFLAG;
+                   expstate |= flags;
+                } else if (grouping[j] == tbl->n_cols - 1) {
+                    /* if we aren't showing any creds under this
+                       outline level, we should also show any
+                       flags. */
+                    ol->flags |= KHUI_CW_O_SHOWFLAG;
+                }
+            }
+
+            if (grouping[j] == tbl->n_cols - 1) {
+                ol->flags |= KHUI_CW_O_NOOUTLINE;
+            } else {
+                ol->flags &= ~KHUI_CW_O_NOOUTLINE;
+            }
+
+            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 */
+        if (ol)
+            visible = visible && (ol->flags & KHUI_CW_O_EXPAND);
+
+        if(visible && n_grouping > 0 &&
+           grouping[n_grouping - 1] < tbl->n_cols - 1) {
+            khm_int32 c_flags;
+
+            cw_set_tbl_row_cred(tbl, n_rows, thiscred, 
+                                grouping[n_grouping-1]);
+
+            flags = cw_get_buf_exp_flags(tbl, thiscred);
+            if(flags) {
+                tbl->rows[n_rows].flags |= flags;
+            }
+
+            kcdb_cred_get_flags(thiscred, &c_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++;
+        }
+
+        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 default identities with no credentials and 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;
+
+        /* see if the defualt identity is in the list */
+        {
+            khm_handle id_def = NULL;
+            wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
+            khm_size cb;
+            khm_int32 flags;
+
+            if (KHM_FAILED(kcdb_identity_get_default(&id_def))) {
+                goto done_with_defident;
+            }
+
+            kcdb_identity_get_flags(id_def, &flags);
+            cb = sizeof(idname);
+            kcdb_identity_get_name(id_def, idname, &cb);
+
+            for (o = tbl->outline; o; o = LNEXT(o)) {
+                if (!wcscmp(idname, o->header))
+                    break;
+            }
+
+            if (o == NULL) {
+                o = cw_new_outline_node(idname);
+                LPUSH(&tbl->outline, o);
+                o->flags = KHUI_CW_O_VISIBLE | KHUI_CW_O_RELIDENT | KHUI_CW_O_EMPTY;
+                o->level = 0;
+                o->col = grouping[0];
+                o->data = id_def;
+                o->attr_id = KCDB_ATTR_ID;
+                o->start = -1;
+            } else {
+                kcdb_identity_release(id_def);
+            }
+
+            if (o->start != -1)
+                goto done_with_defident;
+
+            if (flags & KCDB_IDENT_FLAG_STICKY)
+                o->flags |= KHUI_CW_O_STICKY;
+            else
+                o->flags &= ~KHUI_CW_O_STICKY;
+
+            o->start = n_rows;
+            o->length = 1;
+            o->idx_start = -1;
+            o->idx_end = -1;
+
+            if (grouping[0] == tbl->n_cols - 1)
+                o->flags |= KHUI_CW_O_NOOUTLINE;
+
+            cw_set_tbl_row_header(tbl, n_rows, grouping[0], o);
+
+            n_rows ++;
+
+        done_with_defident:
+            ;
+        }
+
+        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 = PMALLOC(cb_names);
+        idarray = PMALLOC(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++) {
+            khm_handle h;
+
+            if (KHM_FAILED(kcdb_identity_create(idarray[i], 
+                                                KCDB_IDENT_FLAG_CREATE, &h)))
+                continue;
+
+            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;
+                o->flags &= KHUI_CW_O_RELIDENT;
+                o->flags |= KHUI_CW_O_STICKY | KHUI_CW_O_VISIBLE | KHUI_CW_O_EMPTY;
+
+                if (!kcdb_identity_is_equal(o->data, h)) {
+                    if (o->flags & KHUI_CW_O_RELIDENT)
+                        kcdb_identity_release(o->data);
+                    o->data = h;
+                    o->flags |= KHUI_CW_O_RELIDENT;
+                    kcdb_identity_hold(h);
+                }
+            } else {
+                /* not found.  create */
+                o = cw_new_outline_node(idarray[i]);
+                LPUSH(&tbl->outline, o);
+                o->flags = KHUI_CW_O_STICKY | KHUI_CW_O_VISIBLE | KHUI_CW_O_EMPTY | KHUI_CW_O_RELIDENT;
+                o->level = 0;
+                o->col = grouping[0];
+                o->data = h;
+                kcdb_identity_hold(h);
+                o->attr_id = KCDB_ATTR_ID;
+            }
+
+            if (grouping[0] == tbl->n_cols - 1)
+                o->flags |= KHUI_CW_O_NOOUTLINE;
+
+            kcdb_identity_release(h);
+
+            o->flags &= ~KHUI_CW_O_EXPAND;
+            o->start = n_rows;
+            o->length = 1;
+            o->idx_start = -1;
+            o->idx_end = -1;
+
+            cw_set_tbl_row_header(tbl, n_rows, grouping[0], o);
+
+            n_rows ++;
+        }
+
+    _cleanup_sticky:
+        if (idnames)
+            PFREE(idnames);
+        if (idarray)
+            PFREE(idarray);
+    }
+
+    tbl->n_rows = n_rows;
+    tbl->flags |= KHUI_CW_TBL_ROW_DIRTY;
+
+    tbl->flags &= ~KHUI_CW_TBL_COL_DIRTY;
+
+    if (tbl->cursor_row >= tbl->n_rows)
+        tbl->cursor_row = tbl->n_rows - 1;
+    if (tbl->cursor_row < 0)
+        tbl->cursor_row = 0;
+_exit:
+    if(grouping)
+        PFREE(grouping);
+
+    /* note that the expstate is derived from whether or not 
+     * we have expiration states set for any active identities */
+    if (n_creds == 0)
+        khm_notify_icon_expstate(KHM_NOTIF_EMPTY);
+    else if ((expstate & CW_EXPSTATE_EXPIRED) == CW_EXPSTATE_EXPIRED)
+        khm_notify_icon_expstate(KHM_NOTIF_EXP);
+    else if ((expstate & CW_EXPSTATE_WARN) == CW_EXPSTATE_WARN ||
+             (expstate & CW_EXPSTATE_CRITICAL) == CW_EXPSTATE_CRITICAL)
+        khm_notify_icon_expstate(KHM_NOTIF_WARN);
+    else
+        khm_notify_icon_expstate(KHM_NOTIF_OK);
+}
+
+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_s);
+
+    SafeDeleteObject(tbl->hb_hdr_bg);
+    SafeDeleteObject(tbl->hb_hdr_bg_crit);
+    SafeDeleteObject(tbl->hb_hdr_bg_exp);
+    SafeDeleteObject(tbl->hb_hdr_bg_warn);
+    SafeDeleteObject(tbl->hb_hdr_bg_def);
+
+    SafeDeleteObject(tbl->hb_hdr_bg_s);
+    SafeDeleteObject(tbl->hb_hdr_bg_crit_s);
+    SafeDeleteObject(tbl->hb_hdr_bg_exp_s);
+    SafeDeleteObject(tbl->hb_hdr_bg_warn_s);
+    SafeDeleteObject(tbl->hb_hdr_bg_def_s);
+
+#undef SafeDeleteObject
+
+    if (tbl->hi_lg_ident) {
+        DestroyIcon(tbl->hi_lg_ident);
+        tbl->hi_lg_ident = NULL;
+    }
+
+    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) {
+        int i;
+
+        for(i=0; i < tbl->n_cols; i++) {
+            if(tbl->cols[i].title)
+                PFREE(tbl->cols[i].title);
+            Header_DeleteItem(tbl->hwnd_header, 0);
+
+            if (tbl->cols[i].attr_id >= 0 &&
+                tbl->cols[i].attr_id <= KCDB_ATTR_MAX_ID &&
+                attr_to_action[tbl->cols[i].attr_id]) {
+
+                khui_check_action(attr_to_action[tbl->cols[i].attr_id], FALSE);
+
+            }
+        }
+        PFREE(tbl->cols);
+        tbl->cols = NULL;
+        tbl->n_cols = 0;
+        tbl->n_total_cols = 0;
+
+        kmq_post_message(KMSG_ACT, KMSG_ACT_REFRESH, 0, 0);
+    }
+
+    if(tbl->rows) {
+        PFREE(tbl->rows);
+        tbl->rows = NULL;
+        tbl->n_rows = 0;
+        tbl->n_total_rows = 0;
+    }
+
+    khui_delete_bitmap(&tbl->kbm_logo_shade);
+
+    if (tbl->csp_view) {
+        khc_close_space(tbl->csp_view);
+        tbl->csp_view = NULL;
+    }
+
+    tbl->cell_height = 0;       /* recalculate cell height next time */
+
+    if (tbl->idents) {
+        khm_size i;
+
+        for (i=0; i < tbl->n_idents; i++) {
+            if (tbl->idents[i].ident) {
+                kcdb_identity_release(tbl->idents[i].ident);
+            }
+        }
+
+        PFREE(tbl->idents);
+        tbl->idents = NULL;
+        tbl->n_idents = 0;
+        tbl->nc_idents = 0;
+    }
+}
+
+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 (_WIN32_WINNT >= 0x501)
+    if (IS_COMMCTL6()) {
+        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;
+        }
+    }
+#endif
+    if(col->width < 0) {
+        /*TODO: come up with a better way to handle this case */
+        col->width = 200;
+    }
+    phi->cxy = col->width;
+}
+
+int
+cw_get_cell_height(HDC hdc, HFONT hf) {
+    SIZE size;
+    size_t cbbuf;
+    wchar_t buf[64];
+    HFONT hfold = NULL;
+
+    if (hf)
+        hfold = SelectFont(hdc, hf);
+
+    LoadString(khm_hInstance, IDS_SAMPLE_STRING, buf, sizeof(buf)/sizeof(buf[0]));
+    StringCchLength(buf, sizeof(buf)/sizeof(buf[0]), &cbbuf);
+    GetTextExtentPoint32(hdc, buf, (int) cbbuf, &size);
+
+    if (hf)
+        SelectFont(hdc, hfold);
+
+    return size.cy;
+}
+
+int
+cw_update_header_column_width(khui_credwnd_tbl * tbl, int c) {
+    int idx;
+    HDITEM hi;
+
+#ifdef DEBUG
+    assert(c >= 0 && c < tbl->n_cols);
+#endif
+
+    if (tbl->hwnd_header == NULL)
+        return 0;
+
+    idx = Header_OrderToIndex(tbl->hwnd_header, c);
+    ZeroMemory(&hi, sizeof(hi));
+    hi.mask = HDI_WIDTH;
+    hi.cxy = tbl->cols[c].width;
+    return Header_SetItem(tbl->hwnd_header, idx, &hi);
+}
+
+/* returns a bitmask indicating which measures were changed */
+int 
+cw_update_extents(khui_credwnd_tbl * tbl, 
+                  khm_boolean update_scroll) {
+    int ext_x = 0;
+    int ext_y = 0;
+    int i;
+    int filler_col = -1;
+    int fill_adjusted = 0;
+
+ recompute_columns:
+
+    ext_x = 0;
+    for(i=0; i < (int) tbl->n_cols; i++) {
+        tbl->cols[i].x = ext_x;
+        if (tbl->cols[i].flags & KHUI_CW_COL_FILLER) {
+            if (filler_col == -1)
+                filler_col = i;
+        }
+        ext_x += tbl->cols[i].width;
+    }
+
+    if (filler_col != -1 && !fill_adjusted) {
+        RECT r;
+        int delta;
+
+        GetClientRect(tbl->hwnd, &r);
+
+        /* we decrement the width so that the width data area is
+           strictly less than the width of the client area.  Windows
+           doesn't disable a scrollbar unless the range is strictly
+           less than the page size. */
+        delta = ((r.right - r.left) - 1) - ext_x;
+
+        if (tbl->cols[filler_col].width + delta <= GetSystemMetrics(SM_CXSMICON)) {
+            tbl->cols[filler_col].width = GetSystemMetrics(SM_CXICON);
+        } else {
+            tbl->cols[filler_col].width += delta;
+        }
+
+        cw_update_header_column_width(tbl, filler_col);
+
+        fill_adjusted = 1;
+        goto recompute_columns;
+    }
+
+    if(!tbl->cell_height) {
+        HDC dc;
+        int maxheight = 0;
+        int height;
+
+        dc = GetWindowDC(tbl->hwnd);
+
+        maxheight = cw_get_cell_height(dc, tbl->hf_normal);
+        height = cw_get_cell_height(dc, tbl->hf_bold);
+        if (height > maxheight)
+            maxheight = height;
+        height = cw_get_cell_height(dc, tbl->hf_header);
+        if (height > maxheight)
+            maxheight = height;
+        height = cw_get_cell_height(dc, tbl->hf_bold_header);
+        if (height > maxheight)
+            maxheight = height;
+
+        ReleaseDC(tbl->hwnd, dc);
+
+        tbl->cell_height = height + tbl->vpad * 2;
+    }
+
+    if (tbl->flags & KHUI_CW_TBL_EXPIDENT) {
+        RECT r;
+
+        ext_y = 0;
+        r.left = 0;
+        r.right = ext_x;
+
+        for (i=0; i < (int) tbl->n_rows; i++) {
+            r.top = ext_y;
+            if (tbl->rows[i].flags & KHUI_CW_ROW_EXPVIEW) {
+                ext_y += tbl->cell_height * CW_EXP_ROW_MULT;
+            } else {
+                ext_y += tbl->cell_height;
+            }
+            r.bottom = ext_y;
+            tbl->rows[i].r_ext = r;
+        }
+    } else {
+        RECT r;
+
+        r.left = 0;
+        r.right = ext_x;
+
+        for (i=0; i < (int) tbl->n_rows; i++) {
+            r.top = i * tbl->cell_height;
+            r.bottom = r.top + tbl->cell_height;
+
+            tbl->rows[i].r_ext = r;
+        }
+
+        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;
+
+    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_s;
+        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 {
+        ZeroMemory(&rlogo, sizeof(rlogo));
+        ZeroMemory(&ri, sizeof(ri));
+        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;
+    khm_int32 idf = 0;
+
+    /* each header consists of a couple of widgets 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;
+
+    if (colattr == KCDB_ATTR_ID_NAME) {
+        khm_handle ident = o->data;
+
+        kcdb_identity_get_flags(ident, &idf);
+    }
+
+    selected = o->flags & KHUI_CW_O_SELECTED;
+
+    {
+        HBRUSH hbr;
+
+        if(selected) {
+            if ((o->flags & CW_EXPSTATE_MASK) == CW_EXPSTATE_EXPIRED)
+                hbr = tbl->hb_hdr_bg_exp_s;
+            else if ((o->flags & CW_EXPSTATE_MASK) == CW_EXPSTATE_CRITICAL)
+                hbr = tbl->hb_hdr_bg_crit_s;
+            else if ((o->flags & CW_EXPSTATE_MASK) == CW_EXPSTATE_WARN)
+                hbr = tbl->hb_hdr_bg_warn_s;
+            else if (idf & KCDB_IDENT_FLAG_DEFAULT)
+                hbr = tbl->hb_hdr_bg_def_s;
+            else
+                hbr = tbl->hb_hdr_bg_s;
+        } 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 if (idf & KCDB_IDENT_FLAG_DEFAULT)
+                hbr = tbl->hb_hdr_bg_def;
+            else
+                hbr = tbl->hb_hdr_bg;
+        }
+
+        FillRect(hdc, r, hbr);
+    }
+
+    /* draw the background */
+    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_NOOUTLINE) &&
+        !(o->flags & KHUI_CW_O_EMPTY)) {
+        if((tbl->mouse_state & CW_MOUSE_WOUTLINE) && 
+           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->top + r->bottom - KHUI_SMICON_CY) / 2, 0);
+            } else {
+                khui_ilist_draw_id(tbl->ilist, IDB_WDG_COLLAPSE_HI,
+                                   hdc, r->left,
+                                   (r->top + r->bottom - KHUI_SMICON_CY) / 2, 0);
+            }
+        } else {
+            if(o->flags & KHUI_CW_O_EXPAND) {
+                khui_ilist_draw_id(tbl->ilist, IDB_WDG_EXPAND,
+                                   hdc, r->left,
+                                   (r->top + r->bottom - KHUI_SMICON_CY) / 2, 0);
+            } else {
+                khui_ilist_draw_id(tbl->ilist, IDB_WDG_COLLAPSE,
+                                   hdc, r->left,
+                                   (r->top + r->bottom - KHUI_SMICON_CY) / 2, 0);
+            }
+        }
+
+        r->left += KHUI_SMICON_CX * 3 / 2;
+    } else if (!(o->flags & KHUI_CW_O_NOOUTLINE)) {
+        r->left += KHUI_SMICON_CX * 3 / 2;
+    }
+
+    /* try to draw the icon, if there is one */
+    if(colattr == KCDB_ATTR_ID_NAME) {
+
+        khui_ilist_draw_id(tbl->ilist,
+                           (((tbl->mouse_state & CW_MOUSE_WSTICKY) &&
+                             tbl->mouse_row == row)?
+                            ((idf & KCDB_IDENT_FLAG_STICKY)?
+                             IDB_WDG_STUCK_HI:
+                             IDB_WDG_STICK_HI):
+                            ((idf & KCDB_IDENT_FLAG_STICKY)?
+                             IDB_WDG_STUCK:
+                             IDB_WDG_STICK)),
+                           hdc,
+                           r->left,
+                           (r->top + r->bottom - KHUI_SMICON_CY) / 2, 0);
+
+        r->left += KHUI_SMICON_CX * 3 / 2;
+
+        /* the TRUE part of the 'if' is for drawing large icons.  It's
+           disabled for now until we have new icons. */
+        if ((cr->flags & KHUI_CW_ROW_EXPVIEW) && FALSE) {
+            int cx = GetSystemMetrics(SM_CXICON);
+            int cy = GetSystemMetrics(SM_CYICON);
+
+            DrawIcon(hdc, r->left, (r->top + r->bottom - cy) / 2, tbl->hi_lg_ident);
+
+            r->left += cx + KHUI_SMICON_CX / 2;
+
+        } else {
+            khui_ilist_draw_id(tbl->ilist, 
+                               ((o->flags & KHUI_CW_O_EMPTY)?
+                                IDB_ID_DIS_SM:
+                                IDB_ID_SM), 
+                               hdc,
+                               r->left,
+                               (r->top + r->bottom - KHUI_SMICON_CY) / 2, 0);
+            r->left += KHUI_SMICON_CX * 3 / 2 ;
+        }
+    }
+
+
+    if (!(cr->flags & KHUI_CW_ROW_EXPVIEW)) {
+
+        SetTextAlign(hdc, TA_BOTTOM | TA_LEFT);
+
+        if(selected)
+            SetTextColor(hdc, tbl->cr_hdr_s);
+        else
+            SetTextColor(hdc, tbl->cr_hdr_normal);
+
+        TextOut(hdc, r->left, r->bottom - tbl->vpad, o->header, (int) wcslen(o->header));
+
+        if (colattr == KCDB_ATTR_ID_NAME &&
+            (idf & KCDB_IDENT_FLAG_DEFAULT)) {
+            wchar_t defstr[64];
+            SIZE size;
+
+            LoadString(khm_hInstance, IDS_CW_DEFAULT,
+                       defstr, ARRAYLENGTH(defstr));
+
+            GetTextExtentPoint32(hdc, o->header, (int) wcslen(o->header),
+                                 &size);
+
+            r->left += size.cx + KHUI_SMICON_CX * 2;
+
+            TextOut(hdc, r->left, r->bottom - tbl->vpad, 
+                    defstr, (int) wcslen(defstr));
+        }
+    } else {
+
+        RECT tr;
+        int len;
+        wchar_t typestr[128];
+        int cx_id;
+        SIZE size;
+        khui_credwnd_ident * cwi;
+
+        /* expanded view */
+#ifdef DEBUG
+        assert(colattr == KCDB_ATTR_ID_NAME);
+#endif
+
+        cwi = cw_find_ident(tbl, o->data);
+
+        CopyRect(&tr, r);
+        tr.bottom -= (tr.bottom - tr.top) / 2; /* drawing two lines of text */
+
+        if (selected)
+            SetTextColor(hdc, tbl->cr_hdr_s);
+        else
+            SetTextColor(hdc, tbl->cr_hdr_normal);
+
+        len = (int) wcslen(o->header);
+        DrawText(hdc, o->header, len, &tr, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
+        GetTextExtentPoint32(hdc, o->header, (int) len, &size);
+        cx_id = size.cx;
+
+        typestr[0] = L'\0';
+
+        if ((idf & KCDB_IDENT_FLAG_DEFAULT)) {
+            if (cwi && cwi->credtype_name[0]) {
+                wchar_t fmt[64];
+
+                LoadString(khm_hInstance, IDS_CW_DEFAULTTF,
+                           fmt, ARRAYLENGTH(fmt));
+                StringCbPrintf(typestr, sizeof(typestr), fmt,
+                               cwi->credtype_name);
+            } else {
+                LoadString(khm_hInstance, IDS_CW_DEFAULT,
+                           typestr, ARRAYLENGTH(typestr));
+            }
+        } else if (cwi && cwi->credtype_name[0]) {
+            wchar_t fmt[64];
+
+            LoadString(khm_hInstance, IDS_CW_TYPEF,
+                       fmt, ARRAYLENGTH(fmt));
+            StringCbPrintf(typestr, sizeof(typestr), fmt,
+                           cwi->credtype_name);
+        }
+
+        if (typestr[0]) {
+            int cx_str;
+
+            len = (int) wcslen(typestr);
+            GetTextExtentPoint32(hdc, typestr, (int) len, &size);
+            cx_str = size.cx + KHUI_SMICON_CX / 2;
+
+            tr.left = max(tr.right - cx_str, tr.left + cx_id + KHUI_SMICON_CX * 2);
+            if (selected)
+                SetTextColor(hdc, tbl->cr_hdr_gray_s);
+            else
+                SetTextColor(hdc, tbl->cr_hdr_gray);
+            DrawText(hdc, typestr, len, &tr, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
+        }
+
+        CopyRect(&tr, r);
+        tr.top += (tr.bottom - tr.top) / 2;
+
+        if (1) {
+            wchar_t buf[128];
+            khui_credwnd_ident * cwi;
+
+            buf[0] = L'\0';
+            cwi = cw_find_ident(tbl, o->data);
+
+            if (cwi) {
+#ifdef SHOW_CREDENTIAL_COUNTS
+                if (cwi->credcount == 0)
+                    LoadString(khm_hInstance, IDS_IDEXPDISP_NOCRED,
+                               buf, ARRAYLENGTH(buf));
+                else if (cwi->credcount == 1)
+                    LoadString(khm_hInstance, IDS_IDEXPDISP_1CRED,
+                               buf, ARRAYLENGTH(buf));
+                else {
+                    wchar_t fmt[128];
+                    LoadString(khm_hInstance, IDS_IDEXPDISP_NCRED,
+                               fmt, ARRAYLENGTH(fmt));
+                    StringCbPrintf(buf, sizeof(buf), fmt, (int) cwi->credcount);
+                }
+#else
+                if (FtToInt(&cwi->ft_expire) != 0) {
+                    FILETIME ft_now;
+
+                    GetSystemTimeAsFileTime(&ft_now);
+                    if (CompareFileTime(&cwi->ft_expire, &ft_now) > 0) {
+                        wchar_t fmt[64];
+                        wchar_t intstr[128];
+                        FILETIME ft;
+                        khm_size cb;
+
+                        ft = FtSub(&cwi->ft_expire, &ft_now);
+                        intstr[0] = L'\0';
+                        cb = sizeof(intstr);
+                        FtIntervalToString(&ft, intstr, &cb);
+
+                        LoadString(khm_hInstance, IDS_CW_EXPIREF,
+                                   fmt, ARRAYLENGTH(fmt));
+                        StringCbPrintf(buf, sizeof(buf), fmt, intstr);
+                    } else {
+                        LoadString(khm_hInstance, IDS_CW_EXPIRED,
+                                   buf, ARRAYLENGTH(buf));
+                    }
+                }
+#endif
+
+                len = (int) wcslen(buf);
+
+                if (selected)
+                    SetTextColor(hdc, tbl->cr_hdr_gray_s);
+                else
+                    SetTextColor(hdc, tbl->cr_hdr_gray);
+                DrawText(hdc, buf, len, &tr, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
+            }
+        }
+    }
+}
+
+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:
+        {
+            ZeroMemory(&hi, sizeof(hi));
+            hi.mask = HDI_ORDER;
+            Header_GetItem(tbl->hwnd_header, ph->iItem, &hi);
+
+            if(tbl->cols[hi.iOrder].flags & KHUI_CW_COL_FIXED_WIDTH)
+                return TRUE;
+            else
+                return FALSE;
+        }
+
+    case HDN_TRACK:
+        return FALSE;
+
+    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 HDN_BEGINDRAG:
+        {
+
+            ZeroMemory(&hi, sizeof(hi));
+            hi.mask = HDI_ORDER;
+            Header_GetItem(tbl->hwnd_header, ph->iItem, &hi);
+
+            if (tbl->cols[hi.iOrder].flags & KHUI_CW_COL_FIXED_POS) {
+                return TRUE;
+            } else {
+                return FALSE;
+            }
+        }
+        break;
+
+    case HDN_ENDDRAG:
+        {
+            int drag_start_index;
+            int drag_end_index;
+            int i;
+            khui_credwnd_col tcol;
+            int sort_index = 0;
+            khm_int32 old_flags;
+
+            if (ph->pitem == NULL)
+                return TRUE;
+
+            hi.mask = HDI_ORDER;
+            Header_GetItem(tbl->hwnd_header, ph->iItem, &hi);
+            drag_start_index = hi.iOrder;
+            drag_end_index = ph->pitem->iOrder;
+
+            /* the user dragged the column which was at drag_start_index
+               to drag_end_index. */
+
+            if (drag_end_index == drag_start_index)
+                return TRUE;
+
+            /* we don't allow dragging in to the "fixed" area. */
+            for (i=0; i < tbl->n_cols; i++) {
+                if (!(tbl->cols[i].flags & KHUI_CW_COL_FIXED_POS))
+                    break;
+            }
+
+            if (drag_end_index <= i)
+                return TRUE;
+            tcol = tbl->cols[drag_start_index];
+            if (drag_end_index < drag_start_index) {
+                MoveMemory(&tbl->cols[drag_end_index + 1],
+                           &tbl->cols[drag_end_index],
+                           sizeof(tbl->cols[0]) *
+                           (drag_start_index - drag_end_index));
+            } else {
+                MoveMemory(&tbl->cols[drag_start_index],
+                           &tbl->cols[drag_start_index + 1],
+                           sizeof(tbl->cols[0]) *
+                           (drag_end_index - drag_start_index));
+            }
+            tbl->cols[drag_end_index] = tcol;
+
+            old_flags = tbl->cols[drag_end_index].flags;
+
+            if (drag_end_index < tbl->n_cols - 1) {
+                khm_int32 tflags = tbl->cols[drag_end_index + 1].flags;
+
+                if (tflags & KHUI_CW_COL_GROUP) {
+                    tbl->cols[drag_end_index].flags |= KHUI_CW_COL_GROUP;
+                }
+
+                if ((tflags & (KHUI_CW_COL_SORT_INC | KHUI_CW_COL_SORT_DEC)) &&
+                    !(old_flags & (KHUI_CW_COL_SORT_INC | KHUI_CW_COL_SORT_DEC)))
+                    tbl->cols[drag_end_index].flags |= KHUI_CW_COL_SORT_INC;
+            }
+
+            if (drag_end_index > 0) {
+                khm_int32 tflags = tbl->cols[drag_end_index - 1].flags;
+
+                if (!(tflags & KHUI_CW_COL_GROUP))
+                    tbl->cols[drag_end_index].flags &= ~KHUI_CW_COL_GROUP;
+
+                if (!(tflags & (KHUI_CW_COL_SORT_INC | KHUI_CW_COL_SORT_DEC)))
+                    tbl->cols[drag_end_index].flags &=
+                        ~(KHUI_CW_COL_SORT_INC | KHUI_CW_COL_SORT_DEC);
+            }
+
+            if (old_flags != tbl->cols[drag_end_index].flags) {
+                cw_hditem_from_tbl_col(&tbl->cols[drag_end_index], &hi);
+                hi.mask = HDI_FORMAT;
+                Header_SetItem(tbl->hwnd_header, ph->iItem, &hi);
+            }
+
+            if ((old_flags ^ tbl->cols[drag_end_index].flags) &
+                KHUI_CW_COL_GROUP)
+                tbl->flags |= KHUI_CW_TBL_COL_DIRTY;
+
+            for (i=0; i < tbl->n_cols; i++) {
+                if (tbl->cols[i].attr_id < 0)
+                    continue;
+
+                if (tbl->cols[i].flags &
+                    (KHUI_CW_COL_GROUP |
+                     KHUI_CW_COL_SORT_INC |
+                     KHUI_CW_COL_SORT_DEC))
+                    tbl->cols[i].sort_index = sort_index++;
+                else
+                    break;
+            }
+
+            tbl->flags |= KHUI_CW_TBL_CUSTVIEW;
+
+            cw_update_creds(tbl);
+            cw_update_outline(tbl);
+            cw_update_extents(tbl, TRUE);
+
+            InvalidateRect(tbl->hwnd, NULL, FALSE);
+
+            return FALSE;
+        }
+        break;
+
+    case HDN_ITEMCLICK:
+        {
+            int idx;
+            int hidx;
+
+            hi.mask = HDI_ORDER;
+            Header_GetItem(tbl->hwnd_header, ph->iItem, &hi);
+            idx = hi.iOrder;
+
+            if (idx < 0 || idx >= tbl->n_cols)
+                return FALSE;
+
+            if (tbl->cols[idx].flags & KHUI_CW_COL_META)
+                return FALSE;
+
+            if (tbl->cols[idx].flags &
+                (KHUI_CW_COL_SORT_INC | KHUI_CW_COL_SORT_DEC)) {
+
+                tbl->cols[idx].flags ^=
+                    (KHUI_CW_COL_SORT_INC | KHUI_CW_COL_SORT_DEC);
+
+                cw_hditem_from_tbl_col(&tbl->cols[idx], &hi);
+                hi.mask = HDI_FORMAT;
+                Header_SetItem(tbl->hwnd_header, ph->iItem, &hi);
+
+            } else {
+                int i;
+                int sort_idx = 0;
+
+                for (i=0; i <= idx; i++) {
+                    if (tbl->cols[i].attr_id < 0)
+                        continue;
+
+                    if (!(tbl->flags &
+                          (KHUI_CW_COL_SORT_INC | KHUI_CW_COL_SORT_DEC))) {
+                        tbl->cols[i].flags |= KHUI_CW_COL_SORT_INC;
+
+                        cw_hditem_from_tbl_col(&tbl->cols[i], &hi);
+                        hi.mask = HDI_FORMAT;
+                        hidx = Header_OrderToIndex(tbl->hwnd_header, i);
+                        Header_SetItem(tbl->hwnd_header, hidx, &hi);
+                    }
+
+                    tbl->cols[i].sort_index = sort_idx++;
+                }
+            }
+
+            tbl->flags |= KHUI_CW_TBL_CUSTVIEW;
+
+            cw_update_creds(tbl);
+            cw_update_outline(tbl);
+            cw_update_extents(tbl, TRUE);
+
+            InvalidateRect(tbl->hwnd, NULL, FALSE);
+
+        }
+        break;
+
+    case HDN_ITEMDBLCLICK:
+        {
+            int idx;
+            int hidx;
+
+            hi.mask = HDI_ORDER;
+            Header_GetItem(tbl->hwnd_header, ph->iItem, &hi);
+            idx = hi.iOrder;
+
+            if (idx == 0 || idx >= tbl->n_cols)
+                return FALSE;
+
+            if (tbl->cols[idx].flags & KHUI_CW_COL_GROUP) {
+                /* we are removing grouping from this level */
+
+                int i;
+
+                for (i=idx; i < tbl->n_cols; i++) {
+                    if (!(tbl->cols[i].flags & KHUI_CW_COL_GROUP))
+                        break;
+
+                    tbl->cols[i].flags &= ~KHUI_CW_COL_GROUP;
+
+                    cw_hditem_from_tbl_col(&tbl->cols[idx], &hi);
+                    hi.mask = HDI_FORMAT;
+                    hidx = Header_OrderToIndex(tbl->hwnd_header, i);
+                    Header_SetItem(tbl->hwnd_header, hidx, &hi);
+                }
+
+#if 0
+            } else if (tbl->cols[idx].flags &
+                       (KHUI_CW_COL_SORT_INC |
+                        KHUI_CW_COL_SORT_DEC)) {
+                int i;
+
+                /* remove the sort condition from a column */
+
+                for (i=idx; i < tbl->n_cols; i++) {
+                    if (!tbl->cols[i].flags &
+                        (KHUI_CW_COL_SORT_INC |
+                         KHUI_CW_COL_SORT_DEC))
+                        break;
+
+                    tbl->cols[i].flags &=
+                        ~(KHUI_CW_COL_SORT_INC |
+                          KHUI_CW_COL_SORT_DEC);
+
+                    cw_hditem_from_tbl_col(&tbl->cols[idx], &hi);
+                    hi.mask = HDI_FORMAT;
+                    hidx = Header_OrderToIndex(tbl->hwnd_header, i);
+                    Header_SetItem(tbl->hwnd_header, hidx, &hi);
+                }
+#endif
+            } else {
+                int i;
+                int sort_index = 0;
+
+                for (i=0; i <= idx; i++) {
+                    if (tbl->cols[i].attr_id < 0)
+                        continue;
+
+                    if (!(tbl->cols[i].flags & KHUI_CW_COL_GROUP)) {
+                        tbl->cols[i].flags |= KHUI_CW_COL_GROUP;
+
+                        if (!(tbl->cols[i].flags &
+                              (KHUI_CW_COL_SORT_INC |
+                               KHUI_CW_COL_SORT_DEC)))
+                            tbl->cols[i].flags |= KHUI_CW_COL_SORT_INC;
+
+                        cw_hditem_from_tbl_col(&tbl->cols[i], &hi);
+                        hi.mask = HDI_FORMAT;
+                        hidx = Header_OrderToIndex(tbl->hwnd_header, i);
+                        Header_SetItem(tbl->hwnd_header, hidx, &hi);
+                    }
+
+                    tbl->cols[i].sort_index = sort_index++;
+                }
+            }
+
+            tbl->flags |= KHUI_CW_TBL_COL_DIRTY;
+            tbl->flags |= KHUI_CW_TBL_CUSTVIEW;
+
+            cw_update_creds(tbl);
+            cw_update_outline(tbl);
+            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);
+    kmq_subscribe_hwnd(KMSG_KCDB, hwnd);
+    kmq_subscribe_hwnd(KMSG_KMM, hwnd);
+
+    /* freed in cw_wm_destroy  */
+    tbl = PMALLOC(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)
+
+    cw_refresh_attribs(hwnd);
+
+    tbl->hwnd_header = CreateWindowEx(
+        0,
+        WC_HEADER,
+        (LPWSTR) NULL,
+        WS_CHILD | HDS_BUTTONS |
+        HDS_FULLDRAG | HDS_HORZ | HDS_HOTTRACK |
+        HDS_DRAGDROP
+#if (_WIN32_WINNT >= 0x501)
+        | ((IS_COMMCTL6())?HDS_FLAT:0)
+#endif
+        ,
+        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_selection_state(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);
+    kmq_unsubscribe_hwnd(KMSG_KCDB, hwnd);
+    kmq_unsubscribe_hwnd(KMSG_KMM, hwnd);
+
+    tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);
+
+    cw_save_view(tbl, NULL);
+
+    cw_unload_view(tbl);
+
+    PFREE(tbl);
+    return DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+
+/* handles WM_PAINT and WM_PRINTCLIENT */
+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 = NULL;
+    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;
+    int rowheight = 0;
+    BOOL has_dc = FALSE;
+
+    tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);
+
+    if (wParam != 0) {
+        /* we assume that if wParam != 0, then that contains a device
+           context for us to draw in.  Otherwise, we have to call
+           BeginPaint() to get one. */
+        hdc = (HDC) wParam;
+        has_dc = TRUE;
+    }
+
+    if(!has_dc && !GetUpdateRect(hwnd, &r, FALSE)) {
+#ifdef DEBUG
+        assert(FALSE);
+#endif
+        goto _exit;
+    }
+
+    if (!has_dc)
+        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 = 0;
+        ys = 0;
+        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;
+            rowheight = (tbl->rows[i].flags & KHUI_CW_ROW_EXPVIEW)? tbl->cell_height * CW_EXP_ROW_MULT : tbl->cell_height;
+
+            if(tbl->cursor_row == i) {
+                if (tbl->rows[i].flags & KHUI_CW_ROW_HEADER)
+                    SelectFont(hdc, tbl->hf_bold_header);
+                else
+                    SelectFont(hdc, tbl->hf_bold);
+            } else if (tbl->rows[i].flags & KHUI_CW_ROW_HEADER) {
+                SelectFont(hdc, tbl->hf_header);
+            }
+
+            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 + rowheight;
+                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_s);
+            else
+                SetTextColor(hdc, tbl->cr_normal);
+
+            x = xs;
+            rh.top = y;
+            rh.bottom = y + rowheight;
+            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(!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);
+                        }
+                    }
+                } 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 if(!(tbl->rows[i].flags & KHUI_CW_ROW_HEADER)) {
+                            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_FLAG_RENEW,
+                                                   hdc,
+                                                   x, y, 0);
+                            } else {
+                                khui_ilist_draw_id(tbl->ilist,
+                                                   IDB_TK_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);
+            }
+
+            if (tbl->cursor_row == i ||
+                (tbl->rows[i].flags & KHUI_CW_ROW_HEADER)) {
+                SelectFont(hdc, tbl->hf_normal);
+            }
+
+            y += rowheight;
+
+        }
+
+        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,tbl->cell_height * 4,
+                0,              /* This can be WS_EX_TRANSPARENT, but
+                                   we don't fully support it yet. */
+                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);
+
+    if (!has_dc)
+        EndPaint(hwnd,&ps);
+
+ _exit:
+    return TRUE;
+}
+
+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,
+            tbl->cell_height * 4,
+            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) {
+        switch (m->subtype) {
+        case KMSG_CRED_ROOTDELTA:
+            cw_update_creds(tbl);
+            cw_update_outline(tbl);
+            cw_update_extents(tbl, TRUE);
+            InvalidateRect(hwnd, NULL, FALSE);
+            break;
+
+        case KMSG_CRED_PP_BEGIN:
+            cw_pp_begin((khui_property_sheet *) m->vparam);
+            break;
+
+        case KMSG_CRED_PP_PRECREATE:
+            cw_pp_precreate((khui_property_sheet *) m->vparam);
+            break;
+
+        case KMSG_CRED_PP_END:
+            cw_pp_end((khui_property_sheet *) m->vparam);
+            break;
+
+        case KMSG_CRED_PP_DESTROY:
+            cw_pp_destroy((khui_property_sheet *) m->vparam);
+            break;
+        }
+    } else if (m->type == KMSG_KCDB) {
+        if (m->subtype == KMSG_KCDB_IDENT &&
+            m->uparam == KCDB_OP_MODIFY) {
+
+            cw_update_outline(tbl);
+            cw_update_extents(tbl, TRUE);
+            InvalidateRect(hwnd, NULL, FALSE);
+
+        }
+        else if (m->subtype == KMSG_KCDB_IDENT && 
+                 m->uparam == KCDB_OP_NEW_DEFAULT) {
+
+            cw_update_outline(tbl);
+            cw_update_extents(tbl, TRUE);
+            InvalidateRect(hwnd, NULL, FALSE);
+
+        }
+        else if (m->subtype == KMSG_KCDB_ATTRIB &&
+                 (m->uparam == KCDB_OP_INSERT ||
+                  m->uparam == KCDB_OP_DELETE)) {
+
+            cw_refresh_attribs(hwnd);
+
+        }
+    } else if (m->type == KMSG_KMM &&
+               m->subtype == KMSG_KMM_I_DONE) {
+
+        if (tbl->flags & KHUI_CW_TBL_COLSKIP) {
+            wchar_t cname[KCONF_MAXCCH_NAME];
+            khm_size cb;
+
+            cname[0] = L'\0';
+
+            if (tbl->csp_view) {
+                cb = sizeof(cname);
+                khc_get_config_space_name(tbl->csp_view,
+                                          cname,
+                                          &cb);
+            }
+
+            cw_unload_view(tbl);
+
+            cw_load_view(tbl, ((cname[0])?cname: NULL), hwnd);
+            cw_insert_header_cols(tbl);
+
+            cw_update_creds(tbl);
+            cw_update_outline(tbl);
+            cw_update_selection_state(tbl);
+            cw_update_extents(tbl, TRUE);
+
+            InvalidateRect(tbl->hwnd, NULL, TRUE);
+        }
+
+    } else if (m->type == KMSG_ACT &&
+               m->subtype == KMSG_ACT_ACTIVATE) {
+        /* a column selector menu item was activated */
+
+        khm_int32 attr_id;
+        khm_int32 action;
+        khui_action * paction;
+        int i;
+        int first_non_fixed = -1;
+
+        action = m->uparam;
+        paction = khui_find_action(action);
+
+        if (paction == NULL)
+            goto _skip_action;
+
+        attr_id = (khm_int32)(INT_PTR) paction->data;
+
+        if (attr_id < 0 || attr_id > KCDB_ATTR_MAX_ID)
+            goto _skip_action;
+
+        for (i=0; i < tbl->n_cols; i++) {
+            if (tbl->cols[i].attr_id >= 0 &&
+                first_non_fixed == -1)
+                first_non_fixed = i;
+
+            if (tbl->cols[i].attr_id == attr_id)
+                break;
+        }
+
+        if (first_non_fixed == i &&
+            i == tbl->n_cols - 1) {
+            /* this is the only non-fixed column.  We don't allow
+               deleting it, althoguh there's nothing wrong with doing
+               so other than not being very useful. */
+            goto _skip_action;
+        }
+
+        if (i < tbl->n_cols) {
+            khm_int32 sort_index;
+
+            /* we need to remove a column */
+
+            Header_DeleteItem(tbl->hwnd_header, i);
+            sort_index = tbl->cols[i].sort_index;
+
+            if (tbl->cols[i].title)
+                PFREE(tbl->cols[i].title);
+            tbl->cols[i].title = NULL;
+
+            if (i < tbl->n_cols - 1) {
+                MoveMemory(&tbl->cols[i], &tbl->cols[i+1],
+                           sizeof(tbl->cols[0]) * (tbl->n_cols - (i + 1)));
+            }
+            tbl->n_cols--;
+
+            /* fix the sort index */
+            if (sort_index >= 0) {
+                for (i=0; i < tbl->n_cols; i++) {
+                    if (tbl->cols[i].sort_index > sort_index)
+                        tbl->cols[i].sort_index--;
+                }
+            }
+
+            tbl->flags |= KHUI_CW_TBL_COL_DIRTY;
+
+            cw_update_creds(tbl);
+            cw_update_outline(tbl);
+            cw_update_extents(tbl, TRUE);
+
+            InvalidateRect(tbl->hwnd, NULL, TRUE);
+
+            khui_check_action(attr_to_action[attr_id], FALSE);
+
+            tbl->flags |= KHUI_CW_TBL_CUSTVIEW;
+
+        } else {
+            /* we need to add a column */
+            wchar_t buf[KCDB_MAXCCH_SHORT_DESC];
+            khm_size cb;
+            khm_int32 idx = tbl->n_cols;
+            HDITEM hi;
+
+            /* for now, we only allow KHUI_CW_COL_INITIAL columns */
+            if (tbl->n_rows == tbl->n_total_rows)
+                goto _skip_action;
+
+            cb = sizeof(buf);
+            if (KHM_FAILED(kcdb_attrib_describe(attr_id,
+                                                buf,
+                                                &cb,
+                                                KCDB_TS_SHORT)))
+                goto _skip_action;
+
+            tbl->cols[idx].attr_id = attr_id;
+            tbl->cols[idx].width = 100;
+            tbl->cols[idx].x = -1;
+            tbl->cols[idx].flags = 0;
+            tbl->cols[idx].sort_index = -1;
+            tbl->cols[idx].title = PMALLOC(cb);
+#ifdef DEBUG
+            assert(tbl->cols[idx].title);
+#endif
+            if (!tbl->cols[idx].title)
+                goto _skip_action;
+
+            StringCbCopy(tbl->cols[idx].title,
+                         cb,
+                         buf);
+
+            tbl->n_cols++;
+
+            cw_hditem_from_tbl_col(&(tbl->cols[idx]), &hi);
+            Header_InsertItem(tbl->hwnd_header, 512, &hi);
+
+            tbl->flags |= KHUI_CW_TBL_COL_DIRTY;
+
+            cw_update_creds(tbl);
+            cw_update_outline(tbl);
+            cw_update_extents(tbl, TRUE);
+
+            InvalidateRect(tbl->hwnd, NULL, TRUE);
+
+            khui_check_action(attr_to_action[attr_id], TRUE);
+
+            tbl->flags |= KHUI_CW_TBL_CUSTVIEW;
+        }
+
+        kmq_post_message(KMSG_ACT, KMSG_ACT_REFRESH, 0, 0);
+
+    _skip_action:
+        ;
+    }
+
+    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_select_row_creds(khui_credwnd_tbl * tbl, int row, int selected) {
+
+    khm_size j;
+    khm_size idx_start, idx_end;
+
+#ifdef DEBUG
+    assert(row >= 0 && row < tbl->n_rows);
+#endif
+
+    if (row >= 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;
+        if (o->col == tbl->n_cols - 1) {
+            /* this is a special case where the outline column is the
+               last displayed column.  In this case, the credentials
+               do not occupy any rows, and this header row acts as a
+               group credential row. */
+            idx_start = o->idx_start;
+            idx_end = o->idx_end;
+        } else {
+            return;
+        }
+    } else {
+        idx_start = tbl->rows[row].idx_start;
+        idx_end = tbl->rows[row].idx_end;
+    }
+
+    if (idx_start == -1 || idx_end == -1)
+        return;
+
+    for (j = idx_start; j <= idx_end; j++) {
+        khm_handle cred = NULL;
+
+        kcdb_credset_get_cred(tbl->credset, (khm_int32) j, &cred);
+
+        if (cred) {
+            kcdb_cred_set_flags(cred, ((selected)?KCDB_CRED_FLAG_SELECTED:0),
+                                KCDB_CRED_FLAG_SELECTED);
+            kcdb_cred_release(cred);
+        }
+    }
+}
+
+static void 
+cw_unselect_all(khui_credwnd_tbl * tbl)
+{
+    int i;
+
+    for(i=0; i<tbl->n_rows; i++) {
+        tbl->rows[i].flags &= ~KHUI_CW_ROW_SELECTED;
+
+        cw_select_row_creds(tbl, i, FALSE);
+    }
+
+    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)
+{
+    int 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 (row < 0 || row >= (int) tbl->n_rows) {
+        if (tbl->n_rows > 0)
+            row = 0;
+        else {
+            khui_context_reset();
+            return;
+        }
+    }
+
+    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) {
+                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 {
+                    /* we can't narrow it down using the standard set
+                       of scopes.  We consider this to be an identity
+                       selection because the user right-clicked on an
+                       identity header. */
+                    khui_context_set(KHUI_SCOPE_IDENT,
+                                     (khm_handle) o->data,
+                                     KCDB_CREDTYPE_INVALID,
+                                     NULL,
+                                     NULL,
+                                     0,
+                                     tbl->credset);
+                }
+            } else {
+                /* The user clicked on an identity header.  Even
+                   though not all credentials belonging to the
+                   identity maybe within the scope right now, we still
+                   consider this to be an identity scope. */
+                khui_context_set(KHUI_SCOPE_IDENT,
+                                 (khm_handle) o->data,
+                                 KCDB_CREDTYPE_INVALID,
+                                 NULL,
+                                 NULL,
+                                 0,
+                                 tbl->credset);
+            }
+        } 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) {
+                    /* credtype under an identity.  Even though not
+                       all the credentials of this credtype belonging
+                       to this identity might be within the scope, we
+                       still consider this to be a type selection
+                       under a specific 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_all(khui_credwnd_tbl * tbl)
+{
+    int i;
+
+    for(i=0; i<tbl->n_rows; i++) {
+        tbl->rows[i].flags |= KHUI_CW_ROW_SELECTED;
+        cw_select_row_creds(tbl, i, TRUE);
+    }
+
+    cw_select_outline_level(tbl->outline, TRUE);
+
+    cw_update_selection_state(tbl);
+
+    cw_set_row_context(tbl, tbl->cursor_row);
+
+    InvalidateRect(tbl->hwnd, NULL, FALSE);
+}
+
+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;
+            cw_select_row_creds(tbl, i, TRUE);
+        }
+    } 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;
+
+            cw_select_row_creds(tbl, i, select);
+        }
+    } 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;
+
+            cw_select_row_creds(tbl, i, TRUE);
+        }
+
+        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 = -1;
+
+    for (i=0; i < tbl->n_rows; i++) {
+        if (y >= tbl->rows[i].r_ext.top &&
+            y < tbl->rows[i].r_ext.bottom) {
+            row = i;
+            break;
+        }
+    }
+
+    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) {
+            khui_credwnd_outline * o;
+
+            o = (khui_credwnd_outline *) tbl->rows[row].data;
+
+            /* are we on a widget then? */
+            x -= tbl->cols[o->col].x;
+
+            if (!(o->flags & KHUI_CW_O_NOOUTLINE)) {
+                if(x >= 0 && x < KHUI_SMICON_CX) /* hit */ {
+                    nm_state |= CW_MOUSE_WOUTLINE | CW_MOUSE_WIDGET;
+                } else if (tbl->cols[tbl->rows[row].col].attr_id == 
+                           KCDB_ATTR_ID_NAME &&
+                           col == tbl->rows[row].col &&
+                           x >= KHUI_SMICON_CX * 3 / 2 &&
+                           x < KHUI_SMICON_CX * 5 / 2){
+                    nm_state |= CW_MOUSE_WSTICKY | CW_MOUSE_WIDGET;
+                } else if (tbl->cols[tbl->rows[row].col].attr_id ==
+                           KCDB_ATTR_ID_NAME &&
+                           col == tbl->rows[row].col &&
+                           x >= KHUI_SMICON_CX * 3 &&
+                           x < KHUI_SMICON_CX * 4) {
+                    nm_state |= CW_MOUSE_WICON | CW_MOUSE_WIDGET;
+                }
+            } else if (tbl->cols[o->col].attr_id == KCDB_ATTR_ID_NAME) {
+                if (col == tbl->rows[row].col &&
+                    x >= 0 &&
+                    x < KHUI_SMICON_CX){
+
+                    nm_state |= CW_MOUSE_WSTICKY | CW_MOUSE_WIDGET;
+
+                } else if (col == tbl->rows[row].col &&
+                           x >= KHUI_SMICON_CX * 3 / 2 &&
+                           x < KHUI_SMICON_CX * 5 / 2) {
+                    nm_state |= CW_MOUSE_WICON | CW_MOUSE_WIDGET;
+                }
+            }
+        }
+    }
+
+    /* did the user drag the cursor off the current row? */
+    if((tbl->mouse_state & CW_MOUSE_LDOWN) &&
+       (nm_row != tbl->mouse_row)) {
+        nm_state &= ~CW_MOUSE_WMASK;
+    }
+
+    if(!(nm_state & CW_MOUSE_LDOWN) && 
+       (tbl->mouse_state & CW_MOUSE_LDOWN) &&
+       tbl->mouse_row == nm_row) {
+
+        if((nm_state & CW_MOUSE_WOUTLINE) &&
+           (tbl->mouse_state & CW_MOUSE_WOUTLINE)) {
+            /* click on an outline widget */
+            khui_credwnd_outline * o;
+
+            o = (khui_credwnd_outline *) tbl->rows[nm_row].data;
+            tbl->mouse_state = CW_MOUSE_WIDGET | CW_MOUSE_WOUTLINE;
+
+            cw_toggle_outline_state(tbl, o);
+
+            return 0;
+        } else if ((nm_state & CW_MOUSE_WSTICKY) &&
+                   (tbl->mouse_state & CW_MOUSE_WSTICKY)) {
+
+            khui_credwnd_outline * o;
+            khm_handle ident;
+            khm_int32 idf = 0;
+
+            o = tbl->rows[nm_row].data;
+            ident = o->data;
+
+            kcdb_identity_get_flags(ident, &idf);
+            idf &= KCDB_IDENT_FLAG_STICKY;
+            kcdb_identity_set_flags(ident, (idf ^ KCDB_IDENT_FLAG_STICKY),
+                                    KCDB_IDENT_FLAG_STICKY);
+
+            tbl->mouse_state = CW_MOUSE_WIDGET | CW_MOUSE_WSTICKY;
+
+            return 0;
+        } else if ((nm_state & CW_MOUSE_WICON) &&
+                   (tbl->mouse_state & CW_MOUSE_WICON)) {
+            /* click on an row icon */
+            cw_select_row(tbl, nm_row, wParam);
+            cw_properties(hwnd);
+        } else {
+            /* click on a row */
+            cw_select_row(tbl, nm_row, wParam);
+
+            if (tbl->mouse_col == nm_col &&
+                nm_col >= 0 &&
+                tbl->cols[nm_col].attr_id == CW_CA_FLAGS &&
+                !(tbl->rows[nm_row].flags & KHUI_CW_ROW_HEADER)) {
+                /* clicked on a cred icon */
+
+                cw_properties(hwnd);
+            }
+        }
+    }
+
+    /* ok, now if we are changing state, we need to invalidate a few
+       regions */
+    if (((tbl->mouse_state ^ nm_state) & (CW_MOUSE_WIDGET |
+                                          CW_MOUSE_WOUTLINE |
+                                          CW_MOUSE_WSTICKY)) ||
+        tbl->mouse_row != nm_row) {
+
+        if(tbl->mouse_state & CW_MOUSE_WOUTLINE) {
+            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);
+        }
+        if(tbl->mouse_state & CW_MOUSE_WSTICKY) {
+            if (tbl->flags & KHUI_CW_TBL_EXPIDENT) {
+
+                if (tbl->mouse_row >= 0 && tbl->mouse_row < tbl->n_rows) {
+                    r = tbl->rows[tbl->mouse_row].r_ext;
+                    OffsetRect(&r, -tbl->scr_left, tbl->header_height - tbl->scr_top);
+                    r.right = r.left + KHUI_SMICON_CX;
+                    InvalidateRect(tbl->hwnd, &r, TRUE);
+                }
+
+            } else {
+                r.left = KHUI_SMICON_CX * 3 / 2 + 
+                    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);
+        }
+
+        if ((tbl->mouse_state & nm_state) & CW_MOUSE_LDOWN) {
+            if (tbl->mouse_row == nm_row)
+                tbl->mouse_col = nm_col;
+        } else {
+            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_WOUTLINE) {
+            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);
+        }
+        if(tbl->mouse_state & CW_MOUSE_WSTICKY) {
+            if (tbl->flags & KHUI_CW_TBL_EXPIDENT) {
+
+                if (tbl->mouse_row >= 0 && tbl->mouse_row < tbl->n_rows) {
+                    r = tbl->rows[tbl->mouse_row].r_ext;
+                    OffsetRect(&r, -tbl->scr_left, tbl->header_height - tbl->scr_top);
+                    r.right = r.left + KHUI_SMICON_CX;
+                    InvalidateRect(tbl->hwnd, &r, TRUE);
+                }
+
+            } else {
+                r.left = KHUI_SMICON_CX * 3 / 2 + 
+                    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) {
+
+        if ((tbl->mouse_state & nm_state) & CW_MOUSE_LDOWN) {
+            if (tbl->mouse_row == nm_row) {
+                tbl->mouse_col = nm_col;
+                tbl->mouse_state = nm_state;
+            }
+        } else {
+            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);
+}
+
+static void
+cw_vscroll_to_pos(HWND hwnd, khui_credwnd_tbl * tbl, int newpos) {
+    RECT cr;
+    RECT sr;
+    RECT lr;
+    int dy;
+
+    GetClientRect(hwnd, &cr);
+    cr.top += tbl->header_height;
+    dy = tbl->scr_top;
+
+    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);
+    }
+}
+
+LRESULT 
+cw_wm_vscroll(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    khui_credwnd_tbl * tbl;
+    SCROLLINFO si;
+    int newpos;
+    RECT cr;
+
+    tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);
+
+    GetClientRect(hwnd, &cr);
+    cr.top += tbl->header_height;
+
+    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);
+    }
+
+    cw_vscroll_to_pos(hwnd, tbl, newpos);
+
+    return DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+
+static void
+cw_ensure_row_visible(HWND hwnd, khui_credwnd_tbl * tbl, int row) {
+    RECT r;
+    int newpos;
+
+    if (row < 0)
+        row = 0;
+    else if (row >= (int) tbl->n_rows)
+        row = (int) tbl->n_rows - 1;
+
+    GetClientRect(hwnd, &r);
+    r.top += tbl->header_height;
+
+    if (row * tbl->cell_height < tbl->scr_top) {
+        newpos = row * tbl->cell_height;
+    } else if ((row + 1) * tbl->cell_height
+             > tbl->scr_top + (r.bottom - r.top)) {
+        newpos = ((row + 1) * tbl->cell_height) - (r.bottom - r.top);
+    } else
+        return;
+
+    cw_vscroll_to_pos(hwnd, tbl, newpos);
+}
+
+static INT_PTR CALLBACK 
+cw_pp_ident_proc(HWND hwnd,
+                 UINT uMsg,
+                 WPARAM wParam,
+                 LPARAM lParam)
+{
+    khui_property_sheet * s;
+
+    switch(uMsg) {
+    case WM_INITDIALOG:
+        {
+            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);
+
+            CheckDlgButton(hwnd, IDC_PP_IDDEF,
+                           ((i & KCDB_IDENT_FLAG_DEFAULT)?BST_CHECKED:
+                            BST_UNCHECKED));
+
+            /* if it's default, you can't change it further */
+            if (i & KCDB_IDENT_FLAG_DEFAULT) {
+                EnableWindow(GetDlgItem(hwnd, IDC_PP_IDDEF), FALSE);
+            }
+
+            CheckDlgButton(hwnd, IDC_PP_IDSEARCH,
+                           ((i & KCDB_IDENT_FLAG_SEARCHABLE)?BST_CHECKED:
+                            BST_UNCHECKED));
+
+            CheckDlgButton(hwnd, IDC_PP_STICKY,
+                           ((i & KCDB_IDENT_FLAG_STICKY)?BST_CHECKED:
+                            BST_UNCHECKED));
+
+            khui_property_wnd_set_record(GetDlgItem(hwnd, IDC_PP_PROPLIST),
+                                         ident);
+        }
+        return TRUE;
+
+    case WM_COMMAND:
+        s = (khui_property_sheet *) (LONG_PTR) 
+            GetWindowLongPtr(hwnd, DWLP_USER);
+
+        switch(wParam) {
+        case MAKEWPARAM(IDC_PP_IDDEF, BN_CLICKED):
+            /* fallthrough */
+        case MAKEWPARAM(IDC_PP_STICKY, BN_CLICKED):
+
+            if (s->status != KHUI_PS_STATUS_NONE)
+                PropSheet_Changed(s->hwnd, hwnd);
+            return TRUE;
+
+        case MAKEWPARAM(IDC_PP_CONFIG, BN_CLICKED):
+            {
+                khui_config_node cfg_id = NULL;
+                khui_config_node cfg_ids = NULL;
+                wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
+                khm_size cb;
+                khm_int32 rv;
+
+                khm_refresh_config();
+
+                rv = khui_cfg_open(NULL,
+                                   L"KhmIdentities",
+                                   &cfg_ids);
+
+                if (KHM_FAILED(rv))
+                    return TRUE;
+
+                cb = sizeof(idname);
+                if (KHM_SUCCEEDED(kcdb_identity_get_name(s->identity,
+                                                         idname,
+                                                         &cb))) {
+                    rv = khui_cfg_open(cfg_ids,
+                                       idname,
+                                       &cfg_id);
+                }
+
+                if (cfg_id)
+                    khm_show_config_pane(cfg_id);
+                else
+                    khm_show_config_pane(cfg_ids);
+
+                if (cfg_ids)
+                    khui_cfg_release(cfg_ids);
+                if (cfg_id)
+                    khui_cfg_release(cfg_id);
+            }
+            return TRUE;
+        }
+        return FALSE;
+
+    case WM_NOTIFY:
+        {
+            LPPSHNOTIFY lpp;
+            khm_int32 flags;
+
+            lpp = (LPPSHNOTIFY) lParam;
+            s = (khui_property_sheet *) (LONG_PTR) 
+                GetWindowLongPtr(hwnd, DWLP_USER);
+
+            switch(lpp->hdr.code) {
+            case PSN_APPLY:
+                flags = 0;
+                if (IsDlgButtonChecked(hwnd, IDC_PP_STICKY) == BST_CHECKED)
+                    flags |= KCDB_IDENT_FLAG_STICKY;
+                if (IsDlgButtonChecked(hwnd, IDC_PP_IDDEF) == BST_CHECKED)
+                    flags |= KCDB_IDENT_FLAG_DEFAULT;
+
+                kcdb_identity_set_flags(s->identity, flags,
+                                        KCDB_IDENT_FLAG_STICKY |
+                                        KCDB_IDENT_FLAG_DEFAULT);
+                return TRUE;
+
+            case PSN_RESET:
+                kcdb_identity_get_flags(s->identity, &flags);
+
+                CheckDlgButton(hwnd, 
+                               IDC_PP_IDDEF, 
+                               ((flags & KCDB_IDENT_FLAG_DEFAULT)?BST_CHECKED:
+                                BST_UNCHECKED));
+
+                /* if it's default, you can't change it further */
+                if (flags & KCDB_IDENT_FLAG_DEFAULT) {
+                    EnableWindow(GetDlgItem(hwnd, IDC_PP_IDDEF), FALSE);
+                }
+
+                CheckDlgButton(hwnd, IDC_PP_IDSEARCH,
+                               ((flags & KCDB_IDENT_FLAG_SEARCHABLE)?BST_CHECKED:BST_UNCHECKED));
+
+                CheckDlgButton(hwnd, IDC_PP_STICKY,
+                               ((flags & KCDB_IDENT_FLAG_STICKY)?BST_CHECKED:BST_UNCHECKED));
+                return TRUE;
+            }
+        }
+        break;
+    }
+    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 = PMALLOC(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, 129, p, NULL);
+    }
+
+    if(s->cred) {
+        p = PMALLOC(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, 128, 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) {
+        PFREE(p->p_page);
+        p->p_page = NULL;
+    }
+
+    p = NULL;
+
+    khui_ps_find_page(s, KHUI_PPCT_CREDENTIAL, &p);
+    if(p) {
+        PFREE(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)
+            PFREE((LPWSTR) ps->header.pszCaption);
+    }
+
+    khui_context_release(&ps->ctx);
+
+    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) {
+        khui_context_release(&ctx);
+        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) {
+        khui_context_release(&ctx);
+        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 = PMALLOC(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;
+
+        if (ctx.identity == NULL) {
+            /* currently, we can't show a property sheet at this point
+               since most credentials providers don't provide a
+               property sheet that works without an identity. */
+
+            khui_context_release(&ctx);
+            khui_ps_destroy_sheet(ps);
+            return TRUE;
+        }
+
+        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 = PMALLOC(t);
+                kcdb_identity_get_name(ctx.identity, (wchar_t *) ps->header.pszCaption, &t);
+            } else {
+                ps->header.pszCaption = NULL;
+            }
+        } else {
+            /* we don't actually reach here since we handle this case
+               above */
+            kcdb_credtype_describe(cred_type, NULL, &t, KCDB_TS_LONG);
+            if(t > 0) {
+                ps->header.pszCaption = PMALLOC(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 = PMALLOC(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_context_release(&ctx);
+        khui_ps_destroy_sheet(ps);
+    }
+
+    /* by the way, if we are actually opening a property sheet, we
+       leave ctx held (which is now copied to ps->ctx).  it will be
+       released when the property sheet is destroyed */
+
+    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;
+        StringCchCopyN(wid, ARRAYLENGTH(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_RELOAD:
+        {
+            wchar_t cname[KCONF_MAXCCH_NAME];
+            khm_size cb;
+
+            cname[0] = L'\0';
+
+            if (tbl->csp_view) {
+                cb = sizeof(cname);
+                khc_get_config_space_name(tbl->csp_view,
+                                          cname,
+                                          &cb);
+            }
+
+            cw_unload_view(tbl);
+
+            cw_load_view(tbl, ((cname[0])?cname: NULL), hwnd);
+            cw_insert_header_cols(tbl);
+
+            cw_update_creds(tbl);
+            cw_update_outline(tbl);
+            cw_update_selection_state(tbl);
+            cw_update_extents(tbl, TRUE);
+
+            InvalidateRect(tbl->hwnd, NULL, TRUE);
+        }
+        break;
+
+    case KHUI_ACTION_LAYOUT_ID:
+        {
+            cw_save_view(tbl, NULL);
+            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_selection_state(tbl);
+            cw_update_extents(tbl, TRUE);
+
+            InvalidateRect(tbl->hwnd, NULL, TRUE);
+
+        }
+        break;
+
+    case KHUI_ACTION_LAYOUT_LOC:
+        {
+            cw_save_view(tbl, NULL);
+            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_selection_state(tbl);
+            cw_update_extents(tbl, TRUE);
+
+            InvalidateRect(tbl->hwnd, NULL, TRUE);
+
+        }
+        break;
+
+    case KHUI_ACTION_LAYOUT_TYPE:
+        {
+            cw_save_view(tbl, NULL);
+            cw_unload_view(tbl);
+
+            cw_load_view(tbl, L"ByType", hwnd);
+            cw_insert_header_cols(tbl);
+
+            cw_update_creds(tbl);
+            cw_update_outline(tbl);
+            cw_update_selection_state(tbl);
+            cw_update_extents(tbl, TRUE);
+
+            InvalidateRect(tbl->hwnd, NULL, TRUE);
+
+        }
+        break;
+
+    case KHUI_ACTION_LAYOUT_CUST:
+        {
+            cw_save_view(tbl, NULL);
+            cw_unload_view(tbl);
+
+            cw_load_view(tbl, L"Custom_0", hwnd);
+            cw_insert_header_cols(tbl);
+
+            cw_update_creds(tbl);
+            cw_update_outline(tbl);
+            cw_update_selection_state(tbl);
+            cw_update_extents(tbl, TRUE);
+
+            InvalidateRect(tbl->hwnd, NULL, TRUE);
+
+        }
+        break;
+
+    case KHUI_ACTION_LAYOUT_MINI:
+        {
+            cw_save_view(tbl, NULL);
+            cw_unload_view(tbl);
+
+            cw_load_view(tbl, NULL, hwnd);
+            cw_insert_header_cols(tbl);
+
+            cw_update_creds(tbl);
+            cw_update_outline(tbl);
+            cw_update_selection_state(tbl);
+            cw_update_extents(tbl, TRUE);
+
+            InvalidateRect(tbl->hwnd, NULL, TRUE);
+        }
+        break;
+
+    case KHUI_PACTION_UP:
+    case KHUI_PACTION_UP_EXTEND:
+    case KHUI_PACTION_UP_TOGGLE:
+        { /* cursor up */
+            khm_int32 new_row;
+            WPARAM wp = 0;
+
+            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 - 1;
+
+            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;
+            else {
+#ifdef DEBUG
+                assert(FALSE);
+#endif
+            }
+
+            cw_select_row(tbl, new_row, wp);
+            cw_ensure_row_visible(hwnd, tbl, new_row);
+        }
+        break;
+
+    case KHUI_PACTION_PGUP_EXTEND:
+    case KHUI_PACTION_PGUP:
+        {
+            khm_int32 new_row;
+            WPARAM wp;
+            RECT r;
+
+            if (LOWORD(wParam) == KHUI_PACTION_PGUP_EXTEND)
+                wp = MK_SHIFT;
+            else
+                wp = 0;
+
+            GetClientRect(hwnd, &r);
+
+            new_row = tbl->cursor_row -
+                ((r.bottom - r.top) - tbl->header_height) / tbl->cell_height;
+
+            if (new_row < 0)
+                new_row = 0;
+            if (new_row >= (int) tbl->n_rows)
+                new_row = (int) tbl->n_rows - 1;
+
+            cw_select_row(tbl, new_row, wp);
+            cw_ensure_row_visible(hwnd, tbl, new_row);
+        }
+        break;
+
+    case KHUI_PACTION_DOWN:
+    case KHUI_PACTION_DOWN_EXTEND:
+    case KHUI_PACTION_DOWN_TOGGLE:
+        { /* cursor down */
+            khm_int32 new_row;
+            WPARAM wp = 0;
+
+            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 - 1;
+
+            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;
+            else {
+#ifdef DEBUG
+                assert(FALSE);
+#endif
+            }
+
+            cw_select_row(tbl, new_row, wp);
+            cw_ensure_row_visible(hwnd, tbl, new_row);
+        }
+        break;
+
+    case KHUI_PACTION_PGDN_EXTEND:
+    case KHUI_PACTION_PGDN:
+        {
+            khm_int32 new_row;
+            RECT r;
+            WPARAM wp;
+
+            if (LOWORD(wParam) == KHUI_PACTION_PGDN_EXTEND)
+                wp = MK_SHIFT;
+            else
+                wp = 0;
+
+            GetClientRect(hwnd, &r);
+
+            new_row = tbl->cursor_row +
+                ((r.bottom - r.top) - tbl->header_height) / tbl->cell_height;
+
+            if (new_row < 0)
+                new_row = 0;
+            if (new_row >= (int) tbl->n_rows)
+                new_row = (int) tbl->n_rows - 1;
+
+            cw_select_row(tbl, new_row, wp);
+            cw_ensure_row_visible(hwnd, tbl, new_row);
+        }
+        break;
+
+    case KHUI_PACTION_SELALL:
+        {
+            cw_select_all(tbl);
+        }
+        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++;
+            if (r >= (int) tbl->n_rows)
+                r = (int)tbl->n_rows - 1;
+
+            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;
+
+    if (y < 0) {
+        /* context menu for header control */
+        khm_menu_show_panel(KHUI_MENU_CWHEADER_CTX,
+                            GET_X_LPARAM(lParam),
+                            GET_Y_LPARAM(lParam));
+
+        return DefWindowProc(hwnd, uMsg, wParam, lParam);
+    }
+
+    if (tbl->flags & KHUI_CW_TBL_EXPIDENT) {
+        int i, yt;
+
+        yt = 0;
+        for (i=0; i < tbl->n_rows && yt < y; i++) {
+            if (tbl->rows[i].flags & KHUI_CW_ROW_EXPVIEW)
+                yt += tbl->cell_height * CW_EXP_ROW_MULT;
+            else
+                yt += tbl->cell_height;
+            if (yt > y)
+                break;
+        }
+
+        row = i;
+
+    } else {
+        row = y / tbl->cell_height;
+    }
+
+    if(row < 0 || row >= (int) tbl->n_rows)
+        return FALSE;
+
+    cw_set_row_context(tbl, row);
+
+    khm_menu_show_panel(KHUI_MENU_IDENT_CTX, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+
+#if 0
+    /* calling cw_set_row_context() should take care of enabling or
+       disabling actions as appropriate.  We don't need to
+       differentiate between IDENT_CTX and TOK_CTX here. */
+    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();
+    }
+#endif
+
+    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 FALSE;
+
+    case WM_PAINT:
+        return cw_wm_paint(hwnd, uMsg, wParam, lParam);
+
+    case WM_PRINTCLIENT:
+        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_WINDOW + 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;
+    HWND hwnd;
+
+    ZeroMemory(attr_to_action, sizeof(attr_to_action));
+
+    GetClientRect(parent, &r);
+
+    hwnd = CreateWindowEx
+        (0,
+         MAKEINTATOM(khui_credwnd_cls),
+         L"",
+         WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
+         r.left,
+         r.top,
+         r.right - r.left,
+         r.bottom - r.top,
+         parent,
+         NULL,
+         khm_hInstance,
+         NULL);
+
+    return hwnd;
+}
index adecb9f072b9b23cf66bbbc292aba277f756dcba..47f8a745e125f35307dec771e5f3bbb17c94983c 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_CREDWND_H\r
-#define __KHIMAIRA_CREDWND_H\r
-\r
-#define KHUI_CREDWND_CLASS_NAME L"NetIDMgrCredWnd"\r
-\r
-#define KHUI_CREDWND_FLAG_ATTRNAME L"CredWndFlags"\r
-\r
-extern khm_int32 khui_cw_flag_id;\r
-\r
-/* The expiration states */\r
-#define CW_EXPSTATE_NONE        0x00000000\r
-#define CW_EXPSTATE_WARN        0x00000400\r
-#define CW_EXPSTATE_CRITICAL    0x00000800\r
-#define CW_EXPSTATE_EXPIRED     0x00000c00\r
-\r
-#define CW_EXPSTATE_MASK        0x00000c00\r
-\r
-typedef struct khui_credwnd_outline_t {\r
-    khm_int32   flags;      /* combination of KHUI_CW_O_* */\r
-    khm_int32   start;      /* first row of outline */\r
-    khm_int32   length;     /* number of rows in outline */\r
-    khm_int32   level;      /* outline level */\r
-    khm_int32   col;        /* outline column */\r
-    wchar_t     *header;    /* character string associated with header */\r
-    khm_int32   attr_id;\r
-    void *      data;       /* level specific data :\r
-                               Identity -> handle to identity\r
-                               Type -> type ID\r
-                               otherwise -> canonical data buffer\r
-                            */\r
-    khm_size    cb_data;\r
-\r
-    khm_size    idx_start;  /* index of the first cred in the credset */\r
-    khm_size    idx_end;    /* index of the last cred in the credset */\r
-    TDCL(struct khui_credwnd_outline_t);\r
-} khui_credwnd_outline;\r
-\r
-#define KHUI_CW_O_EXPAND        0x00000001\r
-#define KHUI_CW_O_STICKY        0x00000002\r
-#define KHUI_CW_O_VISIBLE       0x00000004\r
-#define KHUI_CW_O_SHOWFLAG      0x00000008\r
-#define KHUI_CW_O_SELECTED      0x00000010\r
-#define KHUI_CW_O_DATAALLOC     0x00000020\r
-#define KHUI_CW_O_NOOUTLINE     0x00000040\r
-#define KHUI_CW_O_RELIDENT      0x00000080\r
-#define KHUI_CW_O_EMPTY         0x00000100\r
-/* NOTE: KHUI_CW_O_* shares the same bit-space as CW_EXPSTATE_* */\r
-\r
-typedef struct khui_credwnd_row_t {\r
-    khm_int32   flags;\r
-    khm_int32   col;\r
-    khm_handle  data;\r
-    khm_size idx_start;\r
-    khm_size idx_end;\r
-    RECT        r_ext;          /* extents of this row */\r
-} khui_credwnd_row;\r
-\r
-#define KHUI_CW_ROW_CRED        0x00000002\r
-#define KHUI_CW_ROW_HEADER      0x00000004\r
-#define KHUI_CW_ROW_TIMERSET    0x00000008\r
-#define KHUI_CW_ROW_SELECTED    0x00000010\r
-#define KHUI_CW_ROW_EXPVIEW     0x00000020\r
-/* NOTE: KHUI_CW_ROW_* shares the same bit-space as CW_EXPSTATE_* */\r
-\r
-/* row allocation */\r
-/* initial number of rows to be allocated */\r
-#define KHUI_CW_ROW_INITIAL     512\r
-/* allocation increment, if we run out of space */\r
-#define KHUI_CW_ROW_INCREMENT   512\r
-\r
-typedef struct khui_credwnd_col_t {\r
-    khm_int32 attr_id;\r
-    khm_int32 width;        /* width of the column (screen units) */\r
-    khm_int32 x;            /* starting x coordinate (screen units) */\r
-    khm_int32 flags;        /* combination of KHUI_CW_COL_* */\r
-    khm_int32 sort_index;\r
-    wchar_t * title;\r
-} khui_credwnd_col;\r
-\r
-/* column allocation */\r
-/* initial number of columns to be allocated */\r
-#define KHUI_CW_COL_INITIAL     16\r
-/* allocation increment, if we run out of space */\r
-#define KHUI_CW_COL_INCREMENT   16\r
-\r
-#define KHUI_CW_COL_AUTOSIZE    0x00000001\r
-#define KHUI_CW_COL_SORT_INC    0x00000002\r
-#define KHUI_CW_COL_SORT_DEC    0x00000004\r
-#define KHUI_CW_COL_GROUP       0x00000008\r
-#define KHUI_CW_COL_FIXED_WIDTH 0x00000010\r
-#define KHUI_CW_COL_FIXED_POS   0x00000020\r
-#define KHUI_CW_COL_META        0x00000040\r
-#define KHUI_CW_COL_FILLER      0x00000080\r
-\r
-/* Custom column attributes (are not kcdb attributes) */\r
-#define CW_CA_FLAGS -1\r
-#define CW_CANAME_FLAGS L"_CWFlags"\r
-\r
-#define CW_CA_TYPEICON -2\r
-#define CW_CANAME_TYPEICON L"_CWTypeIcon"\r
-\r
-#define cw_is_custom_attr(i) ((i)<0)\r
-\r
-typedef struct tag_khui_credwnd_ident {\r
-\r
-    khm_handle ident;\r
-    khm_int32  ident_flags;\r
-    khm_int32  credtype;\r
-    wchar_t    name[KCDB_IDENT_MAXCCH_NAME];\r
-    wchar_t    credtype_name[KCDB_MAXCCH_NAME];\r
-\r
-    khm_size   credcount;       /* count of all credentials */\r
-    khm_size   id_credcount;    /* count of identity credentials\r
-                                   (credentials that are of the\r
-                                   identity type */\r
-    khm_size   init_credcount;  /* count of initial credentials */\r
-    FILETIME   ft_expire;\r
-\r
-} khui_credwnd_ident;\r
-\r
-#define CW_IDENT_ALLOC_INCR 4\r
-\r
-#define CW_EXP_ROW_MULT 2\r
-\r
-typedef struct khui_credwnd_tbl_t {\r
-    HWND hwnd;                  /* the window that this table belongs to */\r
-\r
-    khm_handle csp_view;        /* handle to the configuration space\r
-                                   that defined the view */\r
-\r
-    khm_int32 scr_top;          /* screen units */\r
-    khm_int32 scr_left;         /* screen units */\r
-    khm_int32 ext_width;        /* screen units */\r
-    khm_int32 ext_height;       /* screen units */\r
-    khm_int32 cell_height;      /* screen units */\r
-\r
-    HWND hwnd_header;           /* header control */\r
-    khm_int32 header_height;    /* height of the header */\r
-    HWND hwnd_notif;            /* notification control */\r
-\r
-    khui_credwnd_col * cols;    /* n_cols elements */\r
-    khui_credwnd_row * rows;    /* n_rows elements */\r
-    int       n_cols;\r
-    int       n_total_cols;     /* number of columns actually\r
-                                   allocated in cols */\r
-    int       n_rows;\r
-    int       n_total_rows;     /* number of rows actually allocated\r
-                                   in rows */\r
-\r
-    khui_credwnd_outline * outline;\r
-\r
-    khm_int32 flags;            /* combo of KHUI_CW_TBL_* */\r
-\r
-    int       cursor_row;       /* cursor and selection */\r
-    int       anchor_row;       /* anchor, for range selections */\r
-\r
-    /* view parameters */\r
-    khm_int32 hpad;\r
-    khm_int32 vpad;\r
-    khm_int32 hpad_h;       /* horizontal padding correction for headers */\r
-    khm_int32 threshold_warn;  /* Warning threshold, in seconds*/\r
-    khm_int32 threshold_critical; /* Critical threshold, in seconds */\r
-\r
-    /* graphics objects we are going to need. */\r
-    HFONT hf_normal;        /* normal text */\r
-    HFONT hf_header;        /* header text */\r
-    HFONT hf_bold;          /* bold text */\r
-    HFONT hf_bold_header;   /* bold header text */\r
-\r
-    HBRUSH hb_normal;       /* normal background brush */\r
-    HBRUSH hb_grey;         /* normal background brush (greyed) */\r
-    HBRUSH hb_s;            /* normal background brush (selected) */\r
-\r
-    HBRUSH hb_hdr_bg;       /* header background brush (normal) */\r
-    HBRUSH hb_hdr_bg_exp;   /* header background brush (expired) */\r
-    HBRUSH hb_hdr_bg_warn;  /* header background brush (warn) */\r
-    HBRUSH hb_hdr_bg_crit;  /* header background brush (critical) */\r
-    HBRUSH hb_hdr_bg_def;   /* header background brush (default) */\r
-\r
-    HBRUSH hb_hdr_bg_s;     /* header background brush (selected) */\r
-    HBRUSH hb_hdr_bg_exp_s; /* header background brush (expired,selected) */\r
-    HBRUSH hb_hdr_bg_warn_s;/* header background brush (warn,selected) */\r
-    HBRUSH hb_hdr_bg_crit_s;/* header background brush (critical,selected) */\r
-    HBRUSH hb_hdr_bg_def_s; /* header background brush (default,selected) */\r
-\r
-    COLORREF cr_normal;     /* text color (normal) */\r
-    COLORREF cr_s;          /* text color (selected) */\r
-    COLORREF cr_hdr_normal; /* header text color (normal) */\r
-    COLORREF cr_hdr_s;      /* header text color (selected) */\r
-    COLORREF cr_hdr_gray;   /* header text color (greyed) */\r
-    COLORREF cr_hdr_gray_s; /* header text color (greyed,selected) */\r
-\r
-    COLORREF cr_hdr_outline;/* header outline color */\r
-\r
-    HCURSOR hc_hand;        /* the HAND cursor */\r
-    khui_ilist * ilist;     /* image list */\r
-\r
-    HICON   hi_lg_ident;    /* large identity icon */\r
-\r
-    /* mouse state */\r
-    khm_int32 mouse_state;        /* state of the mouse can be combo of CW_MOUSE_* values */\r
-    khm_int32 mouse_row;          /* row that the mouse state applies to */\r
-    khm_int32 mouse_col;          /* col that the mouse state applies to */\r
-\r
-    khui_bitmap kbm_logo_shade;\r
-\r
-    /* the credentials set */\r
-    khm_handle credset;\r
-\r
-    khui_credwnd_ident * idents;\r
-    khm_size n_idents;\r
-    khm_size nc_idents;\r
-\r
-} khui_credwnd_tbl;\r
-\r
-#define KHUI_MAXCB_HEADING 256\r
-\r
-/* table flags */\r
-#define KHUI_CW_TBL_INITIALIZED 0x00000001\r
-#define KHUI_CW_TBL_COL_DIRTY   0x00000002\r
-#define KHUI_CW_TBL_ROW_DIRTY   0x00000004\r
-#define KHUI_CW_TBL_ACTIVE      0x00000100\r
-#define KHUI_CW_TBL_CUSTVIEW    0x00000200\r
-#define KHUI_CW_TBL_COLSKIP     0x00000400\r
-#define KHUI_CW_TBL_EXPIDENT    0x00000800\r
-#define KHUI_CW_TBL_NOHEADER    0x00001000\r
-\r
-/* mouse_state constants */\r
-#define CW_MOUSE_NONE       0x00000000 /* nothing interesting */\r
-#define CW_MOUSE_WIDGET     0x00000001 /* mouse is highlighting a\r
-                                          widget */\r
-#define CW_MOUSE_LDOWN      0x00000002 /* left button is down */\r
-#define CW_MOUSE_ROW        0x00000004 /* mouse is acive over a valid\r
-                                          row */\r
-#define CW_MOUSE_WOUTLINE   0x00000008 /* mouse is highlighting an\r
-                                          outline widget */\r
-#define CW_MOUSE_WSTICKY    0x00000010 /* mouse is highlighting a\r
-                                          sticky widget */\r
-#define CW_MOUSE_WICON      0x00000020 /* an icon widget.  represents\r
-                                          the icon next to identities\r
-                                          and next to credentials. */\r
-\r
-#define CW_MOUSE_WMASK      0x00000039 /* all widget bits */\r
-\r
-void khm_unregister_credwnd_class(void);\r
-\r
-void khm_register_credwnd_class(void);\r
-\r
-HWND khm_create_credwnd(HWND parent);\r
-\r
-LRESULT CALLBACK khm_credwnd_proc(HWND hwnd,\r
-    UINT uMsg,\r
-    WPARAM wParam,\r
-    LPARAM lParam\r
-    );\r
-\r
-void    cw_load_view(khui_credwnd_tbl * tbl, wchar_t * viewname, HWND hwnd);\r
-\r
-void    cw_update_creds(khui_credwnd_tbl * tbl);\r
-\r
-void    cw_unload_view(khui_credwnd_tbl * tbl);\r
-\r
-void    cw_hditem_from_tbl_col(khui_credwnd_col * col, HDITEM *phi);\r
-\r
-int     cw_update_extents(khui_credwnd_tbl * tbl, khm_boolean update_scroll);\r
-\r
-void    cw_insert_header_cols(khui_credwnd_tbl * tbl);\r
-\r
-void    khm_get_cw_element_font(HDC hdc, wchar_t * name, BOOL use_default,\r
-                                LOGFONT * pfont);\r
-\r
-void    khm_set_cw_element_font(wchar_t * name, LOGFONT * pfont);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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        0x00000000
+#define CW_EXPSTATE_WARN        0x00000400
+#define CW_EXPSTATE_CRITICAL    0x00000800
+#define CW_EXPSTATE_EXPIRED     0x00000c00
+
+#define CW_EXPSTATE_MASK        0x00000c00
+
+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
+#define KHUI_CW_O_NOOUTLINE     0x00000040
+#define KHUI_CW_O_RELIDENT      0x00000080
+#define KHUI_CW_O_EMPTY         0x00000100
+/* NOTE: KHUI_CW_O_* shares the same bit-space as CW_EXPSTATE_* */
+
+typedef struct khui_credwnd_row_t {
+    khm_int32   flags;
+    khm_int32   col;
+    khm_handle  data;
+    khm_size idx_start;
+    khm_size idx_end;
+    RECT        r_ext;          /* extents of this row */
+} khui_credwnd_row;
+
+#define KHUI_CW_ROW_CRED        0x00000002
+#define KHUI_CW_ROW_HEADER      0x00000004
+#define KHUI_CW_ROW_TIMERSET    0x00000008
+#define KHUI_CW_ROW_SELECTED    0x00000010
+#define KHUI_CW_ROW_EXPVIEW     0x00000020
+/* NOTE: KHUI_CW_ROW_* shares the same bit-space as CW_EXPSTATE_* */
+
+/* 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    0x00000001
+#define KHUI_CW_COL_SORT_INC    0x00000002
+#define KHUI_CW_COL_SORT_DEC    0x00000004
+#define KHUI_CW_COL_GROUP       0x00000008
+#define KHUI_CW_COL_FIXED_WIDTH 0x00000010
+#define KHUI_CW_COL_FIXED_POS   0x00000020
+#define KHUI_CW_COL_META        0x00000040
+#define KHUI_CW_COL_FILLER      0x00000080
+
+/* 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 tag_khui_credwnd_ident {
+
+    khm_handle ident;
+    khm_int32  ident_flags;
+    khm_int32  credtype;
+    wchar_t    name[KCDB_IDENT_MAXCCH_NAME];
+    wchar_t    credtype_name[KCDB_MAXCCH_NAME];
+
+    khm_size   credcount;       /* count of all credentials */
+    khm_size   id_credcount;    /* count of identity credentials
+                                   (credentials that are of the
+                                   identity type */
+    khm_size   init_credcount;  /* count of initial credentials */
+    FILETIME   ft_expire;
+
+} khui_credwnd_ident;
+
+#define CW_IDENT_ALLOC_INCR 4
+
+#define CW_EXP_ROW_MULT 2
+
+typedef struct khui_credwnd_tbl_t {
+    HWND hwnd;                  /* the window that this table belongs to */
+
+    khm_handle csp_view;        /* handle to the configuration space
+                                   that defined the view */
+
+    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 */
+    int       n_cols;
+    int       n_total_cols;     /* number of columns actually
+                                   allocated in cols */
+    int       n_rows;
+    int       n_total_rows;     /* number of rows actually allocated
+                                   in rows */
+
+    khui_credwnd_outline * outline;
+
+    khm_int32 flags;            /* combo of KHUI_CW_TBL_* */
+
+    int       cursor_row;       /* cursor and selection */
+    int       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 background brush (greyed) */
+    HBRUSH hb_s;            /* normal background brush (selected) */
+
+    HBRUSH hb_hdr_bg;       /* header background brush (normal) */
+    HBRUSH hb_hdr_bg_exp;   /* header background brush (expired) */
+    HBRUSH hb_hdr_bg_warn;  /* header background brush (warn) */
+    HBRUSH hb_hdr_bg_crit;  /* header background brush (critical) */
+    HBRUSH hb_hdr_bg_def;   /* header background brush (default) */
+
+    HBRUSH hb_hdr_bg_s;     /* header background brush (selected) */
+    HBRUSH hb_hdr_bg_exp_s; /* header background brush (expired,selected) */
+    HBRUSH hb_hdr_bg_warn_s;/* header background brush (warn,selected) */
+    HBRUSH hb_hdr_bg_crit_s;/* header background brush (critical,selected) */
+    HBRUSH hb_hdr_bg_def_s; /* header background brush (default,selected) */
+
+    COLORREF cr_normal;     /* text color (normal) */
+    COLORREF cr_s;          /* text color (selected) */
+    COLORREF cr_hdr_normal; /* header text color (normal) */
+    COLORREF cr_hdr_s;      /* header text color (selected) */
+    COLORREF cr_hdr_gray;   /* header text color (greyed) */
+    COLORREF cr_hdr_gray_s; /* header text color (greyed,selected) */
+
+    COLORREF cr_hdr_outline;/* header outline color */
+
+    HCURSOR hc_hand;        /* the HAND cursor */
+    khui_ilist * ilist;     /* image list */
+
+    HICON   hi_lg_ident;    /* large identity icon */
+
+    /* 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_ident * idents;
+    khm_size n_idents;
+    khm_size nc_idents;
+
+} 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
+#define KHUI_CW_TBL_CUSTVIEW    0x00000200
+#define KHUI_CW_TBL_COLSKIP     0x00000400
+#define KHUI_CW_TBL_EXPIDENT    0x00000800
+#define KHUI_CW_TBL_NOHEADER    0x00001000
+
+/* mouse_state constants */
+#define CW_MOUSE_NONE       0x00000000 /* nothing interesting */
+#define CW_MOUSE_WIDGET     0x00000001 /* mouse is highlighting a
+                                          widget */
+#define CW_MOUSE_LDOWN      0x00000002 /* left button is down */
+#define CW_MOUSE_ROW        0x00000004 /* mouse is acive over a valid
+                                          row */
+#define CW_MOUSE_WOUTLINE   0x00000008 /* mouse is highlighting an
+                                          outline widget */
+#define CW_MOUSE_WSTICKY    0x00000010 /* mouse is highlighting a
+                                          sticky widget */
+#define CW_MOUSE_WICON      0x00000020 /* an icon widget.  represents
+                                          the icon next to identities
+                                          and next to credentials. */
+
+#define CW_MOUSE_WMASK      0x00000039 /* all widget bits */
+
+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);
+
+void    khm_get_cw_element_font(HDC hdc, wchar_t * name, BOOL use_default,
+                                LOGFONT * pfont);
+
+void    khm_set_cw_element_font(wchar_t * name, LOGFONT * pfont);
+
+#endif
index 7df6e9d651730864faa306e7bf7067550862e991..3e30f117e4289b9d73972b401b96498ee760addd 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<tchar.h>\r
-\r
-#include<shlwapi.h>\r
-#include<khmapp.h>\r
-\r
-#include<stdio.h>\r
-\r
-#if DEBUG\r
-#include<assert.h>\r
-#endif\r
-\r
-#define LOGFILENAME "nidmdbg.log"\r
-\r
-CRITICAL_SECTION cs_log;\r
-FILE * logfile = NULL;\r
-BOOL log_started = FALSE;\r
-\r
-wchar_t *\r
-severity_string(kherr_severity severity) {\r
-    switch(severity) {\r
-    case KHERR_FATAL:\r
-       return L"FATAL";\r
-\r
-    case KHERR_ERROR:\r
-       return L"ERROR";\r
-\r
-    case KHERR_WARNING:\r
-       return L"Warning";\r
-\r
-    case KHERR_INFO:\r
-       return L"Info";\r
-\r
-    case KHERR_DEBUG_3:\r
-       return L"Debug(3)";\r
-\r
-    case KHERR_DEBUG_2:\r
-       return L"Debug(2)";\r
-\r
-    case KHERR_DEBUG_1:\r
-       return L"Debug(1)";\r
-\r
-    case KHERR_NONE:\r
-       return L"(None)";\r
-\r
-    default:\r
-       return L"(Unknown severity)";\r
-    }\r
-}\r
-\r
-void\r
-fprint_systime(FILE * f, SYSTEMTIME *psystime) {\r
-    fprintf(logfile,\r
-            "%d-%d-%d %02d:%02d:%02d.%03d",\r
-\r
-            (int) psystime->wYear,\r
-            (int) psystime->wMonth,\r
-            (int) psystime->wDay,\r
-\r
-            (int) psystime->wHour,\r
-            (int) psystime->wMinute,\r
-            (int) psystime->wSecond,\r
-            (int) psystime->wMilliseconds);\r
-}\r
-\r
-void KHMAPI\r
-debug_event_handler(enum kherr_ctx_event e,\r
-                   kherr_context * c) {\r
-    kherr_event * evt;\r
-\r
-    EnterCriticalSection(&cs_log);\r
-\r
-    if (!logfile)\r
-       goto _done;\r
-\r
-    if (e == KHERR_CTX_BEGIN) {\r
-        SYSTEMTIME systime;\r
-\r
-        GetSystemTime(&systime);\r
-       fprintf(logfile,\r
-               "%d\t",\r
-               c->serial);\r
-\r
-        fprint_systime(logfile, &systime);\r
-\r
-        fprintf(logfile,\r
-                "\t<< Context begin --\n");\r
-\r
-    } else if (e == KHERR_CTX_DESCRIBE) {\r
-       evt = kherr_get_desc_event(c);\r
-       if (evt) {\r
-           kherr_evaluate_event(evt);\r
-           fprintf(logfile,\r
-                   "%d\t  Description: %S\n",\r
-                   c->serial,\r
-                   (evt->long_desc)? evt->long_desc: evt->short_desc);\r
-       }\r
-    } else if (e == KHERR_CTX_END) {\r
-        SYSTEMTIME systime;\r
-\r
-       fprintf(logfile,\r
-               "%d\t",\r
-               c->serial);\r
-\r
-        GetSystemTime(&systime);\r
-        fprint_systime(logfile, &systime);\r
-\r
-        fprintf(logfile,\r
-                "\t>> Context end --\n");\r
-\r
-    } else if (e == KHERR_CTX_EVTCOMMIT) {\r
-       evt = kherr_get_last_event(c);\r
-       if (evt && (evt->short_desc || evt->long_desc)) {\r
-           SYSTEMTIME systime;\r
-\r
-           kherr_evaluate_event(evt);\r
-           FileTimeToSystemTime(&evt->time_ft, &systime);\r
-           \r
-           fprintf(logfile,\r
-                   "%d[%d](%S)\t",\r
-                   c->serial,\r
-                   evt->thread_id,\r
-                   (evt->facility ? evt->facility: L""));\r
-\r
-            fprint_systime(logfile, &systime);\r
-\r
-            fprintf(logfile,\r
-                    "\t%S: %S %S%S%S %S%S%S\n",\r
-\r
-                   severity_string(evt->severity),\r
-\r
-                   (evt->short_desc ? evt->short_desc: L""),\r
-\r
-                   (evt->short_desc ? L"(":L""),\r
-                   (evt->long_desc ? evt->long_desc: L""),\r
-                   (evt->short_desc ? L")":L""),\r
-\r
-                   (evt->suggestion ? L"[":L""),\r
-                   (evt->suggestion ? evt->suggestion: L""),\r
-                   (evt->suggestion ? L"]":L"")\r
-                   );\r
-       }\r
-    }\r
-\r
- _done:\r
-\r
-    LeaveCriticalSection(&cs_log);\r
-}\r
-\r
-void khm_get_file_log_path(khm_size cb_buf, wchar_t * buf) {\r
-#ifdef DEBUG\r
-    assert(cb_buf > sizeof(wchar_t));\r
-#endif\r
-    *buf = L'\0';\r
-\r
-    GetTempPath((DWORD) cb_buf / sizeof(wchar_t), buf);\r
-\r
-    StringCbCat(buf, cb_buf, _T(LOGFILENAME));\r
-}\r
-\r
-void khm_start_file_log(void) {\r
-    wchar_t temppath[MAX_PATH];\r
-    khm_handle cs_cw = NULL;\r
-    khm_int32 t = 0;\r
-\r
-    EnterCriticalSection(&cs_log);\r
-\r
-    if (log_started)\r
-       goto _done;\r
-\r
-    if (KHM_FAILED(khc_open_space(NULL, L"CredWindow", 0, &cs_cw)))\r
-       goto _done;\r
-\r
-    if (KHM_FAILED(khc_read_int32(cs_cw, L"LogToFile", &t)) ||\r
-       !t)\r
-       goto _done;\r
-\r
-    khm_get_file_log_path(sizeof(temppath), temppath);\r
-\r
-    logfile = NULL;\r
-#if _MSC_VER >= 1400\r
-    _wfopen_s(&logfile, temppath, L"w");\r
-#else\r
-    logfile = _wfopen(temppath, L"w");\r
-#endif\r
-    kherr_add_ctx_handler(debug_event_handler,\r
-                         KHERR_CTX_BEGIN |\r
-                         KHERR_CTX_END |\r
-                         KHERR_CTX_DESCRIBE |\r
-                         KHERR_CTX_EVTCOMMIT,\r
-                         0);\r
-\r
-    log_started = TRUE;\r
-\r
- _done:\r
-    if (cs_cw)\r
-       khc_close_space(cs_cw);\r
-\r
-    LeaveCriticalSection(&cs_log);\r
-}\r
-\r
-void khm_stop_file_log(void) {\r
-\r
-    EnterCriticalSection(&cs_log);\r
-\r
-    if (!log_started)\r
-       goto _done;\r
-\r
-    kherr_remove_ctx_handler(debug_event_handler, 0);\r
-\r
-    if (logfile)\r
-       fclose (logfile);\r
-    logfile = NULL;\r
-\r
-    log_started = FALSE;\r
-\r
- _done:\r
-    LeaveCriticalSection(&cs_log);\r
-}\r
-\r
-void khm_init_debug(void) {\r
-    InitializeCriticalSection(&cs_log);\r
-\r
-    khm_start_file_log();\r
-}\r
-\r
-void khm_exit_debug(void) {\r
-    khm_stop_file_log();\r
-\r
-    DeleteCriticalSection(&cs_log);\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<tchar.h>
+
+#include<shlwapi.h>
+#include<khmapp.h>
+
+#include<stdio.h>
+
+#if DEBUG
+#include<assert.h>
+#endif
+
+#define LOGFILENAME "nidmdbg.log"
+
+CRITICAL_SECTION cs_log;
+FILE * logfile = NULL;
+BOOL log_started = FALSE;
+
+wchar_t *
+severity_string(kherr_severity severity) {
+    switch(severity) {
+    case KHERR_FATAL:
+       return L"FATAL";
+
+    case KHERR_ERROR:
+       return L"ERROR";
+
+    case KHERR_WARNING:
+       return L"Warning";
+
+    case KHERR_INFO:
+       return L"Info";
+
+    case KHERR_DEBUG_3:
+       return L"Debug(3)";
+
+    case KHERR_DEBUG_2:
+       return L"Debug(2)";
+
+    case KHERR_DEBUG_1:
+       return L"Debug(1)";
+
+    case KHERR_NONE:
+       return L"(None)";
+
+    default:
+       return L"(Unknown severity)";
+    }
+}
+
+void
+fprint_systime(FILE * f, SYSTEMTIME *psystime) {
+    fprintf(logfile,
+            "%d-%d-%d %02d:%02d:%02d.%03d",
+
+            (int) psystime->wYear,
+            (int) psystime->wMonth,
+            (int) psystime->wDay,
+
+            (int) psystime->wHour,
+            (int) psystime->wMinute,
+            (int) psystime->wSecond,
+            (int) psystime->wMilliseconds);
+}
+
+void KHMAPI
+debug_event_handler(enum kherr_ctx_event e,
+                   kherr_context * c) {
+    kherr_event * evt;
+
+    EnterCriticalSection(&cs_log);
+
+    if (!logfile)
+       goto _done;
+
+    if (e == KHERR_CTX_BEGIN) {
+        SYSTEMTIME systime;
+
+        GetSystemTime(&systime);
+       fprintf(logfile,
+               "%d\t",
+               c->serial);
+
+        fprint_systime(logfile, &systime);
+
+        fprintf(logfile,
+                "\t<< Context begin --\n");
+
+    } else if (e == KHERR_CTX_DESCRIBE) {
+       evt = kherr_get_desc_event(c);
+       if (evt) {
+           kherr_evaluate_event(evt);
+           fprintf(logfile,
+                   "%d\t  Description: %S\n",
+                   c->serial,
+                   (evt->long_desc)? evt->long_desc: evt->short_desc);
+       }
+    } else if (e == KHERR_CTX_END) {
+        SYSTEMTIME systime;
+
+       fprintf(logfile,
+               "%d\t",
+               c->serial);
+
+        GetSystemTime(&systime);
+        fprint_systime(logfile, &systime);
+
+        fprintf(logfile,
+                "\t>> Context end --\n");
+
+    } else if (e == KHERR_CTX_EVTCOMMIT) {
+       evt = kherr_get_last_event(c);
+       if (evt && (evt->short_desc || evt->long_desc)) {
+           SYSTEMTIME systime;
+
+           kherr_evaluate_event(evt);
+           FileTimeToSystemTime(&evt->time_ft, &systime);
+           
+           fprintf(logfile,
+                   "%d[%d](%S)\t",
+                   c->serial,
+                   evt->thread_id,
+                   (evt->facility ? evt->facility: L""));
+
+            fprint_systime(logfile, &systime);
+
+            fprintf(logfile,
+                    "\t%S: %S %S%S%S %S%S%S\n",
+
+                   severity_string(evt->severity),
+
+                   (evt->short_desc ? evt->short_desc: L""),
+
+                   (evt->short_desc ? L"(":L""),
+                   (evt->long_desc ? evt->long_desc: L""),
+                   (evt->short_desc ? L")":L""),
+
+                   (evt->suggestion ? L"[":L""),
+                   (evt->suggestion ? evt->suggestion: L""),
+                   (evt->suggestion ? L"]":L"")
+                   );
+       }
+    }
+
+ _done:
+
+    LeaveCriticalSection(&cs_log);
+}
+
+void khm_get_file_log_path(khm_size cb_buf, wchar_t * buf) {
+#ifdef DEBUG
+    assert(cb_buf > sizeof(wchar_t));
+#endif
+    *buf = L'\0';
+
+    GetTempPath((DWORD) cb_buf / sizeof(wchar_t), buf);
+
+    StringCbCat(buf, cb_buf, _T(LOGFILENAME));
+}
+
+void khm_start_file_log(void) {
+    wchar_t temppath[MAX_PATH];
+    khm_handle cs_cw = NULL;
+    khm_int32 t = 0;
+
+    EnterCriticalSection(&cs_log);
+
+    if (log_started)
+       goto _done;
+
+    if (KHM_FAILED(khc_open_space(NULL, L"CredWindow", 0, &cs_cw)))
+       goto _done;
+
+    if (KHM_FAILED(khc_read_int32(cs_cw, L"LogToFile", &t)) ||
+       !t)
+       goto _done;
+
+    khm_get_file_log_path(sizeof(temppath), temppath);
+
+    logfile = NULL;
+#if _MSC_VER >= 1400
+    _wfopen_s(&logfile, temppath, L"w");
+#else
+    logfile = _wfopen(temppath, L"w");
+#endif
+    kherr_add_ctx_handler(debug_event_handler,
+                         KHERR_CTX_BEGIN |
+                         KHERR_CTX_END |
+                         KHERR_CTX_DESCRIBE |
+                         KHERR_CTX_EVTCOMMIT,
+                         0);
+
+    log_started = TRUE;
+
+ _done:
+    if (cs_cw)
+       khc_close_space(cs_cw);
+
+    LeaveCriticalSection(&cs_log);
+}
+
+void khm_stop_file_log(void) {
+
+    EnterCriticalSection(&cs_log);
+
+    if (!log_started)
+       goto _done;
+
+    kherr_remove_ctx_handler(debug_event_handler, 0);
+
+    if (logfile)
+       fclose (logfile);
+    logfile = NULL;
+
+    log_started = FALSE;
+
+ _done:
+    LeaveCriticalSection(&cs_log);
+}
+
+void khm_init_debug(void) {
+    InitializeCriticalSection(&cs_log);
+
+    khm_start_file_log();
+}
+
+void khm_exit_debug(void) {
+    khm_stop_file_log();
+
+    DeleteCriticalSection(&cs_log);
+}
index 4c378a3a1bab61bac42fc0188f3e7503ce51e36c..6467185f3a5c3113b6e9f4f2d8a384857a8e365a 100644 (file)
@@ -1,37 +1,37 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __NETIDMGR_DEBUGFUNCS_H\r
-#define __NETIDMGR_DEBUGFUNCS_H\r
-\r
-void khm_init_debug(void);\r
-void khm_exit_debug(void);\r
-\r
-void khm_start_file_log(void);\r
-void khm_stop_file_log(void);\r
-void khm_get_file_log_path(khm_size cb_buf, wchar_t * buf);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 __NETIDMGR_DEBUGFUNCS_H
+#define __NETIDMGR_DEBUGFUNCS_H
+
+void khm_init_debug(void);
+void khm_exit_debug(void);
+
+void khm_start_file_log(void);
+void khm_stop_file_log(void);
+void khm_get_file_log_path(khm_size cb_buf, wchar_t * buf);
+
+#endif
index b7c0378668f44d815b877042d0c80c6af056047e..a65ceceeede6d17e521d479dcac1885903433e12 100644 (file)
-/*\r
-* Copyright (c) 2005 Massachusetts Institute of Technology\r
-*\r
-* Permission is hereby granted, free of charge, to any person\r
-* obtaining a copy of this software and associated documentation\r
-* files (the "Software"), to deal in the Software without\r
-* restriction, including without limitation the rights to use, copy,\r
-* modify, merge, publish, distribute, sublicense, and/or sell copies\r
-* of the Software, and to permit persons to whom the Software is\r
-* furnished to do so, subject to the following conditions:\r
-*\r
-* The above copyright notice and this permission notice shall be\r
-* included in all copies or substantial portions of the Software.\r
-*\r
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
-* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
-* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
-* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
-* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
-* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
-* SOFTWARE.\r
-*/\r
-\r
-/* $Id$ */\r
-\r
-#include<khmapp.h>\r
-#include<crtdbg.h>\r
-\r
-ATOM khui_htwnd_cls;\r
-\r
-#define HTW_STYLE_NORMAL        0\r
-\r
-/* There are currently 4 style "bits" and 3 sizes, which means\r
-   there can be 2^4*3=48 possible styles max.  If someone is\r
-   feeling adventurous you can slightly improve performance of\r
-   the parser using this little fact.  For now, I don't care.\r
-   (hint: combine size and style bits to form a single number\r
-   and use it as an index into the styles array)\r
-*/\r
-#define HTW_STYLE_MAX           48\r
-\r
-#define HTW_FORMAT_MAX          128\r
-\r
-#define HTW_TAB_MAX             8\r
-\r
-#define HTW_DEFAULT             (-1)\r
-\r
-#define HTW_NORMAL_SIZE         8\r
-#define HTW_LARGE_SIZE          12\r
-#define HTW_HUGE_SIZE           20\r
-\r
-/* font variant */\r
-#define FV_ABSOLUTE     0x10000000\r
-\r
-#define FV_ITALIC       0x00000002\r
-#define FV_UNDERLINE    0x00000004\r
-#define FV_STRIKEOUT    0x00000008\r
-#define FV_BOLD         0x00000010\r
-\r
-#define FV_NOITALIC     0x00020000\r
-#define FV_NOUNDERLINE  0x00040000\r
-#define FV_NOSTRIKEOUT  0x00080000\r
-#define FV_NOBOLD       0x00100000\r
-\r
-#define FV_NONE         0x00000000\r
-#define FV_MASK         0x0000001f\r
-\r
-#define HTW_LINK_ALLOC     8\r
-\r
-#define ALIGN_LEFT      0\r
-#define ALIGN_CENTER    1\r
-#define ALIGN_RIGHT     2\r
-\r
-struct tx_tbl_t {\r
-    wchar_t *   string;\r
-    LONG        value;\r
-} \r
-\r
-htw_color_table[] = {\r
-    {L"black", RGB(0,0,0)},\r
-    {L"white", RGB(255,255,255)},\r
-    {L"red", RGB(255,0,0)},\r
-    {L"green", RGB(0,255,0)},\r
-    {L"blue", RGB(0,0,255)},\r
-    {L"grey", RGB(128,128,128)}\r
-},\r
-\r
-htw_size_table[] = {\r
-    {L"normal", HTW_NORMAL_SIZE},\r
-    {L"large", HTW_LARGE_SIZE},\r
-    {L"huge", HTW_HUGE_SIZE}\r
-},\r
-\r
-htw_align_table[] = {\r
-    {L"left", ALIGN_LEFT},\r
-    {L"center", ALIGN_CENTER},\r
-    {L"right", ALIGN_RIGHT}\r
-};\r
-\r
-typedef struct khui_htwnd_style_t {\r
-    LONG height;\r
-    LONG variation;     /* combination of FV_* */\r
-\r
-    HFONT font;\r
-} khui_htwnd_style;\r
-\r
-typedef struct khui_format_t {\r
-    int style_idx;\r
-    COLORREF color;\r
-} khui_format;\r
-\r
-typedef struct format_stack_t {\r
-    khui_format stack[HTW_FORMAT_MAX];\r
-    int stack_top;\r
-} format_stack;\r
-\r
-typedef struct khui_htwnd_data_t {\r
-    int id; /* control ID */\r
-    int flags;\r
-    wchar_t * text;\r
-    int scroll_left;\r
-    int scroll_top;\r
-    int ext_width;\r
-    int ext_height;\r
-    COLORREF bk_color;\r
-    HCURSOR hc_hand;\r
-    int l_pixel_y;\r
-\r
-    khui_htwnd_style styles[HTW_STYLE_MAX];\r
-    int n_styles;\r
-\r
-    khui_htwnd_link ** links;\r
-    int n_links;\r
-    int max_links;\r
-    int active_link;\r
-    int md_link;\r
-\r
-    int tabs[HTW_TAB_MAX];\r
-    int n_tabs;\r
-} khui_htwnd_data;\r
-\r
-static LONG table_lookup(struct tx_tbl_t * tbl, int n, wchar_t * v, int len)\r
-{\r
-    int i;\r
-\r
-    for(i=0; i<n; i++) {\r
-        if(!_wcsnicmp(tbl[i].string, v, len))\r
-            return tbl[i].value;\r
-    }\r
-\r
-    return -1;\r
-}\r
-\r
-static void clear_styles(khui_htwnd_data * d)\r
-{\r
-    int i;\r
-\r
-    for(i=0; i<d->n_styles; i++) {\r
-        if(d->styles[i].font != NULL) {\r
-            DeleteObject(d->styles[i].font);\r
-            d->styles[i].font = NULL;\r
-        }\r
-    }\r
-\r
-    d->n_styles = 0;\r
-}\r
-\r
-static void format_init(format_stack * s)\r
-{\r
-    s->stack_top = -1;\r
-    ZeroMemory(s->stack, sizeof(s->stack));\r
-}\r
-\r
-static khui_format * format_current(format_stack * s)\r
-{\r
-    if(s->stack_top >= 0)\r
-        return &(s->stack[s->stack_top]);\r
-    else\r
-        return NULL;\r
-}\r
-\r
-static int format_style(format_stack * s)\r
-{\r
-    if(s->stack_top >= 0)\r
-        return s->stack[s->stack_top].style_idx;\r
-    else\r
-        return 0;\r
-}\r
-\r
-static COLORREF format_color(format_stack * s)\r
-{\r
-    if(s->stack_top >= 0)\r
-        return s->stack[s->stack_top].color;\r
-    else\r
-        return 0;\r
-}\r
-\r
-static int format_level(format_stack * s)\r
-{\r
-    return s->stack_top;\r
-}\r
-\r
-static void format_unwind(format_stack * s, int level)\r
-{\r
-    s->stack_top = level;\r
-}\r
-\r
-static void format_push(format_stack * s, khui_htwnd_data * d, LONG height, LONG variation, COLORREF color)\r
-{\r
-    int i;\r
-    khui_format * top;\r
-    khui_htwnd_style * style;\r
-\r
-    _ASSERT(s->stack_top < (HTW_FORMAT_MAX-1));\r
-\r
-    /* formatting is additive unless FV_NORMAL is set in variation */\r
-    top = format_current(s);\r
-    if(top) {\r
-        style = &(d->styles[top->style_idx]);\r
-        if(height == HTW_DEFAULT)\r
-            height = style->height;\r
-\r
-        if(variation == HTW_DEFAULT)\r
-            variation = style->variation;\r
-        else if(!(variation & FV_ABSOLUTE))\r
-            variation |= style->variation;\r
-\r
-        if(color == HTW_DEFAULT)\r
-            color = top->color;\r
-    }\r
-\r
-    variation &= ~FV_ABSOLUTE;\r
-    variation ^= variation & (variation>>16);\r
-    variation &= FV_MASK;\r
-\r
-    /* now look for an existing style that matches the requested one */\r
-    for(i=0; i<d->n_styles; i++) {\r
-        style = &(d->styles[i]);\r
-\r
-        if(style->height == height &&\r
-            style->variation == variation)\r
-            break;\r
-    }\r
-\r
-    s->stack_top++;\r
-\r
-    if(i<d->n_styles) {\r
-        s->stack[s->stack_top].style_idx = i;\r
-    } else {\r
-        if(d->n_styles == HTW_STYLE_MAX) {\r
-            s->stack[s->stack_top].style_idx = 0;\r
-        } else {\r
-            s->stack[s->stack_top].style_idx = d->n_styles;\r
-            d->styles[d->n_styles].font = NULL;\r
-            d->styles[d->n_styles].height = height;\r
-            d->styles[d->n_styles].variation = variation;\r
-            d->n_styles++;\r
-        }\r
-    }\r
-    s->stack[s->stack_top].color = color;\r
-}\r
-\r
-static void format_pop(format_stack * s) {\r
-    if(s->stack_top >= 0)\r
-        s->stack_top--;\r
-}\r
-\r
-static wchar_t * token_end(wchar_t * s) {\r
-    while(iswalnum(*s) || *s == L'/')\r
-        s++;\r
-    return s;\r
-}\r
-\r
-static wchar_t * skip_ws(wchar_t * s) {\r
-    while(iswspace(*s))\r
-        s++;\r
-    return s;\r
-}\r
-\r
-/* s points to something like " = \"value\"" \r
-   start and len will point to the start and\r
-   length of value.  return value will point to the\r
-   character following the last double quote. */\r
-static wchar_t * read_attr(wchar_t * s, wchar_t ** start, int * len)\r
-{\r
-    wchar_t *e;\r
-\r
-    *start = NULL;\r
-    *len = 0;\r
-\r
-    do {\r
-        s = skip_ws(s);\r
-        if(*s != L'=')\r
-            break;\r
-        s = skip_ws(++s);\r
-        if(*s != L'"')\r
-            break;\r
-        e = wcschr(++s, L'"');\r
-        if(!e)\r
-            break;\r
-\r
-        *start = s;\r
-        *len = (int) (e - s);\r
-\r
-        s = e + 1;\r
-    } while(FALSE);\r
-\r
-    return s;\r
-}\r
-\r
-/*\r
-We currently support the following tags:\r
-\r
-<a [id="string"] [param="paramstring"]>link text</a>\r
-<b>foo</b>\r
-<u>foo</u>\r
-<i>foo</i>\r
-\r
-<font [color="(color)"] [size="normal|large|huge|(point size)"]>foo</font>\r
-   (color)=black|white|red|green|blue|grey\r
-<large>foo</large>\r
-<huge>foo</huge>\r
-\r
-<center>foo</center>\r
-<left>foo</left>\r
-<right>foo</right>\r
-\r
-<p [align="left|center|right"]>foo</p>\r
-<settab pos="(pos)">\r
-<tab>\r
-*/\r
-\r
-static int htw_parse_tag(\r
-    wchar_t * start, \r
-    wchar_t ** end, \r
-    int * align, \r
-    khui_htwnd_data * d, \r
-    format_stack * s, \r
-    PPOINT p_abs,\r
-    PPOINT p_rel,\r
-    int lh, \r
-    BOOL dry_run)\r
-{\r
-    wchar_t * c;\r
-    int n = 0;\r
-\r
-    /* start initially points to the starting '<' */\r
-    c = token_end(++start);\r
-\r
-    if(!_wcsnicmp(start,L"a",c-start)) {\r
-        /* start of an 'a' tag */\r
-        wchar_t * id_start = NULL;\r
-        int id_len = 0;\r
-        wchar_t * param_start = NULL;\r
-        int param_len = 0;\r
-\r
-        /* We don't need to parse the link\r
-           if it is just a dry run */\r
-        if(dry_run) {\r
-            format_push(s, d, HTW_DEFAULT, HTW_DEFAULT, RGB(0,0,255));\r
-            *end = wcschr(start, L'>');\r
-            return FALSE;\r
-        }\r
-\r
-        while(c && *c && *c != L'>') {\r
-            wchar_t * e;\r
-\r
-            c = skip_ws(c);\r
-            e = token_end(c);\r
-\r
-            if(c==e)\r
-                break;\r
-\r
-            if(!_wcsnicmp(c,L"id",e-c)) {\r
-                c = read_attr(e, &id_start, &id_len);\r
-            } else if(!_wcsnicmp(c,L"param",e-c)) {\r
-                c = read_attr(e, &param_start, &param_len);\r
-            }\r
-        }\r
-\r
-        if(d->active_link == d->n_links)\r
-            format_push(s,d, HTW_DEFAULT, FV_UNDERLINE, RGB(0,0,255));\r
-        else\r
-            format_push(s,d, HTW_DEFAULT, FV_NONE, RGB(0,0,255));\r
-\r
-        {\r
-            khui_htwnd_link * l;\r
-\r
-            if(!d->links) {\r
-                d->links = PMALLOC(sizeof(khui_htwnd_link *) * HTW_LINK_ALLOC);\r
-                ZeroMemory(d->links, sizeof(khui_htwnd_link *) * HTW_LINK_ALLOC);\r
-                d->max_links = HTW_LINK_ALLOC;\r
-                d->n_links = 0;\r
-            }\r
-\r
-            if(d->n_links >= d->max_links) {\r
-                khui_htwnd_link ** ll;\r
-                int n_new;\r
-\r
-                n_new = UBOUNDSS(d->n_links + 1, HTW_LINK_ALLOC, HTW_LINK_ALLOC);\r
-\r
-                ll = PMALLOC(sizeof(khui_htwnd_link *) * n_new);\r
-                ZeroMemory(ll, sizeof(khui_htwnd_link *) * n_new);\r
-                memcpy(ll, d->links, sizeof(khui_htwnd_link *) * d->max_links);\r
-                PFREE(d->links);\r
-                d->links = ll;\r
-                d->max_links = n_new;\r
-            }\r
-\r
-            l = d->links[d->n_links];\r
-            if(!l) {\r
-                l = PMALLOC(sizeof(khui_htwnd_link));\r
-                d->links[d->n_links] = l;\r
-            }\r
-\r
-            l->id = id_start;\r
-            l->id_len = id_len;\r
-            l->param = param_start;\r
-            l->param_len = param_len;\r
-\r
-            l->r.left = p_abs->x;\r
-            l->r.top = p_abs->y;\r
-\r
-            d->n_links++;\r
-        }\r
-\r
-    } else if(!_wcsnicmp(start, L"/a", c - start)) {\r
-        khui_htwnd_link * l;\r
-\r
-        c = wcschr(c,L'>');\r
-        if(!c)\r
-            c = c + wcslen(c);\r
-\r
-        format_pop(s);\r
-\r
-        if(!dry_run) {\r
-            l = d->links[d->n_links - 1]; /* last link */\r
-            l->r.right = p_abs->x;\r
-            l->r.bottom = p_abs->y + lh;\r
-        }\r
-    } else if(!_wcsnicmp(start, L"p", c - start)) {\r
-        wchar_t * e;\r
-        wchar_t * align_s = NULL;\r
-        int align_len = 0;\r
-\r
-        c = skip_ws(c);\r
-        e = token_end(c);\r
-\r
-        if(c != e && !_wcsnicmp(c,L"align",e-c)) {\r
-            c = read_attr(e, &align_s, &align_len);\r
-        }\r
-\r
-        c = wcschr(c, L'>');\r
-        if(!c)\r
-            c = c + wcslen(c);\r
-        \r
-\r
-        if(align_s)\r
-            *align = table_lookup(htw_align_table, ARRAYLENGTH(htw_align_table), align_s, align_len);\r
-        else\r
-            *align = ALIGN_LEFT;\r
-\r
-        n = 1;\r
-    } else if(!_wcsnicmp(start, L"b", c - start)) {\r
-        format_push(s,d, HTW_DEFAULT, FV_BOLD, HTW_DEFAULT);\r
-    } else if(!_wcsnicmp(start, L"/b", c - start)) {\r
-        format_pop(s);\r
-    } else if(!_wcsnicmp(start, L"u", c - start)) {\r
-        format_push(s,d, HTW_DEFAULT, FV_UNDERLINE, HTW_DEFAULT);\r
-    } else if(!_wcsnicmp(start, L"/u", c - start)) {\r
-        format_pop(s);\r
-    } else if(!_wcsnicmp(start, L"i", c - start)) {\r
-        format_push(s,d, HTW_DEFAULT, FV_ITALIC, HTW_DEFAULT);\r
-    } else if(!_wcsnicmp(start, L"/i", c - start)) {\r
-        format_pop(s);\r
-    } else if(!_wcsnicmp(start, L"large", c - start)) {\r
-        format_push(s,d,-MulDiv(HTW_LARGE_SIZE, d->l_pixel_y, 72), HTW_DEFAULT, HTW_DEFAULT);\r
-    } else if(!_wcsnicmp(start, L"/large", c - start)) {\r
-        format_pop(s);\r
-    } else if(!_wcsnicmp(start, L"huge", c - start)) {\r
-        format_push(s,d,-MulDiv(HTW_HUGE_SIZE, d->l_pixel_y, 72), HTW_DEFAULT, HTW_DEFAULT);\r
-    } else if(!_wcsnicmp(start, L"/huge", c - start)) {\r
-        format_pop(s);\r
-    } else if(!_wcsnicmp(start, L"center", c - start)) {\r
-        c = wcschr(c, L'>');\r
-        if(!c)\r
-            c = c + wcslen(c);\r
-        *align = ALIGN_CENTER;\r
-        n = 1;\r
-    } else if(!_wcsnicmp(start, L"left", c - start) ||\r
-        !_wcsnicmp(start, L"p", c - start)) \r
-    {\r
-        c = wcschr(c, L'>');\r
-        if(!c)\r
-            c = c + wcslen(c);\r
-        *align = ALIGN_LEFT;\r
-        n = 1;\r
-    } else if(!_wcsnicmp(start, L"right", c - start)) {\r
-        c = wcschr(c, L'>');\r
-        if(!c)\r
-            c = c + wcslen(c);\r
-        *align = ALIGN_RIGHT;\r
-        n = 1;\r
-    } else if(!_wcsnicmp(start, L"/center", c - start) ||\r
-              !_wcsnicmp(start, L"/left", c - start) ||\r
-              !_wcsnicmp(start, L"/right", c - start) ||\r
-              !_wcsnicmp(start, L"/p", c - start)) {\r
-        c = wcschr(c, L'>');\r
-        if(!c)\r
-            c = c + wcslen(c);\r
-        *align = ALIGN_LEFT;\r
-        n = 1;\r
-    } else if(!_wcsnicmp(start, L"font", c - start)) {\r
-        wchar_t * color_s = NULL;\r
-        int color_len = 0;\r
-        wchar_t * size_s = NULL;\r
-        int size_len = 0;\r
-        LONG color = HTW_DEFAULT;\r
-        LONG h = HTW_DEFAULT;\r
-\r
-        while(c && *c && *c != L'>') {\r
-            wchar_t * e;\r
-\r
-            c = skip_ws(c);\r
-            e = token_end(c);\r
-\r
-            if(c==e)\r
-                break;\r
-\r
-            if(!_wcsnicmp(c,L"color",e-c)) {\r
-                c = read_attr(e, &color_s, &color_len);\r
-            } else if(!_wcsnicmp(c,L"size",e-c)) {\r
-                c = read_attr(e, &size_s, &size_len);\r
-            }\r
-        }\r
-\r
-        if(color_s)\r
-            color = table_lookup(htw_color_table, ARRAYLENGTH(htw_color_table), color_s, color_len);\r
-        if(size_s) {\r
-            h = table_lookup(htw_size_table, ARRAYLENGTH(htw_size_table), size_s, size_len);\r
-            if(h)\r
-                h = -MulDiv(h, d->l_pixel_y, 72);\r
-            else\r
-                h = -MulDiv(HTW_NORMAL_SIZE, d->l_pixel_y, 72);\r
-        }\r
-\r
-        format_push(s,d,h,HTW_DEFAULT,color);\r
-    } else if(!_wcsnicmp(start, L"/font", c - start)) {\r
-        format_pop(s);\r
-    } else if(!_wcsnicmp(start, L"settab", c - start)) {\r
-        wchar_t * e;\r
-        wchar_t * pos_s = NULL;\r
-        int pos_len;\r
-\r
-        c = skip_ws(c);\r
-        e = token_end(c);\r
-\r
-        if(c != e && !_wcsnicmp(c,L"pos",e-c)) {\r
-            c = read_attr(e, &pos_s, &pos_len);\r
-        }\r
-\r
-        c = wcschr(c, L'>');\r
-        if(!c)\r
-            c = c + wcslen(c);\r
-\r
-        if(pos_s && d->n_tabs < HTW_TAB_MAX && !dry_run) {\r
-            wchar_t * dummy;\r
-            LONG bu;\r
-            int bx;\r
-            int dx;\r
-\r
-            bu = GetDialogBaseUnits();\r
-            bx = LOWORD(bu);\r
-\r
-            dx = wcstol(pos_s, &dummy, 10);\r
-\r
-            d->tabs[d->n_tabs++] = MulDiv(dx, bx, 4);\r
-        }\r
-    } else if(!_wcsnicmp(start, L"tab", c - start)) {\r
-        int i;\r
-\r
-        if(!dry_run) {\r
-            for(i=0; i < d->n_tabs; i++) {\r
-                if(d->tabs[i] > p_rel->x) {\r
-                    p_rel->x = d->tabs[i];\r
-                    break;\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-    if(*c)\r
-        c++;\r
-    *end = c;\r
-\r
-    return n;\r
-}\r
-\r
-static void htw_assert_style(HDC hdc, khui_htwnd_data * d, int style)\r
-{\r
-    LOGFONT lf;\r
-\r
-    if(d->styles[style].font)\r
-        return;\r
-\r
-    /*TODO: we need select different fonts depending on system locale */\r
-    lf.lfHeight = d->styles[style].height; //-MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72);\r
-    lf.lfWidth = 0;\r
-    lf.lfEscapement = 0;\r
-    lf.lfOrientation = 0;\r
-    lf.lfWeight = (d->styles[style].variation & FV_BOLD)? FW_BOLD: FW_NORMAL;\r
-    lf.lfItalic = !!(d->styles[style].variation & FV_ITALIC);\r
-    lf.lfUnderline = !!(d->styles[style].variation & FV_UNDERLINE);\r
-    lf.lfStrikeOut = !!(d->styles[style].variation & FV_STRIKEOUT);\r
-    lf.lfCharSet = DEFAULT_CHARSET;\r
-    lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
-    lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
-    lf.lfQuality = DEFAULT_QUALITY;\r
-    lf.lfPitchAndFamily = DEFAULT_PITCH;\r
-\r
-    LoadString(khm_hInstance, IDS_DEFAULT_FONT, lf.lfFaceName, ARRAYLENGTH(lf.lfFaceName));\r
-\r
-    d->styles[style].font = CreateFontIndirect(&lf);\r
-}\r
-\r
-static LRESULT htw_paint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
-{\r
-    PAINTSTRUCT ps;\r
-    HBRUSH hbk;\r
-    khui_htwnd_data * d;\r
-    RECT r;\r
-    SIZE s;\r
-    HDC hdc;\r
-    wchar_t * text;\r
-    format_stack s_stack;\r
-\r
-    int align;\r
-    int y;\r
-    wchar_t * par_start;\r
-    int ext_width = 0;\r
-    int ext_height = 0;\r
-\r
-    d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-\r
-    if(!GetUpdateRect(hwnd, &r, !(d->flags & KHUI_HTWND_TRANSPARENT)))\r
-        return 0;\r
-\r
-    if(d->text == NULL)\r
-        return 0;\r
-\r
-    text = d->text;\r
-\r
-    hdc = BeginPaint(hwnd, &ps);\r
-\r
-    GetClientRect(hwnd, &r);\r
-\r
-#ifdef DRAW_HTWND_CLIENT_EDGE\r
-    /* for the moment, we are skipping on the client edge. */\r
-    if(d->flags & KHUI_HTWND_CLIENTEDGE)\r
-        DrawEdge(hdc, &r, EDGE_SUNKEN, BF_ADJUST | BF_RECT | BF_FLAT);\r
-#endif\r
-\r
-    hbk = GetSysColorBrush(COLOR_WINDOW);\r
-    FillRect(hdc, &r, hbk);\r
-    hbk = NULL;                 /* We don't need to destroy system\r
-                                   brushes */\r
-\r
-    /* push the default format */\r
-    format_init(&s_stack);\r
-\r
-    d->l_pixel_y = GetDeviceCaps(hdc, LOGPIXELSY);\r
-    format_push(&s_stack,d, -MulDiv(HTW_NORMAL_SIZE, d->l_pixel_y, 72), FV_NONE, RGB(0,0,0));\r
-\r
-    y = r.top - d->scroll_top;\r
-\r
-    par_start = text;\r
-\r
-    align = ALIGN_LEFT;\r
-\r
-    SetTextAlign(hdc, TA_TOP | TA_LEFT | TA_NOUPDATECP);\r
-    if(d->flags & KHUI_HTWND_TRANSPARENT)\r
-        SetBkMode(hdc, TRANSPARENT);\r
-\r
-    d->n_links = 0;\r
-    d->n_tabs = 0;\r
-\r
-    while(*par_start) {\r
-        wchar_t * p = par_start;\r
-        wchar_t * c = NULL;\r
-        int p_width = 0;\r
-        int s_start;\r
-        int l_height = 0;\r
-        int x = 0;\r
-        POINT pt;\r
-        POINT pt_rel;\r
-\r
-        s_start = format_level(&s_stack);\r
-\r
-        /* begin dry run */\r
-        while(*p) {\r
-            if(*p == L'<') {\r
-                int talign = -1;\r
-                int n = htw_parse_tag(p,&c,&talign,d,&s_stack,NULL,NULL,0,TRUE);\r
-\r
-                if(n && p_width)\r
-                    break;\r
-\r
-                p = c;\r
-\r
-                if(n && talign >= 0)\r
-                    align = talign;\r
-            } else {\r
-                HFONT hfold;\r
-                c = wcschr(p, L'<');\r
-                if(!c)\r
-                    c = p + wcslen(p);\r
-\r
-                htw_assert_style(hdc, d, format_style(&s_stack));\r
-                hfold = SelectFont(hdc, d->styles[format_style(&s_stack)].font);\r
-                GetTextExtentPoint32(hdc, p, (int)(c - p), &s);\r
-                SelectFont(hdc, hfold);\r
-\r
-                p_width += s.cx;\r
-                if(s.cy > l_height)\r
-                    l_height = s.cy;\r
-\r
-                p = c;\r
-            }\r
-        }\r
-\r
-        /* dry run ends */\r
-\r
-        x = r.left - d->scroll_left;\r
-\r
-        if(align == ALIGN_CENTER) {\r
-            if (r.right - r.left > p_width)\r
-                x += (r.right - r.left)/2 - p_width / 2;\r
-        }\r
-\r
-        else if(align == ALIGN_RIGHT) {\r
-            if (r.right - r.left > p_width)\r
-                x += (r.right - r.left) - p_width;\r
-        }\r
-\r
-        /* begin wet run */\r
-        p = par_start;\r
-        format_unwind(&s_stack, s_start); /* unwind format stack */\r
-\r
-        p_width = 0;\r
-\r
-        while(p && *p) {\r
-            if(*p == L'<') {\r
-                int talign = -1;\r
-                int n;\r
-\r
-                pt.x = x + p_width;\r
-                pt.y = y;\r
-                pt_rel.x = p_width;\r
-                pt_rel.y = 0;\r
-\r
-                n = htw_parse_tag(p, &c, &talign, d, &s_stack, &pt, &pt_rel, l_height, FALSE);\r
-\r
-                if(n && p_width) {\r
-                    break;\r
-                }\r
-\r
-                p_width = pt_rel.x;\r
-\r
-                p = c;\r
-                if(n && talign >= 0)\r
-                    align = talign;\r
-            } else {\r
-                HFONT hfold;\r
-                RECT rd;\r
-\r
-                c = wcschr(p, L'<');\r
-                if(!c)\r
-                    c = p + wcslen(p);\r
-\r
-                htw_assert_style(hdc, d, format_style(&s_stack));\r
-                hfold = SelectFont(hdc, d->styles[format_style(&s_stack)].font);\r
-                SetTextColor(hdc, format_color(&s_stack));\r
-\r
-                GetTextExtentPoint32(hdc, p, (int)(c - p), &s);\r
-                rd.left = x + p_width;\r
-                rd.top = y;\r
-                rd.right = rd.left + s.cx;\r
-                rd.bottom = rd.top + l_height;\r
-\r
-                DrawText(hdc, p, (int)(c - p), &rd,\r
-                         DT_BOTTOM | DT_LEFT | DT_SINGLELINE | DT_NOPREFIX);\r
-\r
-                p_width += s.cx;\r
-\r
-                SelectFont(hdc, hfold);\r
-                p = c;\r
-            }\r
-        }\r
-\r
-        if (p_width > ext_width)\r
-            ext_width = p_width;\r
-\r
-        y += l_height;\r
-        par_start = p;\r
-    }\r
-\r
-    if (y > ext_height)\r
-        ext_height = y;\r
-\r
-    EndPaint(hwnd, &ps);\r
-\r
-    if (d->ext_width < ext_width ||\r
-        d->ext_height < ext_height) {\r
-        SCROLLINFO si;\r
-        LONG l;\r
-\r
-        /* the extents need to be adjusted.  But first check if we\r
-           have exactly the right scroll bars we need. */\r
-        if ((ext_width > (r.right - r.left) &&\r
-             !(d->flags & KHUI_HTWND_HSCROLL)) ||\r
-            (ext_height > (r.bottom - r.top) &&\r
-             !(d->flags & KHUI_HTWND_VSCROLL)) ||\r
-\r
-            (ext_width <= (r.right - r.left) &&\r
-             (d->flags & KHUI_HTWND_HSCROLL)) ||\r
-            (ext_height <= (r.bottom - r.top) &&\r
-             (d->flags & KHUI_HTWND_VSCROLL))) {\r
-\r
-            /* need to add scroll bars */\r
-            if (ext_width > (r.right - r.left))\r
-                d->flags |= KHUI_HTWND_HSCROLL;\r
-            else\r
-                d->flags &= ~KHUI_HTWND_HSCROLL;\r
-\r
-            if (ext_height > (r.bottom - r.top))\r
-                d->flags |= KHUI_HTWND_VSCROLL;\r
-            else\r
-                d->flags &= ~KHUI_HTWND_VSCROLL;\r
-\r
-            l = GetWindowLongPtr(hwnd, GWL_STYLE);\r
-            l &= ~(WS_HSCROLL | WS_VSCROLL);\r
-\r
-            l |= ((d->flags & KHUI_HTWND_HSCROLL) ? WS_HSCROLL : 0) |\r
-                ((d->flags & KHUI_HTWND_VSCROLL) ? WS_VSCROLL : 0);\r
-\r
-            SetWindowLongPtr(hwnd, GWL_STYLE, l);\r
-\r
-            InvalidateRect(hwnd, NULL, FALSE);\r
-            /* since the client area changed, we do another redraw\r
-               before updating the scroll bar positions. */\r
-        } else {\r
-            d->ext_width = ext_width;\r
-            d->ext_height = ext_height;\r
-\r
-            if (d->flags & KHUI_HTWND_HSCROLL) {\r
-                ZeroMemory(&si, sizeof(si));\r
-                si.cbSize = sizeof(si);\r
-                si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;\r
-                si.nMin = 0;\r
-                si.nMax = ext_width;\r
-                si.nPage = r.right - r.left;\r
-                si.nPos = d->scroll_left;\r
-\r
-                SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);\r
-            }\r
-\r
-            if (d->flags & KHUI_HTWND_VSCROLL) {\r
-                ZeroMemory(&si, sizeof(si));\r
-                si.cbSize = sizeof(si);\r
-                si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;\r
-                si.nMin = 0;\r
-                si.nMax = ext_height;\r
-                si.nPage = r.bottom - r.top;\r
-                si.nPos = d->scroll_top;\r
-\r
-                SetScrollInfo(hwnd, SB_VERT, &si, TRUE);\r
-            }\r
-        }\r
-    }\r
-\r
-    return 0;\r
-}\r
-\r
-LRESULT CALLBACK khui_htwnd_proc(HWND hwnd,\r
-                                 UINT uMsg,\r
-                                 WPARAM wParam,\r
-                                 LPARAM lParam\r
-                                 )\r
-{\r
-    switch(uMsg) {\r
-    case WM_CREATE:\r
-        {\r
-            CREATESTRUCT * cs;\r
-            khui_htwnd_data * d;\r
-            size_t cbsize;\r
-\r
-            cs = (CREATESTRUCT *) lParam;\r
-\r
-            d = PMALLOC(sizeof(*d));\r
-            ZeroMemory(d, sizeof(*d));\r
-\r
-            if(cs->dwExStyle & WS_EX_TRANSPARENT) {\r
-                d->flags |= KHUI_HTWND_TRANSPARENT;\r
-            }\r
-            if(cs->dwExStyle & WS_EX_CLIENTEDGE) {\r
-                d->flags |= KHUI_HTWND_CLIENTEDGE;\r
-            }\r
-            if(cs->style & WS_HSCROLL) {\r
-                d->flags |= KHUI_HTWND_HSCROLL;\r
-            }\r
-            if(cs->style & WS_VSCROLL) {\r
-                d->flags |= KHUI_HTWND_VSCROLL;\r
-            }\r
-            d->id = (int)(INT_PTR) cs->hMenu;\r
-\r
-            d->active_link = -1;\r
-            d->bk_color = RGB(255,255,255);\r
-            d->hc_hand = LoadCursor(NULL, IDC_HAND);\r
-\r
-            if(SUCCEEDED(StringCbLength(cs->lpszName, KHUI_HTWND_MAXCB_TEXT, &cbsize))) {\r
-                cbsize += sizeof(wchar_t);\r
-                d->text = PMALLOC(cbsize);\r
-                StringCbCopy(d->text, cbsize, cs->lpszName);\r
-            }\r
-\r
-            /* this is just a flag to the WM_PAINT handler that the\r
-               extents haven't been set yet. */\r
-            d->ext_width = -1;\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-            SetWindowLongPtr(hwnd, 0, (LONG_PTR) d);\r
-#pragma warning(pop)\r
-\r
-            return 0;\r
-        }\r
-        break;\r
-\r
-    case WM_SETTEXT:\r
-        {\r
-            wchar_t * newtext;\r
-            size_t cbsize;\r
-            khui_htwnd_data * d;\r
-            BOOL rv;\r
-\r
-            d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-            newtext = (wchar_t *) lParam;\r
-\r
-            if(d->text) {\r
-                PFREE(d->text);\r
-                d->text = NULL;\r
-            }\r
-\r
-            if(SUCCEEDED(StringCbLength(newtext, KHUI_HTWND_MAXCB_TEXT, &cbsize))) {\r
-                cbsize += sizeof(wchar_t);\r
-                d->text = PMALLOC(cbsize);\r
-                StringCbCopy(d->text, cbsize, newtext);\r
-                rv = TRUE;\r
-            } else\r
-                rv = FALSE;\r
-\r
-            clear_styles(d);\r
-\r
-            d->ext_width = -1;\r
-            d->scroll_left = 0;\r
-            d->scroll_top = 0;\r
-\r
-            InvalidateRect(hwnd, NULL, TRUE);\r
-\r
-            return rv;\r
-        }\r
-        break;\r
-\r
-    case WM_DESTROY:\r
-        {\r
-            khui_htwnd_data * d;\r
-            int i;\r
-\r
-            d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-            if(d->text)\r
-                PFREE(d->text);\r
-            d->text = 0;\r
-\r
-            if(d->links) {\r
-                for(i=0;i<d->max_links;i++) {\r
-                    if(d->links[i])\r
-                        PFREE(d->links[i]);\r
-                }\r
-                PFREE(d->links);\r
-            }\r
-\r
-            clear_styles(d);\r
-\r
-            PFREE(d);\r
-        }\r
-        break;\r
-\r
-    case WM_ERASEBKGND:\r
-        {\r
-            HDC hdc = (HDC) wParam;\r
-            khui_htwnd_data * d;\r
-            HBRUSH hbr;\r
-            RECT r;\r
-\r
-            d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-\r
-            GetClientRect(hwnd, &r);\r
-            hbr = GetSysColorBrush(COLOR_WINDOW);\r
-            FillRect(hdc, &r, hbr);\r
-\r
-            /* no need to destroy the brush since it's a system\r
-               brush. */\r
-\r
-            return TRUE;\r
-        }\r
-\r
-    case WM_SIZE:\r
-        {\r
-            khui_htwnd_data * d;\r
-\r
-            d = (khui_htwnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-\r
-            if (d) {\r
-                d->ext_width = 0;\r
-                d->ext_height = 0;\r
-            }\r
-        }\r
-        return 0;\r
-\r
-    case WM_PAINT:\r
-        htw_paint(hwnd, uMsg, wParam, lParam);\r
-        return 0;\r
-\r
-    case WM_SETCURSOR:\r
-        {\r
-            khui_htwnd_data * d;\r
-\r
-            if(hwnd != (HWND)wParam)\r
-                break;\r
-                \r
-            d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-\r
-            if(d->active_link >= 0) {\r
-                SetCursor(d->hc_hand);\r
-                return TRUE;\r
-            }\r
-        }\r
-        break;\r
-\r
-    case WM_SETFOCUS:\r
-        {\r
-            khui_htwnd_data * d;\r
-\r
-            d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-\r
-            d->flags |= KHUI_HTWND_FOCUS;\r
-\r
-            InvalidateRect(hwnd, NULL, TRUE);\r
-        }\r
-        break;\r
-\r
-    case WM_KILLFOCUS:\r
-        {\r
-            khui_htwnd_data * d;\r
-\r
-            d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-\r
-            d->flags &= ~KHUI_HTWND_FOCUS;\r
-\r
-            InvalidateRect(hwnd, NULL, TRUE);\r
-        }\r
-        break;\r
-\r
-    case WM_LBUTTONDOWN:\r
-        {\r
-            khui_htwnd_data * d;\r
-\r
-            d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-\r
-            d->md_link = d->active_link;\r
-\r
-            SetCapture(hwnd);\r
-        }\r
-        break;\r
-\r
-    case WM_LBUTTONUP:\r
-        {\r
-            khui_htwnd_data * d;\r
-\r
-            d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-\r
-            if(d->md_link == d->active_link && d->md_link >= 0) {\r
-                /* clicked */\r
-                SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(d->id, BN_CLICKED), (LPARAM) d->links[d->md_link]);\r
-            }\r
-\r
-            ReleaseCapture();\r
-        }\r
-        break;\r
-\r
-    case WM_HSCROLL:\r
-        {\r
-            khui_htwnd_data * d;\r
-            int old_pos;\r
-            int new_pos;\r
-            int ext;\r
-            SCROLLINFO si;\r
-            RECT r;\r
-\r
-            d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-\r
-            old_pos = new_pos = d->scroll_left;\r
-            ext = d->ext_width;\r
-\r
-            switch(LOWORD(wParam)) {\r
-            case SB_THUMBPOSITION:\r
-            case SB_ENDSCROLL:\r
-                ZeroMemory(&si, sizeof(si));\r
-                si.cbSize = sizeof(si);\r
-                si.fMask = SIF_POS;\r
-                GetScrollInfo(hwnd, SB_HORZ, &si);\r
-                new_pos = si.nPos;\r
-                break;\r
-\r
-            case SB_THUMBTRACK:\r
-                ZeroMemory(&si, sizeof(si));\r
-                si.cbSize = sizeof(si);\r
-                si.fMask = SIF_TRACKPOS;\r
-                GetScrollInfo(hwnd, SB_HORZ, &si);\r
-                new_pos = si.nTrackPos;\r
-                break;\r
-\r
-            case SB_LINELEFT:\r
-                new_pos -= ext / 12; /* arbitrary unit */\r
-                break;\r
-\r
-            case SB_LINERIGHT:\r
-                new_pos += ext / 12; /* arbitrary unit */\r
-                break;\r
-\r
-            case SB_PAGELEFT:\r
-                GetClientRect(hwnd, &r);\r
-                new_pos -= r.right - r.left;\r
-                break;\r
-\r
-            case SB_PAGERIGHT:\r
-                GetClientRect(hwnd, &r);\r
-                new_pos += r.right - r.left;\r
-                break;\r
-            }\r
-\r
-            if (new_pos == old_pos)\r
-                break;\r
-\r
-            GetClientRect(hwnd, &r);\r
-\r
-            if (new_pos > ext - (r.right - r.left))\r
-                new_pos = ext - (r.right - r.left);\r
-\r
-            if (new_pos < 0)\r
-                new_pos = 0;\r
-\r
-            if (new_pos == old_pos)\r
-                break;\r
-\r
-            ZeroMemory(&si, sizeof(si));\r
-            si.cbSize = sizeof(si);\r
-            si.fMask = SIF_POS;\r
-            si.nPos = new_pos;\r
-            SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);\r
-            /* note that Windows sometimes adjusts the position after\r
-               setting it with SetScrollInfo.  We have to look it up\r
-               again to see what value it ended up at. */\r
-            GetScrollInfo(hwnd, SB_HORZ, &si);\r
-            new_pos = si.nPos;\r
-\r
-            if (new_pos == old_pos)\r
-                break;\r
-\r
-            d->scroll_left = new_pos;\r
-\r
-            ScrollWindow(hwnd, old_pos - new_pos, 0, NULL, NULL);\r
-\r
-            return 0;\r
-        }\r
-        break;\r
-\r
-    case WM_MOUSEMOVE:\r
-        {\r
-            khui_htwnd_data * d;\r
-            int i;\r
-            POINT p;\r
-            int nl;\r
-\r
-            d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-            p.x = GET_X_LPARAM(lParam) + d->scroll_left;\r
-            p.y = GET_Y_LPARAM(lParam) + d->scroll_top;\r
-                \r
-            for(i=0; i<d->n_links; i++) {\r
-                if(d->links && d->links[i] && PtInRect(&(d->links[i]->r), p))\r
-                    break;\r
-            }\r
-\r
-            if(i == d->n_links)\r
-                nl = -1;\r
-            else\r
-                nl = i;\r
-\r
-            if(d->active_link != nl) {\r
-                if(d->active_link >= 0) {\r
-                    if(d->flags & KHUI_HTWND_TRANSPARENT) {\r
-                        HWND parent = GetParent(hwnd);\r
-                        if(parent) {\r
-                            RECT rdest = d->links[d->active_link]->r;\r
-\r
-                            MapWindowPoints(hwnd, parent, (LPPOINT) &rdest, 2);\r
-                            InvalidateRect(parent, &rdest, TRUE);\r
-                        }\r
-                    }\r
-                    /* although we are invalidating the rect before setting active_link,\r
-                       WM_PAINT will not be issued until wndproc returns */\r
-                    InvalidateRect(hwnd, &(d->links[d->active_link]->r), TRUE);\r
-                }\r
-                d->active_link = nl;\r
-                if(d->active_link >= 0) {\r
-                    /* although we are invalidating the rect before setting active_link,\r
-                       WM_PAINT will not be issued until wndproc returns */\r
-                    if(d->flags & KHUI_HTWND_TRANSPARENT) {\r
-                        HWND parent = GetParent(hwnd);\r
-                        if(parent) {\r
-                            RECT rdest = d->links[d->active_link]->r;\r
-\r
-                            MapWindowPoints(hwnd, parent, (LPPOINT) &rdest, 2);\r
-                            InvalidateRect(parent, &rdest, TRUE);\r
-                        }\r
-                    }\r
-                    InvalidateRect(hwnd, &(d->links[d->active_link]->r), TRUE);\r
-                }\r
-            }\r
-        }\r
-        break;\r
-    }\r
-\r
-    return DefWindowProc(hwnd, uMsg,wParam,lParam);\r
-}\r
-\r
-void khm_register_htwnd_class(void)\r
-{\r
-    WNDCLASSEX wcx;\r
-\r
-    wcx.cbSize = sizeof(wcx);\r
-    wcx.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;\r
-    wcx.lpfnWndProc = khui_htwnd_proc;\r
-    wcx.cbClsExtra = 0;\r
-    wcx.cbWndExtra = sizeof(LONG_PTR);\r
-    wcx.hInstance = khm_hInstance;\r
-    wcx.hIcon = NULL;\r
-    wcx.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);\r
-    wcx.hbrBackground = CreateSolidBrush(RGB(255,255,255));\r
-    wcx.lpszMenuName = NULL;\r
-    wcx.lpszClassName = KHUI_HTWND_CLASS;\r
-    wcx.hIconSm = NULL;\r
-\r
-    khui_htwnd_cls = RegisterClassEx(&wcx);\r
-}\r
-\r
-void khm_unregister_htwnd_class(void)\r
-{\r
-    UnregisterClass(MAKEINTATOM(khui_htwnd_cls), khm_hInstance);\r
-}\r
-\r
-HWND khm_create_htwnd(HWND parent, LPWSTR text, int x, int y, int width, int height, DWORD ex_style, DWORD style)\r
-{\r
-\r
-    return CreateWindowEx(\r
-        ex_style,\r
-        MAKEINTATOM(khui_htwnd_cls),\r
-        text,\r
-        style | WS_CHILD,\r
-        x,y,width,height,\r
-        parent,\r
-        (HMENU) KHUI_HTWND_CTLID,\r
-        khm_hInstance,\r
-        NULL);\r
-}\r
+/*
+* Copyright (c) 2005 Massachusetts Institute of Technology
+*
+* Permission is hereby granted, free of charge, to any person
+* obtaining a copy of this software and associated documentation
+* files (the "Software"), to deal in the Software without
+* restriction, including without limitation the rights to use, copy,
+* modify, merge, publish, distribute, sublicense, and/or sell copies
+* of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+* ACTION OF 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_CENTER},
+    {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;
+    int ext_width;
+    int ext_height;
+    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>
+<i>foo</i>
+
+<font [color="(color)"] [size="normal|large|huge|(point size)"]>foo</font>
+   (color)=black|white|red|green|blue|grey
+<large>foo</large>
+<huge>foo</huge>
+
+<center>foo</center>
+<left>foo</left>
+<right>foo</right>
+
+<p [align="left|center|right"]>foo</p>
+<settab pos="(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 = PMALLOC(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 = PMALLOC(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);
+                PFREE(d->links);
+                d->links = ll;
+                d->max_links = n_new;
+            }
+
+            l = d->links[d->n_links];
+            if(!l) {
+                l = PMALLOC(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 = 0;
+
+        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"i", c - start)) {
+        format_push(s,d, HTW_DEFAULT, FV_ITALIC, HTW_DEFAULT);
+    } else if(!_wcsnicmp(start, L"/i", 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 = 0;
+        wchar_t * size_s = NULL;
+        int size_len = 0;
+        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;
+    int ext_width = 0;
+    int ext_height = 0;
+
+    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);
+
+#ifdef DRAW_HTWND_CLIENT_EDGE
+    /* for the moment, we are skipping on the client edge. */
+    if(d->flags & KHUI_HTWND_CLIENTEDGE)
+        DrawEdge(hdc, &r, EDGE_SUNKEN, BF_ADJUST | BF_RECT | BF_FLAT);
+#endif
+
+    hbk = GetSysColorBrush(COLOR_WINDOW);
+    FillRect(hdc, &r, hbk);
+    hbk = NULL;                 /* We don't need to destroy system
+                                   brushes */
+
+    /* 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 = r.top - d->scroll_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) {
+            if (r.right - r.left > p_width)
+                x += (r.right - r.left)/2 - p_width / 2;
+        }
+
+        else if(align == ALIGN_RIGHT) {
+            if (r.right - r.left > p_width)
+                x += (r.right - r.left) - p_width;
+        }
+
+        /* begin wet run */
+        p = par_start;
+        format_unwind(&s_stack, s_start); /* unwind format stack */
+
+        p_width = 0;
+
+        while(p && *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;
+
+                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 = rd.left + s.cx;
+                rd.bottom = rd.top + l_height;
+
+                DrawText(hdc, p, (int)(c - p), &rd,
+                         DT_BOTTOM | DT_LEFT | DT_SINGLELINE | DT_NOPREFIX);
+
+                p_width += s.cx;
+
+                SelectFont(hdc, hfold);
+                p = c;
+            }
+        }
+
+        if (p_width > ext_width)
+            ext_width = p_width;
+
+        y += l_height;
+        par_start = p;
+    }
+
+    if (y > ext_height)
+        ext_height = y;
+
+    EndPaint(hwnd, &ps);
+
+    if (d->ext_width < ext_width ||
+        d->ext_height < ext_height) {
+        SCROLLINFO si;
+        LONG l;
+
+        /* the extents need to be adjusted.  But first check if we
+           have exactly the right scroll bars we need. */
+        if ((ext_width > (r.right - r.left) &&
+             !(d->flags & KHUI_HTWND_HSCROLL)) ||
+            (ext_height > (r.bottom - r.top) &&
+             !(d->flags & KHUI_HTWND_VSCROLL)) ||
+
+            (ext_width <= (r.right - r.left) &&
+             (d->flags & KHUI_HTWND_HSCROLL)) ||
+            (ext_height <= (r.bottom - r.top) &&
+             (d->flags & KHUI_HTWND_VSCROLL))) {
+
+            /* need to add scroll bars */
+            if (ext_width > (r.right - r.left))
+                d->flags |= KHUI_HTWND_HSCROLL;
+            else
+                d->flags &= ~KHUI_HTWND_HSCROLL;
+
+            if (ext_height > (r.bottom - r.top))
+                d->flags |= KHUI_HTWND_VSCROLL;
+            else
+                d->flags &= ~KHUI_HTWND_VSCROLL;
+
+            l = GetWindowLongPtr(hwnd, GWL_STYLE);
+            l &= ~(WS_HSCROLL | WS_VSCROLL);
+
+            l |= ((d->flags & KHUI_HTWND_HSCROLL) ? WS_HSCROLL : 0) |
+                ((d->flags & KHUI_HTWND_VSCROLL) ? WS_VSCROLL : 0);
+
+            SetWindowLongPtr(hwnd, GWL_STYLE, l);
+
+            InvalidateRect(hwnd, NULL, FALSE);
+            /* since the client area changed, we do another redraw
+               before updating the scroll bar positions. */
+        } else {
+            d->ext_width = ext_width;
+            d->ext_height = ext_height;
+
+            if (d->flags & KHUI_HTWND_HSCROLL) {
+                ZeroMemory(&si, sizeof(si));
+                si.cbSize = sizeof(si);
+                si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
+                si.nMin = 0;
+                si.nMax = ext_width;
+                si.nPage = r.right - r.left;
+                si.nPos = d->scroll_left;
+
+                SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
+            }
+
+            if (d->flags & KHUI_HTWND_VSCROLL) {
+                ZeroMemory(&si, sizeof(si));
+                si.cbSize = sizeof(si);
+                si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
+                si.nMin = 0;
+                si.nMax = ext_height;
+                si.nPage = r.bottom - r.top;
+                si.nPos = d->scroll_top;
+
+                SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
+            }
+        }
+    }
+
+    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 = PMALLOC(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;
+            }
+            if(cs->style & WS_HSCROLL) {
+                d->flags |= KHUI_HTWND_HSCROLL;
+            }
+            if(cs->style & WS_VSCROLL) {
+                d->flags |= KHUI_HTWND_VSCROLL;
+            }
+            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 = PMALLOC(cbsize);
+                StringCbCopy(d->text, cbsize, cs->lpszName);
+            }
+
+            /* this is just a flag to the WM_PAINT handler that the
+               extents haven't been set yet. */
+            d->ext_width = -1;
+
+#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) {
+                PFREE(d->text);
+                d->text = NULL;
+            }
+
+            if(SUCCEEDED(StringCbLength(newtext, KHUI_HTWND_MAXCB_TEXT, &cbsize))) {
+                cbsize += sizeof(wchar_t);
+                d->text = PMALLOC(cbsize);
+                StringCbCopy(d->text, cbsize, newtext);
+                rv = TRUE;
+            } else
+                rv = FALSE;
+
+            clear_styles(d);
+
+            d->ext_width = -1;
+            d->scroll_left = 0;
+            d->scroll_top = 0;
+
+            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)
+                PFREE(d->text);
+            d->text = 0;
+
+            if(d->links) {
+                for(i=0;i<d->max_links;i++) {
+                    if(d->links[i])
+                        PFREE(d->links[i]);
+                }
+                PFREE(d->links);
+            }
+
+            clear_styles(d);
+
+            PFREE(d);
+        }
+        break;
+
+    case WM_ERASEBKGND:
+        {
+            HDC hdc = (HDC) wParam;
+            khui_htwnd_data * d;
+            HBRUSH hbr;
+            RECT r;
+
+            d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);
+
+            GetClientRect(hwnd, &r);
+            hbr = GetSysColorBrush(COLOR_WINDOW);
+            FillRect(hdc, &r, hbr);
+
+            /* no need to destroy the brush since it's a system
+               brush. */
+
+            return TRUE;
+        }
+
+    case WM_SIZE:
+        {
+            khui_htwnd_data * d;
+
+            d = (khui_htwnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, 0);
+
+            if (d) {
+                d->ext_width = 0;
+                d->ext_height = 0;
+            }
+        }
+        return 0;
+
+    case WM_PAINT:
+        htw_paint(hwnd, uMsg, wParam, lParam);
+        return 0;
+
+    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_HSCROLL:
+        {
+            khui_htwnd_data * d;
+            int old_pos;
+            int new_pos;
+            int ext;
+            SCROLLINFO si;
+            RECT r;
+
+            d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);
+
+            old_pos = new_pos = d->scroll_left;
+            ext = d->ext_width;
+
+            switch(LOWORD(wParam)) {
+            case SB_THUMBPOSITION:
+            case SB_ENDSCROLL:
+                ZeroMemory(&si, sizeof(si));
+                si.cbSize = sizeof(si);
+                si.fMask = SIF_POS;
+                GetScrollInfo(hwnd, SB_HORZ, &si);
+                new_pos = si.nPos;
+                break;
+
+            case SB_THUMBTRACK:
+                ZeroMemory(&si, sizeof(si));
+                si.cbSize = sizeof(si);
+                si.fMask = SIF_TRACKPOS;
+                GetScrollInfo(hwnd, SB_HORZ, &si);
+                new_pos = si.nTrackPos;
+                break;
+
+            case SB_LINELEFT:
+                new_pos -= ext / 12; /* arbitrary unit */
+                break;
+
+            case SB_LINERIGHT:
+                new_pos += ext / 12; /* arbitrary unit */
+                break;
+
+            case SB_PAGELEFT:
+                GetClientRect(hwnd, &r);
+                new_pos -= r.right - r.left;
+                break;
+
+            case SB_PAGERIGHT:
+                GetClientRect(hwnd, &r);
+                new_pos += r.right - r.left;
+                break;
+            }
+
+            if (new_pos == old_pos)
+                break;
+
+            GetClientRect(hwnd, &r);
+
+            if (new_pos > ext - (r.right - r.left))
+                new_pos = ext - (r.right - r.left);
+
+            if (new_pos < 0)
+                new_pos = 0;
+
+            if (new_pos == old_pos)
+                break;
+
+            ZeroMemory(&si, sizeof(si));
+            si.cbSize = sizeof(si);
+            si.fMask = SIF_POS;
+            si.nPos = new_pos;
+            SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
+            /* note that Windows sometimes adjusts the position after
+               setting it with SetScrollInfo.  We have to look it up
+               again to see what value it ended up at. */
+            GetScrollInfo(hwnd, SB_HORZ, &si);
+            new_pos = si.nPos;
+
+            if (new_pos == old_pos)
+                break;
+
+            d->scroll_left = new_pos;
+
+            ScrollWindow(hwnd, old_pos - new_pos, 0, NULL, NULL);
+
+            return 0;
+        }
+        break;
+
+    case WM_MOUSEMOVE:
+        {
+            khui_htwnd_data * d;
+            int i;
+            POINT p;
+            int nl;
+
+            d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);
+            p.x = GET_X_LPARAM(lParam) + d->scroll_left;
+            p.y = GET_Y_LPARAM(lParam) + d->scroll_top;
+                
+            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) {
+                            RECT rdest = d->links[d->active_link]->r;
+
+                            MapWindowPoints(hwnd, parent, (LPPOINT) &rdest, 2);
+                            InvalidateRect(parent, &rdest, 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) {
+                            RECT rdest = d->links[d->active_link]->r;
+
+                            MapWindowPoints(hwnd, parent, (LPPOINT) &rdest, 2);
+                            InvalidateRect(parent, &rdest, 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(MAKEINTATOM(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,
+        MAKEINTATOM(khui_htwnd_cls),
+        text,
+        style | WS_CHILD,
+        x,y,width,height,
+        parent,
+        (HMENU) KHUI_HTWND_CTLID,
+        khm_hInstance,
+        NULL);
+}
index 2ca8c261b8333d382a9e19dd324b04209b3edc55..f842d2d39d1014b50ceb5f72b33e02937117b883 100644 (file)
@@ -1,57 +1,57 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_HTWND_H\r
-#define __KHIMAIRA_HTWND_H\r
-\r
-#include<khuidefs.h>\r
-\r
-/*\r
-We currently support the following tags:\r
-\r
-<a [id="string"] [param="paramstring"]>link text</a>\r
-<center>foo</center>\r
-<left>foo</left>\r
-<right>foo</right>\r
-*/\r
-\r
-#define KHUI_HTWND_TRANSPARENT  1\r
-#define KHUI_HTWND_CLIENTEDGE   2\r
-#define KHUI_HTWND_HSCROLL      4\r
-#define KHUI_HTWND_VSCROLL      8\r
-#define KHUI_HTWND_FOCUS        2048\r
-\r
-#define KHUI_HTWND_CLASS L"KhmHtWnd"\r
-#define KHUI_HTWND_CTLID 2040\r
-\r
-#define KHUI_HTWND_MAXCCH_TEXT 2048\r
-#define KHUI_HTWND_MAXCB_TEXT (sizeof(wchar_t) * KHUI_HTWND_MAXCCH_TEXT)\r
-\r
-HWND khm_create_htwnd(HWND parent, LPWSTR text, int x, int y, int width, int height, DWORD ex_style, DWORD style);\r
-void khm_unregister_htwnd_class(void);\r
-void khm_register_htwnd_class(void);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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_HSCROLL      4
+#define KHUI_HTWND_VSCROLL      8
+#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
index 54beb2a12d702e5064967292aa2ee45b1d7eb9de..ba036c6492fbad0a9abef59e60ee2b2b14244ac6 100644 (file)
@@ -1,70 +1,70 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KHIMAIRA_H\r
-#define __KHIMAIRA_KHIMAIRA_H\r
-\r
-#include<windows.h>\r
-#include<windowsx.h>\r
-#include<strsafe.h>\r
-#include<commctrl.h>\r
-#include<htmlhelp.h>\r
-\r
-#define KHERR_HMODULE khm_hInstance\r
-#define KHERR_FACILITY khm_facility\r
-#define KHERR_FACILITY_ID 3\r
-\r
-#define NOEXPORT\r
-\r
-#include<netidmgr.h>\r
-\r
-#include<khhelp.h>\r
-#include<intaction.h>\r
-#include<intalert.h>\r
-\r
-#include<resource.h>\r
-#include<credfuncs.h>\r
-#include<appglobal.h>\r
-#include<mainwnd.h>\r
-#include<mainmenu.h>\r
-#include<toolbar.h>\r
-#include<statusbar.h>\r
-#include<credwnd.h>\r
-#include<htwnd.h>\r
-#include<passwnd.h>\r
-#include<newcredwnd.h>\r
-#include<propertywnd.h>\r
-#include<configwnd.h>\r
-#include<aboutwnd.h>\r
-#include<debugfuncs.h>\r
-#include<taskbar.h>\r
-\r
-#include<reqdaemon.h>\r
-#include<notifier.h>\r
-#include<timer.h>\r
-#include<addrchange.h>\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+#include<htmlhelp.h>
+
+#define KHERR_HMODULE khm_hInstance
+#define KHERR_FACILITY khm_facility
+#define KHERR_FACILITY_ID 3
+
+#define NOEXPORT
+
+#include<netidmgr.h>
+
+#include<khhelp.h>
+#include<intaction.h>
+#include<intalert.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<debugfuncs.h>
+#include<taskbar.h>
+
+#include<reqdaemon.h>
+#include<notifier.h>
+#include<timer.h>
+#include<addrchange.h>
+
+#endif
index 3e3a94f673654b11feb521f3ae9f4aeb9fe5be11..e094f02731d04eaffb9a066482dd2fde5ca57fc7 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<shlwapi.h>\r
-#include<khmapp.h>\r
-#include<netidmgr_intver.h>\r
-\r
-#if DEBUG\r
-#include<assert.h>\r
-\r
-#if defined(KH_BUILD_PRIVATE) || defined(KH_BUILD_SPECIAL)\r
-/* needed for writing out leaked allocation and handle report */\r
-#include<stdio.h>\r
-#endif\r
-\r
-#endif\r
-\r
-HINSTANCE khm_hInstance;\r
-const wchar_t * khm_facility = L"NetIDMgr";\r
-int khm_nCmdShow;\r
-khm_ui_4 khm_commctl_version = 0;\r
-\r
-khm_startup_options khm_startup;\r
-\r
-const khm_version app_version = {KH_VERSION_LIST};\r
-\r
-HRESULT hr_coinitialize = S_OK;\r
-\r
-void khm_init_gui(void) {\r
-\r
-    hr_coinitialize = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);\r
-\r
-    khui_init_actions();\r
-    khui_init_rescache();\r
-    khui_init_menu();\r
-    khui_init_toolbar();\r
-    khm_init_notifier();\r
-    khm_init_config();\r
-    khm_init_debug();\r
-    khm_init_taskbar_funcs();\r
-}\r
-\r
-void khm_exit_gui(void) {\r
-    khm_exit_taskbar_funcs();\r
-    khm_exit_debug();\r
-    khm_exit_config();\r
-    khm_exit_notifier();\r
-    khui_exit_toolbar();\r
-    khui_exit_menu();\r
-    khui_exit_rescache();\r
-    khui_exit_actions();\r
-\r
-    if (hr_coinitialize == S_OK ||\r
-        hr_coinitialize == S_FALSE) {\r
-        CoUninitialize();\r
-    }\r
-}\r
-\r
-void khm_parse_commandline(void) {\r
-    LPWSTR wcmdline;\r
-    LPWSTR * wargs;\r
-    int      wargc;\r
-    int i;\r
-\r
-    ZeroMemory(&khm_startup, sizeof(khm_startup));\r
-\r
-    wcmdline = GetCommandLine();\r
-    wargs = CommandLineToArgvW(wcmdline, &wargc);\r
-\r
-    for (i=1; i<wargc; i++) {\r
-        if (!wcscmp(wargs[i], L"-i") ||\r
-            !wcscmp(wargs[i], L"--kinit") ||\r
-            !wcscmp(wargs[i], L"-kinit")) {\r
-            khm_startup.init = TRUE;\r
-            khm_startup.exit = TRUE;\r
-            khm_startup.no_main_window = TRUE;\r
-        }\r
-        else if (!wcscmp(wargs[i], L"-m") ||\r
-                 !wcscmp(wargs[i], L"--import") ||\r
-                 !wcscmp(wargs[i], L"-import")) {\r
-            khm_startup.import = TRUE;\r
-            khm_startup.exit = TRUE;\r
-            khm_startup.no_main_window = TRUE;\r
-        }\r
-        else if (!wcscmp(wargs[i], L"-r") ||\r
-                 !wcscmp(wargs[i], L"--renew") ||\r
-                 !wcscmp(wargs[i], L"-renew")) {\r
-            khm_startup.renew = TRUE;\r
-            khm_startup.exit = TRUE;\r
-            khm_startup.no_main_window = TRUE;\r
-        }\r
-        else if (!wcscmp(wargs[i], L"-d") ||\r
-                 !wcscmp(wargs[i], L"--destroy") ||\r
-                 !wcscmp(wargs[i], L"-destroy")) {\r
-            khm_startup.destroy = TRUE;\r
-            khm_startup.exit = TRUE;\r
-            khm_startup.no_main_window = TRUE;\r
-        }\r
-        else if (!wcscmp(wargs[i], L"-a") ||\r
-                 !wcscmp(wargs[i], L"--autoinit") ||\r
-                 !wcscmp(wargs[i], L"-autoinit")) {\r
-            khm_startup.autoinit = TRUE;\r
-        }\r
-        else if (!wcscmp(wargs[i], L"-x") ||\r
-                 !wcscmp(wargs[i], L"--exit") ||\r
-                 !wcscmp(wargs[i], L"-exit")) {\r
-            khm_startup.exit = TRUE;\r
-            khm_startup.remote_exit = TRUE;\r
-            khm_startup.no_main_window = TRUE;\r
-        }\r
-        else {\r
-            wchar_t help[2048];\r
-\r
-            LoadString(khm_hInstance, IDS_CMDLINE_HELP,\r
-                       help, ARRAYLENGTH(help));\r
-\r
-            MessageBox(NULL, help, L"NetIDMgr", MB_OK);\r
-\r
-            khm_startup.error_exit = TRUE;\r
-            break;\r
-        }\r
-    }\r
-\r
-    /* special: always enable renew when other options aren't specified */\r
-    if (!khm_startup.exit &&\r
-        !khm_startup.destroy &&\r
-        !khm_startup.init &&\r
-        !khm_startup.remote_exit)\r
-        khm_startup.renew = TRUE;\r
-}\r
-\r
-void khm_register_window_classes(void) {\r
-    INITCOMMONCONTROLSEX ics;\r
-\r
-    ZeroMemory(&ics, sizeof(ics));\r
-    ics.dwSize = sizeof(ics);\r
-    ics.dwICC = \r
-        ICC_COOL_CLASSES |\r
-        ICC_BAR_CLASSES |\r
-        ICC_DATE_CLASSES |\r
-        ICC_HOTKEY_CLASS |\r
-        ICC_LISTVIEW_CLASSES |\r
-        ICC_TAB_CLASSES |\r
-        ICC_INTERNET_CLASSES |\r
-#if (_WIN32_WINNT >= 0x501)\r
-        ((IS_COMMCTL6())?\r
-         ICC_LINK_CLASS |\r
-         ICC_STANDARD_CLASSES :\r
-         0) |\r
-#endif\r
-        0;\r
-\r
-    InitCommonControlsEx(&ics);\r
-\r
-    khm_register_main_wnd_class();\r
-    khm_register_credwnd_class();\r
-    khm_register_htwnd_class();\r
-    khm_register_passwnd_class();\r
-    khm_register_newcredwnd_class();\r
-    khm_register_propertywnd_class();\r
-}\r
-\r
-void khm_unregister_window_classes(void) {\r
-    khm_unregister_main_wnd_class();\r
-    khm_unregister_credwnd_class();\r
-    khm_unregister_htwnd_class();\r
-    khm_unregister_passwnd_class();\r
-    khm_unregister_newcredwnd_class();\r
-    khm_unregister_propertywnd_class();\r
-}\r
-\r
-\r
-/* we support up to 16 simutaneous dialogs.  In reality, more than two\r
-   is pretty unlikely.  Property sheets are special and are handled\r
-   separately. */\r
-#define MAX_UI_DIALOGS 16\r
-\r
-typedef struct tag_khui_dialog {\r
-    HWND hwnd;\r
-    HWND hwnd_next;\r
-    BOOL active;\r
-} khui_dialog;\r
-\r
-static khui_dialog khui_dialogs[MAX_UI_DIALOGS];\r
-static int n_khui_dialogs = 0;\r
-static HWND khui_modal_dialog = NULL;\r
-static BOOL khui_main_window_active;\r
-\r
-/* should only be called from the UI thread */\r
-void khm_add_dialog(HWND dlg) {\r
-    if(n_khui_dialogs < MAX_UI_DIALOGS - 1) {\r
-        khui_dialogs[n_khui_dialogs].hwnd = dlg;\r
-        khui_dialogs[n_khui_dialogs].hwnd_next = NULL;\r
-        khui_dialogs[n_khui_dialogs].active = TRUE;\r
-        n_khui_dialogs++;\r
-    } else {\r
-#if DEBUG\r
-          assert(FALSE);\r
-#endif\r
-    }\r
-}\r
-\r
-/* should only be called from the UI thread */\r
-void khm_enter_modal(HWND hwnd) {\r
-    int i;\r
-\r
-    if (khui_modal_dialog) {\r
-\r
-        /* we are already in a modal loop. */\r
-\r
-#ifdef DEBUG\r
-        assert(hwnd != khui_modal_dialog);\r
-#endif\r
-\r
-        for (i=0; i < n_khui_dialogs; i++) {\r
-            if (khui_dialogs[i].hwnd == khui_modal_dialog) {\r
-                khui_dialogs[i].active = TRUE;\r
-                EnableWindow(khui_modal_dialog, FALSE);\r
-                break;\r
-            }\r
-        }\r
-\r
-#ifdef DEBUG\r
-        assert(i < n_khui_dialogs);\r
-#endif\r
-\r
-        for (i=0; i < n_khui_dialogs; i++) {\r
-            if (khui_dialogs[i].hwnd == hwnd) {\r
-                khui_dialogs[i].hwnd_next = khui_modal_dialog;\r
-                break;\r
-            }\r
-        }\r
-\r
-#ifdef DEBUG\r
-        assert(i < n_khui_dialogs);\r
-#endif\r
-\r
-        khui_modal_dialog = hwnd;\r
-\r
-    } else {\r
-\r
-        /* we are entering a modal loop.  preserve the active state of\r
-           the overlapped dialogs and proceed with the modal\r
-           dialog. */\r
-\r
-        for (i=0; i < n_khui_dialogs; i++) {\r
-            if(khui_dialogs[i].hwnd != hwnd) {\r
-                khui_dialogs[i].active = IsWindowEnabled(khui_dialogs[i].hwnd);\r
-                EnableWindow(khui_dialogs[i].hwnd, FALSE);\r
-            }\r
-        }\r
-\r
-        khui_main_window_active = khm_is_main_window_active();\r
-        EnableWindow(khm_hwnd_main, FALSE);\r
-\r
-        khui_modal_dialog = hwnd;\r
-\r
-        SetForegroundWindow(hwnd);\r
-    }\r
-}\r
-\r
-/* should only be called from the UI thread */\r
-void khm_leave_modal(void) {\r
-    int i;\r
-\r
-    for (i=0; i < n_khui_dialogs; i++) {\r
-        if (khui_dialogs[i].hwnd == khui_modal_dialog)\r
-            break;\r
-    }\r
-\r
-#ifdef DEBUG\r
-    assert(i < n_khui_dialogs);\r
-#endif\r
-\r
-    if (i < n_khui_dialogs && khui_dialogs[i].hwnd_next) {\r
-\r
-        /* we need to proceed to the next one down the modal dialog\r
-           chain.  We are not exiting a modal loop. */\r
-\r
-        khui_modal_dialog = khui_dialogs[i].hwnd_next;\r
-        khui_dialogs[i].hwnd_next = FALSE;\r
-\r
-        EnableWindow(khui_modal_dialog, TRUE);\r
-\r
-    } else {\r
-\r
-        HWND last_dialog = NULL;\r
-\r
-        /* we are exiting a modal loop. */\r
-\r
-        for (i=0; i < n_khui_dialogs; i++) {\r
-            if(khui_dialogs[i].hwnd != khui_modal_dialog) {\r
-                EnableWindow(khui_dialogs[i].hwnd, khui_dialogs[i].active);\r
-                last_dialog = khui_dialogs[i].hwnd;\r
-            }\r
-        }\r
-\r
-        EnableWindow(khm_hwnd_main, TRUE);\r
-\r
-        khui_modal_dialog = NULL;\r
-\r
-        if(last_dialog)\r
-            SetActiveWindow(last_dialog);\r
-        else\r
-            SetActiveWindow(khm_hwnd_main);\r
-    }\r
-}\r
-\r
-/* should only be called from the UI thread */\r
-void khm_del_dialog(HWND dlg) {\r
-    int i;\r
-    for(i=0;i < n_khui_dialogs; i++) {\r
-        if(khui_dialogs[i].hwnd == dlg)\r
-            break;\r
-    }\r
-    \r
-    if(i < n_khui_dialogs)\r
-        n_khui_dialogs--;\r
-    else\r
-        return;\r
-\r
-    for(;i < n_khui_dialogs; i++) {\r
-        khui_dialogs[i] = khui_dialogs[i+1];\r
-    }\r
-}\r
-\r
-BOOL khm_check_dlg_message(LPMSG pmsg) {\r
-    int i;\r
-    BOOL found = FALSE;\r
-    for(i=0;i<n_khui_dialogs;i++) {\r
-        if(IsDialogMessage(khui_dialogs[i].hwnd, pmsg)) {\r
-            found = TRUE;\r
-            break;\r
-        }\r
-    }\r
-\r
-    return found;\r
-}\r
-\r
-BOOL khm_is_dialog_active(void) {\r
-    HWND hwnd;\r
-    int i;\r
-\r
-    hwnd = GetForegroundWindow();\r
-\r
-    for (i=0; i<n_khui_dialogs; i++) {\r
-        if (khui_dialogs[i].hwnd == hwnd)\r
-            return TRUE;\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-/* We support at most 256 property sheets simultaneously.  256\r
-   property sheets should be enough for everybody. */\r
-#define MAX_UI_PROPSHEETS 256\r
-\r
-khui_property_sheet *_ui_propsheets[MAX_UI_PROPSHEETS];\r
-int _n_ui_propsheets = 0;\r
-\r
-void khm_add_property_sheet(khui_property_sheet * s) {\r
-    if(_n_ui_propsheets < MAX_UI_PROPSHEETS)\r
-        _ui_propsheets[_n_ui_propsheets++] = s;\r
-    else {\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-    }\r
-}\r
-\r
-void khm_del_property_sheet(khui_property_sheet * s) {\r
-    int i;\r
-\r
-    for(i=0;i < _n_ui_propsheets; i++) {\r
-        if(_ui_propsheets[i] == s)\r
-            break;\r
-    }\r
-\r
-    if(i < _n_ui_propsheets)\r
-        _n_ui_propsheets--;\r
-    else\r
-        return;\r
-\r
-    for(;i < _n_ui_propsheets; i++) {\r
-        _ui_propsheets[i] = _ui_propsheets[i+1];\r
-    }\r
-}\r
-\r
-BOOL khm_check_ps_message(LPMSG pmsg) {\r
-    int i;\r
-    khui_property_sheet * ps;\r
-    for(i=0;i<_n_ui_propsheets;i++) {\r
-        if(khui_ps_check_message(_ui_propsheets[i], pmsg)) {\r
-            if(_ui_propsheets[i]->status == KHUI_PS_STATUS_DONE) {\r
-                ps = _ui_propsheets[i];\r
-\r
-                ps->status = KHUI_PS_STATUS_DESTROY;\r
-                kmq_post_message(KMSG_CRED, KMSG_CRED_PP_END, 0, (void *) ps);\r
-\r
-                return TRUE;\r
-            }\r
-            return TRUE;\r
-        }\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-static HACCEL ha_menu;\r
-\r
-WPARAM khm_message_loop_int(khm_boolean * p_exit) {\r
-    int r;\r
-    MSG msg;\r
-\r
-    while((r = GetMessage(&msg, NULL, 0,0)) &&\r
-          (p_exit == NULL || *p_exit)) {\r
-        if(r == -1)\r
-            break;\r
-        if(!khm_check_dlg_message(&msg) &&\r
-           !khm_check_ps_message(&msg) &&\r
-           !TranslateAccelerator(khm_hwnd_main, ha_menu, &msg)) {\r
-            TranslateMessage(&msg);\r
-            DispatchMessage(&msg);\r
-        }\r
-    }\r
-\r
-    return msg.wParam;\r
-}\r
-\r
-WPARAM khm_message_loop(void) {\r
-    WPARAM w;\r
-    ha_menu = khui_create_global_accel_table();\r
-    w = khm_message_loop_int(NULL);\r
-    DestroyAcceleratorTable(ha_menu);\r
-    return w;\r
-}\r
-\r
-/* Handles all context closures which have a signalled error state.\r
-   If the context is a top level context, then the errors are\r
-   displayed. */\r
-void KHMAPI\r
-khm_err_ctx_completion_handler(enum kherr_ctx_event evt,\r
-                               kherr_context * c) {\r
-    kherr_event * e;\r
-    khui_alert * a;\r
-\r
-    /* we only handle top level contexts here.  For others, we allow\r
-       the child contexts to fold upward silently. */\r
-    if (c->parent || !kherr_is_error_i(c))\r
-        return;\r
-\r
-    for(e = kherr_get_first_event(c);\r
-        e;\r
-        e = kherr_get_next_event(e)) {\r
-\r
-        if (e->severity != KHERR_ERROR && e->severity != KHERR_WARNING)\r
-            continue;\r
-\r
-        kherr_evaluate_event(e);\r
-\r
-        /* we only report errors if there is enough information to\r
-           present a message. */\r
-        if (e->short_desc && e->long_desc) {\r
-\r
-            khui_alert_create_empty(&a);\r
-\r
-            khui_alert_set_severity(a, e->severity);\r
-            khui_alert_set_title(a, e->short_desc);\r
-            khui_alert_set_message(a, e->long_desc);\r
-            if (e->suggestion)\r
-                khui_alert_set_suggestion(a, e->suggestion);\r
-\r
-            khui_alert_queue(a);\r
-\r
-            khui_alert_release(a);\r
-        }\r
-    }\r
-}\r
-\r
-static wchar_t helpfile[MAX_PATH] = L"";\r
-\r
-HWND khm_html_help(HWND hwnd, wchar_t * suffix,\r
-                   UINT command, DWORD_PTR data) {\r
-\r
-    wchar_t gpath[MAX_PATH + MAX_PATH];\r
-\r
-    if (!*helpfile) {\r
-        DWORD dw;\r
-        wchar_t ppath[MAX_PATH];\r
-\r
-        dw = GetModuleFileName(NULL, ppath, ARRAYLENGTH(ppath));\r
-\r
-        if (dw == 0) {\r
-            StringCbCopy(helpfile, sizeof(helpfile), NIDM_HELPFILE);\r
-        } else {\r
-            PathRemoveFileSpec(ppath);\r
-            PathAppend(ppath, NIDM_HELPFILE);\r
-            StringCbCopy(helpfile, sizeof(helpfile), ppath);\r
-        }\r
-    }\r
-\r
-    StringCbCopy(gpath, sizeof(gpath), helpfile);\r
-\r
-    if (suffix)\r
-        StringCbCat(gpath, sizeof(gpath), suffix);\r
-\r
-    return HtmlHelp(hwnd, gpath, command, data);\r
-}\r
-\r
-void khm_load_default_modules(void) {\r
-    kmm_load_default_modules();\r
-}\r
-\r
-int khm_compare_version(const khm_version * v1, const khm_version * v2) {\r
-\r
-    if (v1->major != v2->major)\r
-        return ((int)v1->major) - ((int)v2->major);\r
-\r
-    if (v1->minor != v2->minor)\r
-        return ((int)v1->minor) - ((int)v2->minor);\r
-\r
-    if (v1->patch != v2->patch)\r
-        return ((int)v1->patch) - ((int)v2->patch);\r
-\r
-    return ((int)v1->aux - ((int)v2->aux));\r
-}\r
-\r
-int WINAPI WinMain(HINSTANCE hInstance,\r
-                   HINSTANCE hPrevInstance,\r
-                   LPSTR lpCmdLine,\r
-                   int nCmdShow) \r
-{\r
-    int rv = 0;\r
-    HANDLE h_appmutex;\r
-    BOOL slave = FALSE;\r
-    int mutex_retries = 5;\r
-\r
-    khm_hInstance = hInstance;\r
-    khm_nCmdShow = nCmdShow;\r
-\r
-    khm_parse_commandline();\r
-\r
-    if (khm_startup.error_exit)\r
-        return 0;\r
-\r
- _retry_mutex:\r
-\r
-    if (--mutex_retries < 0)\r
-        return 2;\r
-\r
-    h_appmutex = CreateMutex(NULL, FALSE, L"Local\\NetIDMgr_GlobalAppMutex");\r
-    if (h_appmutex == NULL)\r
-        return 5;\r
-    if (GetLastError() == ERROR_ALREADY_EXISTS)\r
-        slave = TRUE;\r
-\r
-    khc_load_schema(NULL, schema_uiconfig);\r
-\r
- _start_app:\r
-\r
-    if(!slave) {\r
-\r
-        PDESCTHREAD(L"UI", L"App");\r
-\r
-        /* set this so that we don't accidently invoke an API that\r
-           inadvertently puts up the new creds dialog at an\r
-           inopportune moment, like, say, during the new creds dialog\r
-           is open.  This only affects this process, and any child\r
-           processes started by plugins. */\r
-        SetEnvironmentVariable(L"KERBEROSLOGIN_NEVER_PROMPT", L"1");\r
-\r
-        khm_version_init();\r
-\r
-        khm_commctl_version = khm_get_commctl_version(NULL);\r
-\r
-        /* we only open a main window if this is the only instance \r
-           of the application that is running. */\r
-        kmq_init();\r
-        khm_init_gui();\r
-        kmm_init();\r
-\r
-        kmq_set_completion_handler(KMSG_CRED, kmsg_cred_completion);\r
-\r
-        kherr_add_ctx_handler(khm_err_ctx_completion_handler,\r
-                              KHERR_CTX_END,\r
-                              0);\r
-\r
-        /* load the standard plugins */\r
-        khm_load_default_modules();\r
-\r
-        khm_register_window_classes();\r
-\r
-        khm_init_request_daemon();\r
-\r
-        khm_create_main_window();\r
-\r
-        if (!khm_startup.no_main_window)\r
-            khm_show_main_window();\r
-\r
-        khm_refresh_config();\r
-\r
-        rv = (int) khm_message_loop();\r
-\r
-        kmq_set_completion_handler(KMSG_CRED, NULL);\r
-\r
-        khm_exit_request_daemon();\r
-\r
-        kmm_exit();\r
-        khm_exit_gui();\r
-        khm_unregister_window_classes();\r
-        kmq_exit();\r
-\r
-        CloseHandle(h_appmutex);\r
-    } else {\r
-        HWND hwnd = NULL;\r
-        int retries = 5;\r
-        HANDLE hmap;\r
-        wchar_t mapname[256];\r
-        DWORD tid;\r
-        void * xfer;\r
-        khm_query_app_version query_app_version;\r
-        khm_version v;\r
-        BOOL use_cmd_v2 = TRUE;\r
-        khm_size cb = 0;\r
-\r
-        CloseHandle(h_appmutex);\r
-\r
-        while (hwnd == NULL && retries) {\r
-            hwnd = FindWindowEx(NULL, NULL, KHUI_MAIN_WINDOW_CLASS, NULL);\r
-\r
-            if (hwnd)\r
-                break;\r
-\r
-            retries--;\r
-\r
-            /* if the app was just starting, we might have to wait\r
-               till the main window is created. */\r
-\r
-            Sleep(1000);\r
-        }\r
-\r
-        if (!hwnd) {\r
-\r
-            /* if the app was just exiting, we might see the mutex but\r
-               not the window.  We go back and check if the mutex is\r
-               still there. */\r
-\r
-            goto _retry_mutex;\r
-        }\r
-\r
-        /* first check if the remote instance supports a version\r
-           query */\r
-\r
-        StringCbPrintf(mapname, sizeof(mapname),\r
-                       QUERY_APP_VER_MAP_FMT,\r
-                       (tid = GetCurrentThreadId()));\r
-\r
-        hmap = CreateFileMapping(INVALID_HANDLE_VALUE,\r
-                                 NULL,\r
-                                 PAGE_READWRITE,\r
-                                 0,\r
-                                 4096,\r
-                                 mapname);\r
-\r
-        if (hmap == NULL)\r
-            return 3;\r
-\r
-        xfer = MapViewOfFile(hmap, FILE_MAP_WRITE, 0, 0,\r
-                             sizeof(query_app_version));\r
-\r
-        ZeroMemory(&query_app_version, sizeof(query_app_version));\r
-\r
-        if (xfer) {\r
-            query_app_version.magic = KHM_QUERY_APP_VER_MAGIC;\r
-            query_app_version.code = KHM_ERROR_NOT_IMPLEMENTED;\r
-            query_app_version.ver_caller = app_version;\r
-\r
-            query_app_version.request_swap = TRUE;\r
-\r
-            memcpy(xfer, &query_app_version, sizeof(query_app_version));\r
-\r
-            SendMessage(hwnd, WM_KHUI_QUERY_APP_VERSION,\r
-                        0, (LPARAM) tid);\r
-\r
-            memcpy(&query_app_version, xfer, sizeof(query_app_version));\r
-\r
-            UnmapViewOfFile(xfer);\r
-            xfer = NULL;\r
-        }\r
-\r
-        CloseHandle(hmap);\r
-        hmap = NULL;\r
-\r
-        if (query_app_version.magic != KHM_QUERY_APP_VER_MAGIC ||\r
-            query_app_version.code != KHM_ERROR_SUCCESS) {\r
-\r
-            /* We managed to communicate with the remote instance, but\r
-               it didn't send us useful information.  The remote\r
-               instance is not running an actual NetIDMgr instance.\r
-               However, it owns a top level window that was registered\r
-               with our classname.  This instance won't function\r
-               properly if we let it proceed.\r
-            */\r
-\r
-            wchar_t error_msg[1024];\r
-            wchar_t error_title[256];\r
-\r
-            LoadString(khm_hInstance, IDS_REMOTE_FAIL_TITLE,\r
-                       error_title, ARRAYLENGTH(error_title));\r
-            LoadString(khm_hInstance, IDS_REMOTE_FAIL,\r
-                       error_msg, ARRAYLENGTH(error_msg));\r
-\r
-            MessageBox(NULL, error_msg, error_title,\r
-                       MB_OK);\r
-            \r
-            goto done_with_remote;\r
-        }\r
-\r
-        if (query_app_version.code == KHM_ERROR_SUCCESS &&\r
-            query_app_version.request_swap) {\r
-            /* the request for swap was granted.  We can now\r
-               initialize our instance as the master instance. */\r
-\r
-            slave = FALSE;\r
-            goto _start_app;\r
-        }\r
-\r
-        /* Now we can work on sending the command-line to the remote\r
-           instance.  However we need to figure out which version of\r
-           the startup structure it supports. */\r
-        v.major = 1;\r
-        v.minor = 2;\r
-        v.patch = 0;\r
-        v.aux = 0;\r
-\r
-        if (khm_compare_version(&query_app_version.ver_remote, &app_version) == 0 ||\r
-            khm_compare_version(&query_app_version.ver_remote, &v) > 0)\r
-            use_cmd_v2 = TRUE;\r
-        else\r
-            use_cmd_v2 = FALSE;\r
-\r
-        StringCbPrintf(mapname, sizeof(mapname),\r
-                       COMMANDLINE_MAP_FMT,\r
-                       (tid = GetCurrentThreadId()));\r
-\r
-        cb = max(sizeof(struct tag_khm_startup_options_v1),\r
-                 sizeof(struct tag_khm_startup_options_v2));\r
-\r
-        cb = UBOUNDSS(cb, 4096, 4096);\r
-\r
-#ifdef DEBUG\r
-        assert(cb >= 4096);\r
-#endif\r
-\r
-        hmap = CreateFileMapping(INVALID_HANDLE_VALUE,\r
-                                 NULL,\r
-                                 PAGE_READWRITE,\r
-                                 0,\r
-                                 (DWORD) cb,\r
-                                 mapname);\r
-\r
-        if (hmap == NULL)\r
-            return 3;\r
-\r
-        /* make the call */\r
-\r
-        if (use_cmd_v2) {\r
-            /* use the v2 structure */\r
-            struct tag_khm_startup_options_v2 v2opt;\r
-\r
-            ZeroMemory(&v2opt, sizeof(v2opt));\r
-\r
-            v2opt.magic = STARTUP_OPTIONS_MAGIC;\r
-            v2opt.cb_size = sizeof(v2opt);\r
-\r
-            v2opt.init = khm_startup.init;\r
-            v2opt.import = khm_startup.import;\r
-            v2opt.renew = khm_startup.renew;\r
-            v2opt.destroy = khm_startup.destroy;\r
-\r
-            v2opt.autoinit = khm_startup.autoinit;\r
-            v2opt.remote_exit = khm_startup.remote_exit;\r
-\r
-            v2opt.code = KHM_ERROR_NOT_IMPLEMENTED;\r
-\r
-            xfer = MapViewOfFile(hmap,\r
-                                 FILE_MAP_WRITE,\r
-                                 0, 0,\r
-                                 sizeof(v2opt));\r
-\r
-            if (xfer) {\r
-                memcpy(xfer, &v2opt, sizeof(v2opt));\r
-\r
-                SendMessage(hwnd, WM_KHUI_ASSIGN_COMMANDLINE_V2,\r
-                            0, (LPARAM) tid);\r
-\r
-                memcpy(&v2opt, xfer, sizeof(v2opt));\r
-\r
-                /* If the request looks like it wasn't processed, we\r
-                   fallback to the v1 request. */\r
-\r
-                if (v2opt.code == KHM_ERROR_NOT_IMPLEMENTED)\r
-                    use_cmd_v2 = FALSE;\r
-\r
-                UnmapViewOfFile(xfer);\r
-                xfer = NULL;\r
-            }\r
-        }\r
-\r
-        if (!use_cmd_v2) {\r
-            /* use the v1 structure */\r
-\r
-            struct tag_khm_startup_options_v1 v1opt;\r
-\r
-            ZeroMemory(&v1opt, sizeof(v1opt));\r
-\r
-            v1opt.init = khm_startup.init;\r
-            v1opt.import = khm_startup.import;\r
-            v1opt.renew = khm_startup.renew;\r
-            v1opt.destroy = khm_startup.destroy;\r
-            v1opt.autoinit = khm_startup.autoinit;\r
-\r
-            xfer = MapViewOfFile(hmap,\r
-                                 FILE_MAP_WRITE,\r
-                                 0, 0,\r
-                                 sizeof(v1opt));\r
-\r
-            if (xfer) {\r
-                memcpy(xfer, &v1opt, sizeof(v1opt));\r
-\r
-                SendMessage(hwnd, WM_KHUI_ASSIGN_COMMANDLINE_V1,\r
-                            0, (LPARAM) tid);\r
-\r
-                UnmapViewOfFile(xfer);\r
-                xfer = NULL;\r
-            }\r
-        }\r
-\r
-    done_with_remote:\r
-\r
-        if (hmap)\r
-            CloseHandle(hmap);\r
-    }\r
-\r
-#if defined(DEBUG) && (defined(KH_BUILD_PRIVATE) || defined(KH_BUILD_SPECIAL))\r
-    {\r
-        FILE * f = NULL;\r
-\r
-        KHMEXP void KHMAPI khcint_dump_handles(FILE * f);\r
-        KHMEXP void KHMAPI perf_dump(FILE * f);\r
-        KHMEXP void KHMAPI kmqint_dump(FILE * f);\r
-\r
-#if _MSC_VER >= 1400\r
-        if (fopen_s(&f, "memleak.txt", "w") != 0)\r
-            goto done_with_dump;\r
-#else\r
-        f = fopen("memleak.txt", "w");\r
-        if (f == NULL)\r
-            goto done_with_dump;\r
-#endif\r
-\r
-        perf_dump(f);\r
-        khcint_dump_handles(f);\r
-        kmqint_dump(f);\r
-\r
-        fclose(f);\r
-\r
-    done_with_dump:\r
-        ;\r
-    }\r
-#endif\r
-\r
-    return rv;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<shlwapi.h>
+#include<khmapp.h>
+#include<netidmgr_intver.h>
+
+#if DEBUG
+#include<assert.h>
+
+#if defined(KH_BUILD_PRIVATE) || defined(KH_BUILD_SPECIAL)
+/* needed for writing out leaked allocation and handle report */
+#include<stdio.h>
+#endif
+
+#endif
+
+HINSTANCE khm_hInstance;
+const wchar_t * khm_facility = L"NetIDMgr";
+int khm_nCmdShow;
+khm_ui_4 khm_commctl_version = 0;
+
+khm_startup_options khm_startup;
+
+const khm_version app_version = {KH_VERSION_LIST};
+
+HRESULT hr_coinitialize = S_OK;
+
+void khm_init_gui(void) {
+
+    hr_coinitialize = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+
+    khui_init_actions();
+    khui_init_rescache();
+    khui_init_menu();
+    khui_init_toolbar();
+    khm_init_notifier();
+    khm_init_config();
+    khm_init_debug();
+    khm_init_taskbar_funcs();
+}
+
+void khm_exit_gui(void) {
+    khm_exit_taskbar_funcs();
+    khm_exit_debug();
+    khm_exit_config();
+    khm_exit_notifier();
+    khui_exit_toolbar();
+    khui_exit_menu();
+    khui_exit_rescache();
+    khui_exit_actions();
+
+    if (hr_coinitialize == S_OK ||
+        hr_coinitialize == S_FALSE) {
+        CoUninitialize();
+    }
+}
+
+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") ||
+            !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") ||
+                 !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") ||
+                 !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") ||
+                 !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") ||
+                 !wcscmp(wargs[i], L"-autoinit")) {
+            khm_startup.autoinit = TRUE;
+        }
+        else if (!wcscmp(wargs[i], L"-x") ||
+                 !wcscmp(wargs[i], L"--exit") ||
+                 !wcscmp(wargs[i], L"-exit")) {
+            khm_startup.exit = TRUE;
+            khm_startup.remote_exit = TRUE;
+            khm_startup.no_main_window = 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;
+        }
+    }
+
+    /* special: always enable renew when other options aren't specified */
+    if (!khm_startup.exit &&
+        !khm_startup.destroy &&
+        !khm_startup.init &&
+        !khm_startup.remote_exit)
+        khm_startup.renew = TRUE;
+}
+
+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_LISTVIEW_CLASSES |
+        ICC_TAB_CLASSES |
+        ICC_INTERNET_CLASSES |
+#if (_WIN32_WINNT >= 0x501)
+        ((IS_COMMCTL6())?
+         ICC_LINK_CLASS |
+         ICC_STANDARD_CLASSES :
+         0) |
+#endif
+        0;
+
+    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;
+    HWND hwnd_next;
+    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;
+        khui_dialogs[n_khui_dialogs].hwnd_next = NULL;
+        khui_dialogs[n_khui_dialogs].active = TRUE;
+        n_khui_dialogs++;
+    } else {
+#if DEBUG
+          assert(FALSE);
+#endif
+    }
+}
+
+/* should only be called from the UI thread */
+void khm_enter_modal(HWND hwnd) {
+    int i;
+
+    if (khui_modal_dialog) {
+
+        /* we are already in a modal loop. */
+
+#ifdef DEBUG
+        assert(hwnd != khui_modal_dialog);
+#endif
+
+        for (i=0; i < n_khui_dialogs; i++) {
+            if (khui_dialogs[i].hwnd == khui_modal_dialog) {
+                khui_dialogs[i].active = TRUE;
+                EnableWindow(khui_modal_dialog, FALSE);
+                break;
+            }
+        }
+
+#ifdef DEBUG
+        assert(i < n_khui_dialogs);
+#endif
+
+        for (i=0; i < n_khui_dialogs; i++) {
+            if (khui_dialogs[i].hwnd == hwnd) {
+                khui_dialogs[i].hwnd_next = khui_modal_dialog;
+                break;
+            }
+        }
+
+#ifdef DEBUG
+        assert(i < n_khui_dialogs);
+#endif
+
+        khui_modal_dialog = hwnd;
+
+    } else {
+
+        /* we are entering a modal loop.  preserve the active state of
+           the overlapped dialogs and proceed with the modal
+           dialog. */
+
+        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 = khm_is_main_window_active();
+        EnableWindow(khm_hwnd_main, FALSE);
+
+        khui_modal_dialog = hwnd;
+
+        SetForegroundWindow(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)
+            break;
+    }
+
+#ifdef DEBUG
+    assert(i < n_khui_dialogs);
+#endif
+
+    if (i < n_khui_dialogs && khui_dialogs[i].hwnd_next) {
+
+        /* we need to proceed to the next one down the modal dialog
+           chain.  We are not exiting a modal loop. */
+
+        khui_modal_dialog = khui_dialogs[i].hwnd_next;
+        khui_dialogs[i].hwnd_next = FALSE;
+
+        EnableWindow(khui_modal_dialog, TRUE);
+
+    } else {
+
+        HWND last_dialog = NULL;
+
+        /* we are exiting a modal loop. */
+
+        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);
+                last_dialog = khui_dialogs[i].hwnd;
+            }
+        }
+
+        EnableWindow(khm_hwnd_main, TRUE);
+
+        khui_modal_dialog = NULL;
+
+        if(last_dialog)
+            SetActiveWindow(last_dialog);
+        else
+            SetActiveWindow(khm_hwnd_main);
+    }
+}
+
+/* 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;
+    BOOL found = FALSE;
+    for(i=0;i<n_khui_dialogs;i++) {
+        if(IsDialogMessage(khui_dialogs[i].hwnd, pmsg)) {
+            found = TRUE;
+            break;
+        }
+    }
+
+    return found;
+}
+
+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;
+    else {
+#ifdef DEBUG
+        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;
+}
+
+static HACCEL ha_menu;
+
+WPARAM khm_message_loop_int(khm_boolean * p_exit) {
+    int r;
+    MSG msg;
+
+    while((r = GetMessage(&msg, NULL, 0,0)) &&
+          (p_exit == NULL || *p_exit)) {
+        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);
+        }
+    }
+
+    return msg.wParam;
+}
+
+WPARAM khm_message_loop(void) {
+    WPARAM w;
+    ha_menu = khui_create_global_accel_table();
+    w = khm_message_loop_int(NULL);
+    DestroyAcceleratorTable(ha_menu);
+    return w;
+}
+
+/* Handles all context closures which have a signalled error state.
+   If the context is a top level context, then the errors are
+   displayed. */
+void KHMAPI
+khm_err_ctx_completion_handler(enum kherr_ctx_event evt,
+                               kherr_context * c) {
+    kherr_event * e;
+    khui_alert * a;
+
+    /* we only handle top level contexts here.  For others, we allow
+       the child contexts to fold upward silently. */
+    if (c->parent || !kherr_is_error_i(c))
+        return;
+
+    for(e = kherr_get_first_event(c);
+        e;
+        e = kherr_get_next_event(e)) {
+
+        if (e->severity != KHERR_ERROR && e->severity != KHERR_WARNING)
+            continue;
+
+        kherr_evaluate_event(e);
+
+        /* we only report errors if there is enough information to
+           present a message. */
+        if (e->short_desc && e->long_desc) {
+
+            khui_alert_create_empty(&a);
+
+            khui_alert_set_severity(a, e->severity);
+            khui_alert_set_title(a, e->short_desc);
+            khui_alert_set_message(a, e->long_desc);
+            if (e->suggestion)
+                khui_alert_set_suggestion(a, e->suggestion);
+
+            khui_alert_queue(a);
+
+            khui_alert_release(a);
+        }
+    }
+}
+
+static wchar_t helpfile[MAX_PATH] = L"";
+
+HWND khm_html_help(HWND hwnd, wchar_t * suffix,
+                   UINT command, DWORD_PTR data) {
+
+    wchar_t gpath[MAX_PATH + MAX_PATH];
+
+    if (!*helpfile) {
+        DWORD dw;
+        wchar_t ppath[MAX_PATH];
+
+        dw = GetModuleFileName(NULL, ppath, ARRAYLENGTH(ppath));
+
+        if (dw == 0) {
+            StringCbCopy(helpfile, sizeof(helpfile), NIDM_HELPFILE);
+        } else {
+            PathRemoveFileSpec(ppath);
+            PathAppend(ppath, NIDM_HELPFILE);
+            StringCbCopy(helpfile, sizeof(helpfile), ppath);
+        }
+    }
+
+    StringCbCopy(gpath, sizeof(gpath), helpfile);
+
+    if (suffix)
+        StringCbCat(gpath, sizeof(gpath), suffix);
+
+    return HtmlHelp(hwnd, gpath, command, data);
+}
+
+void khm_load_default_modules(void) {
+    kmm_load_default_modules();
+}
+
+int khm_compare_version(const khm_version * v1, const khm_version * v2) {
+
+    if (v1->major != v2->major)
+        return ((int)v1->major) - ((int)v2->major);
+
+    if (v1->minor != v2->minor)
+        return ((int)v1->minor) - ((int)v2->minor);
+
+    if (v1->patch != v2->patch)
+        return ((int)v1->patch) - ((int)v2->patch);
+
+    return ((int)v1->aux - ((int)v2->aux));
+}
+
+int WINAPI WinMain(HINSTANCE hInstance,
+                   HINSTANCE hPrevInstance,
+                   LPSTR lpCmdLine,
+                   int nCmdShow) 
+{
+    int rv = 0;
+    HANDLE h_appmutex;
+    BOOL slave = FALSE;
+    int mutex_retries = 5;
+
+    khm_hInstance = hInstance;
+    khm_nCmdShow = nCmdShow;
+
+    khm_parse_commandline();
+
+    if (khm_startup.error_exit)
+        return 0;
+
+ _retry_mutex:
+
+    if (--mutex_retries < 0)
+        return 2;
+
+    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);
+
+ _start_app:
+
+    if(!slave) {
+
+        PDESCTHREAD(L"UI", L"App");
+
+        /* set this so that we don't accidently invoke an API that
+           inadvertently puts up the new creds dialog at an
+           inopportune moment, like, say, during the new creds dialog
+           is open.  This only affects this process, and any child
+           processes started by plugins. */
+        SetEnvironmentVariable(L"KERBEROSLOGIN_NEVER_PROMPT", L"1");
+
+        khm_version_init();
+
+        khm_commctl_version = khm_get_commctl_version(NULL);
+
+        /* we only open a main window if this is the only instance 
+           of the application that is running. */
+        kmq_init();
+        khm_init_gui();
+        kmm_init();
+
+        kmq_set_completion_handler(KMSG_CRED, kmsg_cred_completion);
+
+        kherr_add_ctx_handler(khm_err_ctx_completion_handler,
+                              KHERR_CTX_END,
+                              0);
+
+        /* load the standard plugins */
+        khm_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();
+
+        khm_refresh_config();
+
+        rv = (int) khm_message_loop();
+
+        kmq_set_completion_handler(KMSG_CRED, NULL);
+
+        khm_exit_request_daemon();
+
+        kmm_exit();
+        khm_exit_gui();
+        khm_unregister_window_classes();
+        kmq_exit();
+
+        CloseHandle(h_appmutex);
+    } else {
+        HWND hwnd = NULL;
+        int retries = 5;
+        HANDLE hmap;
+        wchar_t mapname[256];
+        DWORD tid;
+        void * xfer;
+        khm_query_app_version query_app_version;
+        khm_version v;
+        BOOL use_cmd_v2 = TRUE;
+        khm_size cb = 0;
+
+        CloseHandle(h_appmutex);
+
+        while (hwnd == NULL && retries) {
+            hwnd = FindWindowEx(NULL, NULL, KHUI_MAIN_WINDOW_CLASS, NULL);
+
+            if (hwnd)
+                break;
+
+            retries--;
+
+            /* if the app was just starting, we might have to wait
+               till the main window is created. */
+
+            Sleep(1000);
+        }
+
+        if (!hwnd) {
+
+            /* if the app was just exiting, we might see the mutex but
+               not the window.  We go back and check if the mutex is
+               still there. */
+
+            goto _retry_mutex;
+        }
+
+        /* first check if the remote instance supports a version
+           query */
+
+        StringCbPrintf(mapname, sizeof(mapname),
+                       QUERY_APP_VER_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(query_app_version));
+
+        ZeroMemory(&query_app_version, sizeof(query_app_version));
+
+        if (xfer) {
+            query_app_version.magic = KHM_QUERY_APP_VER_MAGIC;
+            query_app_version.code = KHM_ERROR_NOT_IMPLEMENTED;
+            query_app_version.ver_caller = app_version;
+
+            query_app_version.request_swap = TRUE;
+
+            memcpy(xfer, &query_app_version, sizeof(query_app_version));
+
+            SendMessage(hwnd, WM_KHUI_QUERY_APP_VERSION,
+                        0, (LPARAM) tid);
+
+            memcpy(&query_app_version, xfer, sizeof(query_app_version));
+
+            UnmapViewOfFile(xfer);
+            xfer = NULL;
+        }
+
+        CloseHandle(hmap);
+        hmap = NULL;
+
+        if (query_app_version.magic != KHM_QUERY_APP_VER_MAGIC ||
+            query_app_version.code != KHM_ERROR_SUCCESS) {
+
+            /* We managed to communicate with the remote instance, but
+               it didn't send us useful information.  The remote
+               instance is not running an actual NetIDMgr instance.
+               However, it owns a top level window that was registered
+               with our classname.  This instance won't function
+               properly if we let it proceed.
+            */
+
+            wchar_t error_msg[1024];
+            wchar_t error_title[256];
+
+            LoadString(khm_hInstance, IDS_REMOTE_FAIL_TITLE,
+                       error_title, ARRAYLENGTH(error_title));
+            LoadString(khm_hInstance, IDS_REMOTE_FAIL,
+                       error_msg, ARRAYLENGTH(error_msg));
+
+            MessageBox(NULL, error_msg, error_title,
+                       MB_OK);
+            
+            goto done_with_remote;
+        }
+
+        if (query_app_version.code == KHM_ERROR_SUCCESS &&
+            query_app_version.request_swap) {
+            /* the request for swap was granted.  We can now
+               initialize our instance as the master instance. */
+
+            slave = FALSE;
+            goto _start_app;
+        }
+
+        /* Now we can work on sending the command-line to the remote
+           instance.  However we need to figure out which version of
+           the startup structure it supports. */
+        v.major = 1;
+        v.minor = 2;
+        v.patch = 0;
+        v.aux = 0;
+
+        if (khm_compare_version(&query_app_version.ver_remote, &app_version) == 0 ||
+            khm_compare_version(&query_app_version.ver_remote, &v) > 0)
+            use_cmd_v2 = TRUE;
+        else
+            use_cmd_v2 = FALSE;
+
+        StringCbPrintf(mapname, sizeof(mapname),
+                       COMMANDLINE_MAP_FMT,
+                       (tid = GetCurrentThreadId()));
+
+        cb = max(sizeof(struct tag_khm_startup_options_v1),
+                 sizeof(struct tag_khm_startup_options_v2));
+
+        cb = UBOUNDSS(cb, 4096, 4096);
+
+#ifdef DEBUG
+        assert(cb >= 4096);
+#endif
+
+        hmap = CreateFileMapping(INVALID_HANDLE_VALUE,
+                                 NULL,
+                                 PAGE_READWRITE,
+                                 0,
+                                 (DWORD) cb,
+                                 mapname);
+
+        if (hmap == NULL)
+            return 3;
+
+        /* make the call */
+
+        if (use_cmd_v2) {
+            /* use the v2 structure */
+            struct tag_khm_startup_options_v2 v2opt;
+
+            ZeroMemory(&v2opt, sizeof(v2opt));
+
+            v2opt.magic = STARTUP_OPTIONS_MAGIC;
+            v2opt.cb_size = sizeof(v2opt);
+
+            v2opt.init = khm_startup.init;
+            v2opt.import = khm_startup.import;
+            v2opt.renew = khm_startup.renew;
+            v2opt.destroy = khm_startup.destroy;
+
+            v2opt.autoinit = khm_startup.autoinit;
+            v2opt.remote_exit = khm_startup.remote_exit;
+
+            v2opt.code = KHM_ERROR_NOT_IMPLEMENTED;
+
+            xfer = MapViewOfFile(hmap,
+                                 FILE_MAP_WRITE,
+                                 0, 0,
+                                 sizeof(v2opt));
+
+            if (xfer) {
+                memcpy(xfer, &v2opt, sizeof(v2opt));
+
+                SendMessage(hwnd, WM_KHUI_ASSIGN_COMMANDLINE_V2,
+                            0, (LPARAM) tid);
+
+                memcpy(&v2opt, xfer, sizeof(v2opt));
+
+                /* If the request looks like it wasn't processed, we
+                   fallback to the v1 request. */
+
+                if (v2opt.code == KHM_ERROR_NOT_IMPLEMENTED)
+                    use_cmd_v2 = FALSE;
+
+                UnmapViewOfFile(xfer);
+                xfer = NULL;
+            }
+        }
+
+        if (!use_cmd_v2) {
+            /* use the v1 structure */
+
+            struct tag_khm_startup_options_v1 v1opt;
+
+            ZeroMemory(&v1opt, sizeof(v1opt));
+
+            v1opt.init = khm_startup.init;
+            v1opt.import = khm_startup.import;
+            v1opt.renew = khm_startup.renew;
+            v1opt.destroy = khm_startup.destroy;
+            v1opt.autoinit = khm_startup.autoinit;
+
+            xfer = MapViewOfFile(hmap,
+                                 FILE_MAP_WRITE,
+                                 0, 0,
+                                 sizeof(v1opt));
+
+            if (xfer) {
+                memcpy(xfer, &v1opt, sizeof(v1opt));
+
+                SendMessage(hwnd, WM_KHUI_ASSIGN_COMMANDLINE_V1,
+                            0, (LPARAM) tid);
+
+                UnmapViewOfFile(xfer);
+                xfer = NULL;
+            }
+        }
+
+    done_with_remote:
+
+        if (hmap)
+            CloseHandle(hmap);
+    }
+
+#if defined(DEBUG) && (defined(KH_BUILD_PRIVATE) || defined(KH_BUILD_SPECIAL))
+    {
+        FILE * f = NULL;
+
+        KHMEXP void KHMAPI khcint_dump_handles(FILE * f);
+        KHMEXP void KHMAPI perf_dump(FILE * f);
+        KHMEXP void KHMAPI kmqint_dump(FILE * f);
+
+#if _MSC_VER >= 1400
+        if (fopen_s(&f, "memleak.txt", "w") != 0)
+            goto done_with_dump;
+#else
+        f = fopen("memleak.txt", "w");
+        if (f == NULL)
+            goto done_with_dump;
+#endif
+
+        perf_dump(f);
+        khcint_dump_handles(f);
+        kmqint_dump(f);
+
+        fclose(f);
+
+    done_with_dump:
+        ;
+    }
+#endif
+
+    return rv;
+}
index 6634549b4ce80fa428380b9c8a5255d533bd17dd..bc45bddb3b5ae97511cf46a85b5ca74e192bdc2e 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khmapp.h>\r
-#include<assert.h>\r
-\r
-HWND khui_main_menu_toolbar;\r
-int mm_last_hot_item = -1;\r
-int mm_next_hot_item = -1;\r
-BOOL mm_hot_track = FALSE;\r
-\r
-#define MAX_ILIST 256\r
-/* not the same as MENU_SIZE_ICON_* */\r
-#define ILIST_ICON_X 16\r
-#define ILIST_ICON_Y 15\r
-\r
-khui_ilist * il_icon;\r
-int il_icon_id[MAX_ILIST];\r
-\r
-void khui_init_menu(void) {\r
-    int i;\r
-\r
-    il_icon = khui_create_ilist(ILIST_ICON_X, \r
-                                ILIST_ICON_Y, \r
-                                MAX_ILIST, 5, 0);\r
-    for(i=0;i<MAX_ILIST;i++)\r
-        il_icon_id[i] = -1;\r
-\r
-    khm_refresh_identity_menus();\r
-}\r
-\r
-void khui_exit_menu(void) {\r
-    khui_delete_ilist(il_icon);\r
-}\r
-\r
-int khui_get_icon_index(int id) {\r
-    int i;\r
-    HBITMAP hbm;\r
-\r
-    for(i=0;i<MAX_ILIST;i++)\r
-        if(il_icon_id[i] == id) {\r
-            return i;\r
-        }\r
-\r
-    hbm = LoadImage(khm_hInstance, \r
-                    MAKEINTRESOURCE(id), \r
-                    IMAGE_BITMAP, \r
-                    ILIST_ICON_X, ILIST_ICON_Y, \r
-                    LR_DEFAULTCOLOR);\r
-    i = khui_ilist_add_masked(il_icon, hbm, KHUI_TOOLBAR_BGCOLOR);\r
-    il_icon_id[i] = id;\r
-    DeleteObject(hbm);\r
-\r
-    return i;\r
-}\r
-\r
-void khm_get_action_caption(khm_int32 action, wchar_t * buf, khm_size cb_buf) {\r
-    khui_action * act;\r
-\r
-    StringCbCopy(buf, cb_buf, L"");\r
-\r
-    khui_action_lock();\r
-    act = khui_find_action(action);\r
-\r
-    if (act == NULL)\r
-        goto done;\r
-\r
-    if (act->caption) {\r
-        StringCbCopy(buf, cb_buf, act->caption);\r
-    } else if (act->is_caption) {\r
-        LoadString(khm_hInstance, act->is_caption,\r
-                   buf, (int)(cb_buf / sizeof(wchar_t)));\r
-    }\r
-\r
- done:\r
-    khui_action_unlock();\r
-}\r
-\r
-void khm_get_action_tooltip(khm_int32 action, wchar_t * buf, khm_size cb_buf) {\r
-    khui_action * act;\r
-\r
-    StringCbCopy(buf, cb_buf, L"");\r
-\r
-    khui_action_lock();\r
-    act = khui_find_action(action);\r
-\r
-    if (act == NULL)\r
-        goto done;\r
-\r
-    if (act->tooltip) {\r
-        StringCbCopy(buf, cb_buf, act->tooltip);\r
-    } else if (act->is_tooltip) {\r
-        LoadString(khm_hInstance, act->is_tooltip,\r
-                   buf, (int) (cb_buf / sizeof(wchar_t)));\r
-    }\r
-\r
- done:\r
-    khui_action_unlock();\r
-}\r
-\r
-void add_action_to_menu(HMENU hm, khui_action * act, \r
-                        int idx, int flags) {\r
-    MENUITEMINFO mii;\r
-    wchar_t buf[MAX_RES_STRING] = L"";\r
-    wchar_t accel[MAX_RES_STRING] = L"";\r
-\r
-    assert(!act || act->cmd);\r
-\r
-    mii.cbSize = sizeof(mii);\r
-    mii.fMask = 0;\r
-\r
-    if(act == NULL) {\r
-        mii.fMask = MIIM_FTYPE;\r
-        mii.fType = MFT_SEPARATOR;\r
-    } else {\r
-        khui_menu_def * def;\r
-\r
-        khm_get_action_caption(act->cmd, buf, sizeof(buf));\r
-\r
-        if(khui_get_cmd_accel_string(act->cmd, accel, \r
-                                     ARRAYLENGTH(accel))) {\r
-            StringCbCat(buf, sizeof(buf), L"\t");\r
-            StringCbCat(buf, sizeof(buf), accel);\r
-        }\r
-\r
-        mii.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;\r
-        mii.fType = MFT_STRING;\r
-\r
-        mii.dwTypeData = buf;\r
-        mii.cch = (int) wcslen(buf);\r
-\r
-        mii.wID = act->cmd;\r
-\r
-        if(act->state & KHUI_ACTIONSTATE_DISABLED) {\r
-            mii.fMask |= MIIM_STATE;\r
-            mii.fState = MFS_DISABLED;\r
-        } else {\r
-            mii.fState = 0;\r
-        }\r
-\r
-        if((act->type & KHUI_ACTIONTYPE_TOGGLE) && \r
-           (act->state & KHUI_ACTIONSTATE_CHECKED)) {\r
-            mii.fMask |= MIIM_STATE;\r
-            mii.fState |= MFS_CHECKED;\r
-        }\r
-\r
-        if(act->ib_icon) {\r
-            mii.fMask |= MIIM_BITMAP;\r
-            mii.hbmpItem = HBMMENU_CALLBACK;\r
-        }\r
-\r
-        if (flags & KHUI_ACTIONREF_SUBMENU) {\r
-            def = khui_find_menu(act->cmd);\r
-            if(def) {\r
-                mii.fMask |= MIIM_SUBMENU;\r
-                mii.hSubMenu = mm_create_menu_from_def(def, FALSE);\r
-            }\r
-        }\r
-\r
-        if(flags & KHUI_ACTIONREF_DEFAULT) {\r
-            mii.fMask |= MIIM_STATE;\r
-            mii.fState |= MFS_DEFAULT;\r
-        }\r
-    }\r
-\r
-    InsertMenuItem(hm,idx,TRUE,&mii);\r
-}\r
-\r
-static void refresh_menu(HMENU hm, khui_menu_def * def);\r
-\r
-static int refresh_menu_item(HMENU hm, khui_action * act, \r
-                             int idx, int flags) {\r
-    MENUITEMINFO mii;\r
-    khui_menu_def * def;\r
-\r
-    mii.cbSize = sizeof(mii);\r
-    mii.fMask = 0;\r
-\r
-    if (flags & KHUI_ACTIONREF_END) {\r
-        /* we have been asked to assert that the menu doesn't have\r
-           more than idx items */\r
-        mii.fMask = MIIM_FTYPE;\r
-        while (GetMenuItemInfo(hm, idx, TRUE, &mii)) {\r
-            RemoveMenu(hm, idx, MF_BYPOSITION);\r
-            mii.fMask = MIIM_FTYPE;\r
-        }\r
-\r
-        return 0;\r
-    }\r
-\r
-    /* Check if the menu item is there.  Otherwise we need to add\r
-       it. */\r
-    mii.fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;\r
-    if (!GetMenuItemInfo(hm, idx, TRUE, &mii) ||\r
-        ((flags & KHUI_ACTIONREF_SEP) && !(mii.fType & MFT_SEPARATOR)) ||\r
-        (!(flags & KHUI_ACTIONREF_SEP) && mii.wID != act->cmd)) {\r
-        add_action_to_menu(hm, ((flags & KHUI_ACTIONREF_SEP)? NULL : act),\r
-                           idx, flags);\r
-        return 0;\r
-    }\r
-\r
-    if (flags & KHUI_ACTIONREF_SEP)\r
-        return 0;\r
-\r
-#ifdef DEBUG\r
-    assert(act);\r
-#endif\r
-    if (!act)\r
-        return 0;\r
-\r
-    if (flags & KHUI_ACTIONREF_DEFAULT) {\r
-        if (!(mii.fState & MFS_DEFAULT)) {\r
-            mii.fMask |= MIIM_STATE;\r
-            mii.fState |= MFS_DEFAULT;\r
-        }\r
-    } else {\r
-        if (mii.fState & MFS_DEFAULT) {\r
-            RemoveMenu(hm, idx, MF_BYPOSITION);\r
-            add_action_to_menu(hm, act, idx, flags);\r
-            return 0;\r
-        }\r
-    }\r
-\r
-    mii.fMask = 0;\r
-\r
-    if(act->state & KHUI_ACTIONSTATE_DISABLED) {\r
-        mii.fMask |= MIIM_STATE;\r
-        mii.fState &= ~MFS_ENABLED;\r
-        mii.fState |= MFS_DISABLED;\r
-    } else {\r
-        mii.fMask |= MIIM_STATE;\r
-        mii.fState &= ~MFS_DISABLED;\r
-        mii.fState |= MFS_ENABLED;\r
-    }\r
-\r
-    if(act->type & KHUI_ACTIONTYPE_TOGGLE) {\r
-        mii.fMask |= MIIM_STATE;\r
-        if (act->state & KHUI_ACTIONSTATE_CHECKED) {\r
-            mii.fState &= ~MFS_UNCHECKED;\r
-            mii.fState |= MFS_CHECKED;\r
-        } else {\r
-            mii.fState &= ~MFS_CHECKED;\r
-            mii.fState |= MFS_UNCHECKED;\r
-        }\r
-    }\r
-\r
-    SetMenuItemInfo(hm, act->cmd, FALSE, &mii);\r
-\r
-    def = khui_find_menu(act->cmd);\r
-\r
-    if(def) {\r
-        MENUITEMINFO mii2;\r
-\r
-        mii2.cbSize = sizeof(mii2);\r
-        mii2.fMask = MIIM_SUBMENU;\r
-\r
-        if (GetMenuItemInfo(hm, act->cmd, FALSE, &mii2)) {\r
-            refresh_menu(mii2.hSubMenu, def);\r
-        }\r
-    }\r
-\r
-    return 0;\r
-}\r
-\r
-\r
-static void refresh_menu(HMENU hm, khui_menu_def * def) {\r
-    khui_action_ref * act;\r
-    khm_size i, n;\r
-\r
-    for(i = 0, n = khui_menu_get_size(def); i < n; i++) {\r
-        act = khui_menu_get_action(def, i);\r
-        refresh_menu_item(hm, khui_find_action(act->action), (int) i, act->flags);\r
-    }\r
-\r
-    refresh_menu_item(hm, NULL, (int) i, KHUI_ACTIONREF_END);\r
-}\r
-\r
-static HMENU mm_create_menu_from_def(khui_menu_def * def, BOOL main) {\r
-    HMENU hm;\r
-    khui_action_ref * act;\r
-    khm_size i, n;\r
-\r
-    if (main)\r
-        hm = CreateMenu();\r
-    else\r
-        hm = CreatePopupMenu();\r
-\r
-    for (i = 0, n = khui_menu_get_size(def); i < n; i++) {\r
-        act = khui_menu_get_action(def, i);\r
-        add_action_to_menu(hm, khui_find_action(act->action), (int) i, act->flags);\r
-    }\r
-\r
-    return hm;\r
-}\r
-\r
-void mm_begin_hot_track(void);\r
-void mm_end_hot_track(void);\r
-\r
-static void mm_show_panel_def(khui_menu_def * def, LONG x, LONG y)\r
-{\r
-    HMENU hm;\r
-\r
-    hm = mm_create_menu_from_def(def, FALSE);\r
-\r
-    mm_hot_track = (mm_last_hot_item >= 0);\r
-\r
-    if (mm_hot_track)\r
-        mm_begin_hot_track();\r
-\r
-    TrackPopupMenuEx(hm, \r
-                     TPM_LEFTALIGN | TPM_TOPALIGN | \r
-                     TPM_VERPOSANIMATION, \r
-                     x, y, khm_hwnd_main, NULL);\r
-\r
-    mm_last_hot_item = -1;\r
-\r
-    if (mm_hot_track)\r
-        mm_end_hot_track();\r
-\r
-    mm_hot_track = FALSE;\r
-\r
-    DestroyMenu(hm);\r
-}\r
-\r
-void khm_menu_show_panel(int id, LONG x, LONG y) {\r
-    khui_menu_def * def;\r
-\r
-    def = khui_find_menu(id);\r
-    if(!def)\r
-        return;\r
-\r
-    mm_show_panel_def(def, x, y);\r
-}\r
-\r
-LRESULT khm_menu_activate(int menu_id) {\r
-    khui_menu_def * mmdef;\r
-    int nmm;\r
-\r
-    mmdef = khui_find_menu(KHUI_MENU_MAIN);\r
-    nmm = (int) khui_action_list_length(mmdef->items);\r
-\r
-    if(menu_id == MENU_ACTIVATE_DEFAULT) {\r
-        if (mm_last_hot_item != -1)\r
-            menu_id = mm_last_hot_item;\r
-        else\r
-            menu_id = 0;\r
-    } else if(menu_id == MENU_ACTIVATE_LEFT) {\r
-        menu_id = (mm_last_hot_item > 0)? \r
-            mm_last_hot_item - 1: \r
-            ((mm_last_hot_item == 0)? nmm - 1: 0);\r
-    } else if(menu_id == MENU_ACTIVATE_RIGHT) {\r
-        menu_id = (mm_last_hot_item >=0 && mm_last_hot_item < nmm - 1)? \r
-            mm_last_hot_item + 1: \r
-            0;\r
-    } else if(menu_id == MENU_ACTIVATE_NONE) {\r
-        menu_id = -1;\r
-    }\r
-    \r
-    SendMessage(khui_main_menu_toolbar,\r
-                TB_SETHOTITEM,\r
-                menu_id,\r
-                0);\r
-\r
-    khm_menu_track_current();\r
-\r
-    return TRUE;\r
-}\r
-\r
-LRESULT khm_menu_measure_item(WPARAM wParam, LPARAM lParam) {\r
-    /* all menu icons have a fixed size */\r
-    LPMEASUREITEMSTRUCT lpm = (LPMEASUREITEMSTRUCT) lParam;\r
-    lpm->itemWidth = MENU_SIZE_ICON_X;\r
-    lpm->itemHeight = MENU_SIZE_ICON_Y;\r
-    return TRUE;\r
-}\r
-\r
-LRESULT khm_menu_draw_item(WPARAM wParam, LPARAM lParam) {\r
-    LPDRAWITEMSTRUCT lpd;\r
-    khui_action * act;\r
-    int resid;\r
-    int iidx;\r
-    UINT style;\r
-\r
-    lpd = (LPDRAWITEMSTRUCT) lParam;\r
-    act = khui_find_action(lpd->itemID);\r
-\r
-    resid = 0;\r
-    if((lpd->itemState & ODS_DISABLED) || (lpd->itemState & ODS_GRAYED)) {\r
-        resid = act->ib_icon_dis;\r
-    }\r
-    if(!resid)\r
-        resid = act->ib_icon;\r
-\r
-    if(!resid) /* nothing to draw */\r
-        return TRUE;\r
-\r
-    \r
-    iidx = khui_get_icon_index(resid);\r
-    if(iidx == -1)\r
-        return TRUE;\r
-\r
-\r
-    style = ILD_TRANSPARENT;\r
-    if(lpd->itemState & ODS_HOTLIGHT || lpd->itemState & ODS_SELECTED) {\r
-        style |= ILD_SELECTED;\r
-    }\r
-    \r
-    khui_ilist_draw(il_icon, \r
-                    iidx, \r
-                    lpd->hDC, \r
-                    lpd->rcItem.left, lpd->rcItem.top, style);\r
-\r
-    return TRUE;\r
-}\r
-\r
-void khm_track_menu(int menu) {\r
-    TBBUTTON bi;\r
-    RECT r;\r
-    RECT wr;\r
-\r
-    if (menu != -1)\r
-        mm_last_hot_item = menu;\r
-\r
-    if (mm_last_hot_item != -1) {\r
-        SendMessage(khui_main_menu_toolbar,\r
-                    TB_GETBUTTON,\r
-                    mm_last_hot_item,\r
-                    (LPARAM) &bi);\r
-\r
-        SendMessage(khui_main_menu_toolbar,\r
-                    TB_GETITEMRECT,\r
-                    mm_last_hot_item,\r
-                    (LPARAM) &r);\r
-\r
-        GetWindowRect(khui_main_menu_toolbar, &wr);\r
-\r
-        khm_menu_show_panel(bi.idCommand, wr.left + r.left, wr.top + r.bottom);\r
-\r
-        r.left = 0;\r
-\r
-        if (mm_next_hot_item != -1) {\r
-            mm_last_hot_item = mm_next_hot_item;\r
-            mm_next_hot_item = -1;\r
-\r
-            PostMessage(khm_hwnd_main, WM_COMMAND, \r
-                        MAKEWPARAM(KHUI_PACTION_MENU,0),\r
-                        MAKELPARAM(mm_last_hot_item,1));\r
-        }\r
-    }\r
-}\r
-\r
-void khm_menu_track_current(void) {\r
-    khm_track_menu(-1);\r
-}\r
-\r
-LRESULT khm_menu_handle_select(WPARAM wParam, LPARAM lParam) {\r
-    if((HIWORD(wParam) == 0xffff && lParam == 0) || \r
-       (HIWORD(wParam) & MF_POPUP)) {\r
-        /* the menu was closed */\r
-        khm_statusbar_set_part(KHUI_SBPART_INFO, NULL, NULL);\r
-    } else {\r
-        khui_action * act;\r
-        int id;\r
-        wchar_t buf[MAX_RES_STRING] = L"";\r
-\r
-        id = LOWORD(wParam);\r
-        act = khui_find_action(id);\r
-        if(act == NULL || (act->is_tooltip == 0 && act->tooltip == NULL))\r
-            khm_statusbar_set_part(KHUI_SBPART_INFO, NULL, NULL);\r
-        else {\r
-            khm_get_action_tooltip(act->cmd, buf, sizeof(buf));\r
-\r
-            khm_statusbar_set_part(KHUI_SBPART_INFO, NULL, buf);\r
-        }\r
-    }\r
-    return 0;\r
-}\r
-\r
-HHOOK mm_hevt_hook = NULL;\r
-HWND mm_hwnd_menu_panel = NULL;\r
-\r
-LRESULT CALLBACK mm_event_filter(int code,\r
-                                 WPARAM wParam,\r
-                                 LPARAM lParam) {\r
-    MSG * m;\r
-    RECT r;\r
-    int x,y;\r
-\r
-    if (code == MSGF_MENU) {\r
-        /* do stuff */\r
-        m = (MSG *) lParam;\r
-        GetWindowRect(khui_main_menu_toolbar, &r);\r
-\r
-        if (m->hwnd != khm_hwnd_main)\r
-            mm_hwnd_menu_panel = m->hwnd;\r
-\r
-        switch(m->message) {\r
-        case WM_MOUSEMOVE:\r
-\r
-            x = GET_X_LPARAM(m->lParam);\r
-            y = GET_Y_LPARAM(m->lParam);\r
-            x -= r.left;\r
-            y -= r.top;\r
-\r
-            SendMessage(khui_main_menu_toolbar,\r
-                        m->message,\r
-                        m->wParam,\r
-                        MAKELPARAM(x,y));\r
-            break;\r
-        }\r
-    }\r
-\r
-    return CallNextHookEx(mm_hevt_hook, code, wParam, lParam);\r
-}\r
-\r
-\r
-void mm_begin_hot_track(void) {\r
-\r
-    if (mm_hevt_hook)\r
-        UnhookWindowsHookEx(mm_hevt_hook);\r
-\r
-    mm_hevt_hook = SetWindowsHookEx(WH_MSGFILTER,\r
-                                    mm_event_filter,\r
-                                    NULL,\r
-                                    GetCurrentThreadId());\r
-}\r
-\r
-void mm_end_hot_track(void) {\r
-    if (mm_hevt_hook)\r
-        UnhookWindowsHookEx(mm_hevt_hook);\r
-\r
-    mm_hevt_hook = NULL;\r
-    mm_hwnd_menu_panel = NULL;\r
-}\r
-\r
-void mm_cancel_menu(void) {\r
-    if (mm_hwnd_menu_panel)\r
-        SendMessage(mm_hwnd_menu_panel, WM_CANCELMODE, 0, 0);\r
-}\r
-\r
-LRESULT khm_menu_notify_main(LPNMHDR notice) {\r
-    LPNMTOOLBAR nmt;\r
-    LRESULT ret = FALSE;\r
-    RECT r;\r
-    khui_menu_def * mmdef;\r
-    khui_action_ref * mm;\r
-    int nmm;\r
-\r
-    mmdef = khui_find_menu(KHUI_MENU_MAIN);\r
-    mm = mmdef->items;\r
-    nmm = (int) khui_action_list_length(mm);\r
-\r
-    GetWindowRect(khui_main_menu_toolbar, &r);\r
-\r
-    nmt = (LPNMTOOLBAR) notice;\r
-    switch(notice->code) {\r
-    case TBN_DROPDOWN:\r
-        khm_track_menu(-1);\r
-        /*\r
-        khm_menu_show_panel(nmt->iItem, \r
-                        r.left + nmt->rcButton.left, \r
-                        r.top + nmt->rcButton.bottom);\r
-        */\r
-        ret = TBDDRET_DEFAULT;\r
-        break;\r
-\r
-    case TBN_HOTITEMCHANGE:\r
-        {\r
-            LPNMTBHOTITEM nmhi;\r
-            int new_item = -1;\r
-\r
-            nmhi = (LPNMTBHOTITEM) notice;\r
-\r
-            if(nmhi->dwFlags & HICF_LEAVING)\r
-                new_item = -1;\r
-            else {\r
-                int i;\r
-                for(i=0; i < nmm; i++) {\r
-                    if(mm[i].action == nmhi->idNew) {\r
-                        new_item = i;\r
-                        break;\r
-                    }\r
-                }\r
-            }\r
-\r
-            if (mm_hot_track && \r
-                new_item != mm_last_hot_item &&\r
-                new_item != -1 &&\r
-                mm_last_hot_item != -1) {\r
-\r
-                EndMenu();\r
-                mm_next_hot_item = new_item;\r
-\r
-            }\r
-\r
-            ret = 0;\r
-\r
-            if (!mm_hot_track || new_item != -1)\r
-                mm_last_hot_item = new_item;\r
-\r
-        } break;\r
-\r
-    default:\r
-        /* hmm. what to do */\r
-        ret = FALSE;\r
-    }\r
-    return ret;\r
-}\r
-\r
-struct identity_action_map {\r
-    khm_handle identity;\r
-    khm_int32  renew_cmd;\r
-    khm_int32  destroy_cmd;\r
-    khm_int32  new_cmd;\r
-    int        refreshcycle;\r
-};\r
-\r
-#define IDMAP_ALLOC_INCR 8\r
-\r
-struct identity_action_map * id_action_map = NULL;\r
-khm_size n_id_action_map  = 0;\r
-khm_size nc_id_action_map = 0;\r
-\r
-int idcmd_refreshcycle = 0;\r
-\r
-static struct identity_action_map *\r
-create_identity_cmd_map(khm_handle ident) {\r
-\r
-    struct identity_action_map * actmap;\r
-    wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-    wchar_t fmt[128];\r
-    wchar_t caption[KHUI_MAXCCH_SHORT_DESC];\r
-    wchar_t tooltip[KHUI_MAXCCH_SHORT_DESC];\r
-    wchar_t actionname[KHUI_MAXCCH_NAME];\r
-    khm_size cb;\r
-\r
-    if (n_id_action_map + 1 > nc_id_action_map) {\r
-        nc_id_action_map = UBOUNDSS(n_id_action_map + 1,\r
-                                    IDMAP_ALLOC_INCR,\r
-                                    IDMAP_ALLOC_INCR);\r
-#ifdef DEBUG\r
-        assert(nc_id_action_map > n_id_action_map + 1);\r
-#endif\r
-        id_action_map = PREALLOC(id_action_map,\r
-                                 nc_id_action_map * sizeof(id_action_map[0]));\r
-#ifdef DEBUG\r
-        assert(id_action_map);\r
-#endif\r
-        ZeroMemory(&id_action_map[n_id_action_map],\r
-                   sizeof(id_action_map[0]) * (nc_id_action_map - n_id_action_map));\r
-    }\r
-\r
-    actmap = &id_action_map[n_id_action_map];\r
-    n_id_action_map++;\r
-\r
-    cb = sizeof(idname);\r
-    kcdb_identity_get_name(ident, idname, &cb);\r
-\r
-    actmap->identity = ident;\r
-    kcdb_identity_hold(ident);\r
-\r
-#define GETFORMAT(I) do { fmt[0] = L'\0'; LoadString(khm_hInstance, I, fmt, ARRAYLENGTH(fmt)); } while(0)\r
-#define EXPFORMAT(d,s) do { StringCbPrintf(d, sizeof(d), fmt, s); } while(0)\r
-    /* renew */\r
-\r
-    GETFORMAT(IDS_IDACTIONT_RENEW);\r
-    EXPFORMAT(tooltip, idname);\r
-\r
-    GETFORMAT(IDS_IDACTION_RENEW);\r
-    EXPFORMAT(caption, idname);\r
-\r
-    StringCbPrintf(actionname, sizeof(actionname), L"R:%s", idname);\r
-\r
-    actmap->renew_cmd =\r
-        khui_action_create(actionname, caption, tooltip, NULL,\r
-                           KHUI_ACTIONTYPE_TRIGGER, NULL);\r
-\r
-    /* destroy */\r
-\r
-    GETFORMAT(IDS_IDACTIONT_DESTROY);\r
-    EXPFORMAT(tooltip, idname);\r
-\r
-    GETFORMAT(IDS_IDACTION_DESTROY);\r
-    EXPFORMAT(caption, idname);\r
-\r
-    StringCbPrintf(actionname, sizeof(actionname), L"D:%s", idname);\r
-\r
-    actmap->destroy_cmd =\r
-        khui_action_create(actionname, caption, tooltip, NULL,\r
-                           KHUI_ACTIONTYPE_TRIGGER, NULL);\r
-\r
-    /* new */\r
-\r
-    GETFORMAT(IDS_IDACTIONT_NEW);\r
-    EXPFORMAT(tooltip, idname);\r
-\r
-    GETFORMAT(IDS_IDACTION_NEW);\r
-    EXPFORMAT(caption, idname);\r
-\r
-    StringCbPrintf(actionname, sizeof(actionname), L"N:%s", idname);\r
-\r
-    actmap->new_cmd =\r
-        khui_action_create(actionname, caption, tooltip, NULL,\r
-                           KHUI_ACTIONTYPE_TRIGGER, NULL);\r
-\r
-    actmap->refreshcycle = idcmd_refreshcycle;\r
-\r
-#undef GETFORMAT\r
-#undef EXPFORMAT\r
-\r
-    return actmap;\r
-}\r
-\r
-static void\r
-purge_identity_cmd_map(void) {\r
-    khm_size i;\r
-\r
-    for (i=0; i < n_id_action_map; i++) {\r
-        khm_handle ident;\r
-\r
-        if (id_action_map[i].refreshcycle != idcmd_refreshcycle) {\r
-            ident = id_action_map[i].identity;\r
-            id_action_map[i].identity = NULL;\r
-            kcdb_identity_release(ident);\r
-\r
-            khui_action_delete(id_action_map[i].renew_cmd);\r
-            khui_action_delete(id_action_map[i].destroy_cmd);\r
-\r
-            id_action_map[i].renew_cmd = 0;\r
-            id_action_map[i].destroy_cmd = 0;\r
-        }\r
-    }\r
-}\r
-\r
-static struct identity_action_map *\r
-get_identity_cmd_map(khm_handle ident) {\r
-    khm_size i;\r
-\r
-    for (i=0; i < n_id_action_map; i++) {\r
-        if (kcdb_identity_is_equal(id_action_map[i].identity,\r
-                                   ident))\r
-            break;\r
-    }\r
-\r
-    if (i < n_id_action_map) {\r
-        id_action_map[i].refreshcycle = idcmd_refreshcycle;\r
-        return &id_action_map[i];\r
-    } else {\r
-        return create_identity_cmd_map(ident);\r
-    }\r
-}\r
-\r
-khm_int32\r
-khm_get_identity_renew_action(khm_handle ident) {\r
-    struct identity_action_map * map;\r
-\r
-    map = get_identity_cmd_map(ident);\r
-\r
-    if (map)\r
-        return map->renew_cmd;\r
-    else\r
-        return 0;\r
-}\r
-\r
-khm_int32\r
-khm_get_identity_destroy_action(khm_handle ident) {\r
-    struct identity_action_map * map;\r
-\r
-    map = get_identity_cmd_map(ident);\r
-\r
-    if (map)\r
-        return map->destroy_cmd;\r
-    else\r
-        return 0;\r
-}\r
-\r
-khm_int32\r
-khm_get_identity_new_creds_action(khm_handle ident) {\r
-    struct identity_action_map * map;\r
-\r
-    map = get_identity_cmd_map(ident);\r
-\r
-    if (map)\r
-        return map->new_cmd;\r
-    else\r
-        return 0;\r
-}\r
-\r
-void\r
-khm_refresh_identity_menus(void) {\r
-    khui_menu_def * renew_def = NULL;\r
-    khui_menu_def * dest_def = NULL;\r
-    wchar_t * idlist = NULL;\r
-    wchar_t * idname = NULL;\r
-    khm_size cb = 0;\r
-    khm_size n_idents = 0;\r
-    khm_size t;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-    khm_handle csp_cw = NULL;\r
-    khm_int32 idflags;\r
-    khm_int32 def_sticky = 0;\r
-    khm_boolean sticky_done = FALSE;\r
-\r
-    if (KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow", 0, &csp_cw))) {\r
-        khc_read_int32(csp_cw, L"DefaultSticky", &def_sticky);\r
-        khc_close_space(csp_cw);\r
-        csp_cw = NULL;\r
-    }\r
-\r
-    kcdb_identity_refresh_all();\r
-\r
-    khui_action_lock();\r
-\r
-    idcmd_refreshcycle++;\r
-\r
-    do {\r
-        if (idlist)\r
-            PFREE(idlist);\r
-        idlist = NULL;\r
-        cb = 0;\r
-\r
-        rv = kcdb_identity_enum(KCDB_IDENT_FLAG_ACTIVE | KCDB_IDENT_FLAG_EMPTY,\r
-                                KCDB_IDENT_FLAG_ACTIVE,\r
-                                NULL,\r
-                                &cb,\r
-                                &n_idents);\r
-        if (rv != KHM_ERROR_TOO_LONG || cb == 0 || cb == sizeof(wchar_t) * 2)\r
-            break;\r
-\r
-        idlist = PMALLOC(cb);\r
-#ifdef DEBUG\r
-        assert(idlist);\r
-#endif\r
-\r
-        rv = kcdb_identity_enum(KCDB_IDENT_FLAG_ACTIVE | KCDB_IDENT_FLAG_EMPTY,\r
-                                KCDB_IDENT_FLAG_ACTIVE,\r
-                                idlist,\r
-                                &cb,\r
-                                &n_idents);\r
-        if (rv == KHM_ERROR_TOO_LONG)\r
-            continue;\r
-\r
-        if (KHM_FAILED(rv)) {\r
-            /* something else went wrong. hmm. */\r
-            if (idlist)\r
-                PFREE(idlist);\r
-            idlist = NULL;\r
-        }\r
-        break;\r
-\r
-    } while(TRUE);\r
-\r
-    if (idlist != NULL && n_idents > 0) {\r
-        khui_enable_action(KHUI_MENU_RENEW_CRED, TRUE);\r
-        khui_enable_action(KHUI_MENU_DESTROY_CRED, TRUE);\r
-        khui_enable_action(KHUI_ACTION_RENEW_CRED, TRUE);\r
-        khui_enable_action(KHUI_ACTION_DESTROY_CRED, TRUE);\r
-    } else {\r
-        khui_enable_action(KHUI_MENU_RENEW_CRED, FALSE);\r
-        khui_enable_action(KHUI_MENU_DESTROY_CRED, FALSE);\r
-        khui_enable_action(KHUI_ACTION_RENEW_CRED, FALSE);\r
-        khui_enable_action(KHUI_ACTION_DESTROY_CRED, FALSE);\r
-    }\r
-\r
-    renew_def = khui_find_menu(KHUI_MENU_RENEW_CRED);\r
-    dest_def = khui_find_menu(KHUI_MENU_DESTROY_CRED);\r
-#ifdef DEBUG\r
-    assert(renew_def);\r
-    assert(dest_def);\r
-#endif\r
-\r
-    t = khui_menu_get_size(renew_def);\r
-    while(t) {\r
-        khui_menu_remove_action(renew_def, 0);\r
-        t--;\r
-    }\r
-\r
-    t = khui_menu_get_size(dest_def);\r
-    while(t) {\r
-        khui_menu_remove_action(dest_def, 0);\r
-        t--;\r
-    }\r
-\r
-    if (idlist != NULL && n_idents > 1) {\r
-        khui_menu_insert_action(renew_def, 0, KHUI_ACTION_RENEW_ALL, 0);\r
-        khui_menu_insert_action(renew_def, 1, KHUI_MENU_SEP, 0);\r
-\r
-        khui_menu_insert_action(dest_def, 0, KHUI_ACTION_DESTROY_ALL, 0);\r
-        khui_menu_insert_action(dest_def,  1, KHUI_MENU_SEP, 0);\r
-    }\r
-\r
-    for (idname = idlist; idname && idname[0];\r
-         idname = multi_string_next(idname)) {\r
-        khm_handle identity = NULL;\r
-\r
-        if (KHM_FAILED(kcdb_identity_create(idname, 0, &identity))) {\r
-#ifdef DEBUG\r
-            assert(FALSE);\r
-#endif\r
-            continue;\r
-        }\r
-\r
-        khui_menu_insert_action(renew_def, 1000,\r
-                                khm_get_identity_renew_action(identity),\r
-                                0);\r
-\r
-        khui_menu_insert_action(dest_def, 1000,\r
-                                khm_get_identity_destroy_action(identity),\r
-                                0);\r
-\r
-        idflags = 0;\r
-        kcdb_identity_get_flags(identity, &idflags);\r
-\r
-        if (!(idflags & KCDB_IDENT_FLAG_STICKY) && def_sticky) {\r
-            kcdb_identity_set_flags(identity,\r
-                                    KCDB_IDENT_FLAG_STICKY,\r
-                                    KCDB_IDENT_FLAG_STICKY);\r
-            sticky_done = TRUE;\r
-        }\r
-    }\r
-\r
-    if (idlist)\r
-        PFREE(idlist);\r
-\r
-    purge_identity_cmd_map();\r
-\r
-    khui_action_unlock();\r
-\r
-    khui_refresh_actions();\r
-\r
-    if (sticky_done) {\r
-        InvalidateRect(khm_hwnd_main_cred, NULL, TRUE);\r
-    }\r
-}\r
-\r
-khm_boolean\r
-khm_check_identity_menu_action(khm_int32 act_id) {\r
-\r
-    if (act_id == KHUI_ACTION_DESTROY_ALL) {\r
-        khm_size i;\r
-\r
-        for (i=0; i < n_id_action_map; i++) {\r
-            if (id_action_map[i].identity != NULL) {\r
-                khm_cred_destroy_identity(id_action_map[i].identity);\r
-            }\r
-        }\r
-\r
-        return TRUE;\r
-    } else if (act_id == KHUI_ACTION_RENEW_ALL) {\r
-        khm_size i;\r
-\r
-        for (i=0; i < n_id_action_map; i++) {\r
-            if (id_action_map[i].identity != NULL) {\r
-                khm_cred_renew_identity(id_action_map[i].identity);\r
-            }\r
-        }\r
-\r
-        return TRUE;\r
-    } else {\r
-        khm_size i;\r
-\r
-        for (i=0; i < n_id_action_map; i++) {\r
-            if (id_action_map[i].identity == NULL)\r
-                continue;\r
-\r
-            if (id_action_map[i].renew_cmd == act_id) {\r
-                khm_cred_renew_identity(id_action_map[i].identity);\r
-                return TRUE;\r
-            }\r
-\r
-            if (id_action_map[i].destroy_cmd == act_id) {\r
-                khm_cred_destroy_identity(id_action_map[i].identity);\r
-                return TRUE;\r
-            }\r
-\r
-            if (id_action_map[i].new_cmd == act_id) {\r
-                khm_cred_obtain_new_creds_for_ident(id_action_map[i].identity,\r
-                                                    NULL);\r
-                return TRUE;\r
-            }\r
-        }\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-\r
-HMENU khui_hmenu_main = NULL;\r
-\r
-void khm_menu_refresh_items(void) {\r
-    khui_menu_def * def;\r
-\r
-    if (!khui_hmenu_main)\r
-        return;\r
-\r
-    khui_action_lock();\r
-\r
-    def = khui_find_menu(KHUI_MENU_MAIN);\r
-\r
-    refresh_menu(khui_hmenu_main, def);\r
-\r
-    khui_action_unlock();\r
-\r
-    DrawMenuBar(khm_hwnd_main);\r
-}\r
-\r
-void khm_menu_create_main(HWND parent) {\r
-    HMENU hmenu;\r
-    khui_menu_def * def;\r
-\r
-    def = khui_find_menu(KHUI_MENU_MAIN);\r
-\r
-    hmenu = mm_create_menu_from_def(def, TRUE);\r
-\r
-    SetMenu(parent, hmenu);\r
-\r
-    khui_hmenu_main = hmenu;\r
-\r
-    return;\r
-\r
-#ifdef USE_EXPLORER_STYLE_MENU_BAR\r
-    HWND hwtb;\r
-    REBARBANDINFO rbi;\r
-    SIZE sz;\r
-    int i;\r
-    khui_menu_def * mmdef;\r
-    khui_action_ref * mm;\r
-    int nmm;\r
-\r
-    mmdef = khui_find_menu(KHUI_MENU_MAIN);\r
-    mm = mmdef->items;\r
-    nmm = (int) khui_action_list_length(mm);\r
-\r
-    hwtb = CreateWindowEx(0\r
-#if (_WIN32_IE >= 0x0501)\r
-                          | TBSTYLE_EX_MIXEDBUTTONS\r
-#endif\r
-                          ,\r
-                          TOOLBARCLASSNAME,\r
-                          (LPWSTR) NULL,\r
-                          WS_CHILD | \r
-                          CCS_ADJUSTABLE | \r
-                          TBSTYLE_FLAT |\r
-                          TBSTYLE_AUTOSIZE |\r
-                          TBSTYLE_LIST |\r
-                          CCS_NORESIZE |\r
-                          CCS_NOPARENTALIGN |\r
-                          CCS_NODIVIDER,\r
-                          0, 0, 0, 0, rebar,\r
-                          (HMENU) NULL, khm_hInstance,\r
-                          NULL);\r
-\r
-    if(!hwtb) {\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-        return;\r
-    }\r
-\r
-    khui_main_menu_toolbar = hwtb;\r
-\r
-    SendMessage(hwtb,\r
-                TB_BUTTONSTRUCTSIZE,\r
-                (WPARAM) sizeof(TBBUTTON),\r
-                0);\r
-\r
-    for(i=0; i<nmm; i++) {\r
-        khui_add_action_to_toolbar(hwtb, \r
-                                   khui_find_action(mm[i].action), \r
-                                   KHUI_TOOLBAR_ADD_TEXT | \r
-                                   KHUI_TOOLBAR_ADD_DROPDOWN | \r
-                                   KHUI_TOOLBAR_VARSIZE, \r
-                                   NULL);\r
-    }\r
-\r
-    SendMessage(hwtb,\r
-                TB_AUTOSIZE,\r
-                0,0);\r
-    \r
-    SendMessage(hwtb,\r
-                TB_GETMAXSIZE,\r
-                0,\r
-                (LPARAM) &sz);\r
-\r
-    ZeroMemory(&rbi, sizeof(rbi));\r
-\r
-    rbi.cbSize = sizeof(rbi);\r
-\r
-    rbi.fMask = \r
-        RBBIM_ID |\r
-        RBBIM_STYLE | \r
-        RBBIM_CHILD | \r
-        RBBIM_CHILDSIZE | \r
-        RBBIM_SIZE | \r
-        RBBIM_IDEALSIZE; \r
-\r
-    rbi.fStyle = \r
-        RBBS_USECHEVRON;\r
-\r
-    rbi.hwndChild = hwtb;\r
-    rbi.wID = KHUI_MENU_MAIN;\r
-    rbi.cx = sz.cx;\r
-    rbi.cxMinChild = rbi.cx;\r
-    rbi.cxIdeal = rbi.cx;\r
-    rbi.cyMinChild = sz.cy;\r
-    rbi.cyChild = rbi.cyMinChild;\r
-    rbi.cyIntegral = rbi.cyMinChild;\r
-    rbi.cyMaxChild = rbi.cyMinChild;\r
-\r
-    SendMessage(rebar,\r
-                RB_INSERTBAND,\r
-                0,\r
-                (LPARAM) &rbi);\r
-#endif\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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;
+
+    khm_refresh_identity_menus();
+}
+
+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 khm_get_action_caption(khm_int32 action, wchar_t * buf, khm_size cb_buf) {
+    khui_action * act;
+
+    StringCbCopy(buf, cb_buf, L"");
+
+    khui_action_lock();
+    act = khui_find_action(action);
+
+    if (act == NULL)
+        goto done;
+
+    if (act->caption) {
+        StringCbCopy(buf, cb_buf, act->caption);
+    } else if (act->is_caption) {
+        LoadString(khm_hInstance, act->is_caption,
+                   buf, (int)(cb_buf / sizeof(wchar_t)));
+    }
+
+ done:
+    khui_action_unlock();
+}
+
+void khm_get_action_tooltip(khm_int32 action, wchar_t * buf, khm_size cb_buf) {
+    khui_action * act;
+
+    StringCbCopy(buf, cb_buf, L"");
+
+    khui_action_lock();
+    act = khui_find_action(action);
+
+    if (act == NULL)
+        goto done;
+
+    if (act->tooltip) {
+        StringCbCopy(buf, cb_buf, act->tooltip);
+    } else if (act->is_tooltip) {
+        LoadString(khm_hInstance, act->is_tooltip,
+                   buf, (int) (cb_buf / sizeof(wchar_t)));
+    }
+
+ done:
+    khui_action_unlock();
+}
+
+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"";
+
+    assert(!act || act->cmd);
+
+    mii.cbSize = sizeof(mii);
+    mii.fMask = 0;
+
+    if(act == NULL) {
+        mii.fMask = MIIM_FTYPE;
+        mii.fType = MFT_SEPARATOR;
+    } else {
+        khui_menu_def * def;
+
+        khm_get_action_caption(act->cmd, buf, sizeof(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;
+        }
+
+        if (flags & KHUI_ACTIONREF_SUBMENU) {
+            def = khui_find_menu(act->cmd);
+            if(def) {
+                mii.fMask |= MIIM_SUBMENU;
+                mii.hSubMenu = mm_create_menu_from_def(def, FALSE);
+            }
+        }
+
+        if(flags & KHUI_ACTIONREF_DEFAULT) {
+            mii.fMask |= MIIM_STATE;
+            mii.fState |= MFS_DEFAULT;
+        }
+    }
+
+    InsertMenuItem(hm,idx,TRUE,&mii);
+}
+
+static void refresh_menu(HMENU hm, khui_menu_def * def);
+
+static int refresh_menu_item(HMENU hm, khui_action * act, 
+                             int idx, int flags) {
+    MENUITEMINFO mii;
+    khui_menu_def * def;
+
+    mii.cbSize = sizeof(mii);
+    mii.fMask = 0;
+
+    if (flags & KHUI_ACTIONREF_END) {
+        /* we have been asked to assert that the menu doesn't have
+           more than idx items */
+        mii.fMask = MIIM_FTYPE;
+        while (GetMenuItemInfo(hm, idx, TRUE, &mii)) {
+            RemoveMenu(hm, idx, MF_BYPOSITION);
+            mii.fMask = MIIM_FTYPE;
+        }
+
+        return 0;
+    }
+
+    /* Check if the menu item is there.  Otherwise we need to add
+       it. */
+    mii.fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
+    if (!GetMenuItemInfo(hm, idx, TRUE, &mii) ||
+        ((flags & KHUI_ACTIONREF_SEP) && !(mii.fType & MFT_SEPARATOR)) ||
+        (!(flags & KHUI_ACTIONREF_SEP) && mii.wID != act->cmd)) {
+        add_action_to_menu(hm, ((flags & KHUI_ACTIONREF_SEP)? NULL : act),
+                           idx, flags);
+        return 0;
+    }
+
+    if (flags & KHUI_ACTIONREF_SEP)
+        return 0;
+
+#ifdef DEBUG
+    assert(act);
+#endif
+    if (!act)
+        return 0;
+
+    if (flags & KHUI_ACTIONREF_DEFAULT) {
+        if (!(mii.fState & MFS_DEFAULT)) {
+            mii.fMask |= MIIM_STATE;
+            mii.fState |= MFS_DEFAULT;
+        }
+    } else {
+        if (mii.fState & MFS_DEFAULT) {
+            RemoveMenu(hm, idx, MF_BYPOSITION);
+            add_action_to_menu(hm, act, idx, flags);
+            return 0;
+        }
+    }
+
+    mii.fMask = 0;
+
+    if(act->state & KHUI_ACTIONSTATE_DISABLED) {
+        mii.fMask |= MIIM_STATE;
+        mii.fState &= ~MFS_ENABLED;
+        mii.fState |= MFS_DISABLED;
+    } else {
+        mii.fMask |= MIIM_STATE;
+        mii.fState &= ~MFS_DISABLED;
+        mii.fState |= MFS_ENABLED;
+    }
+
+    if(act->type & KHUI_ACTIONTYPE_TOGGLE) {
+        mii.fMask |= MIIM_STATE;
+        if (act->state & KHUI_ACTIONSTATE_CHECKED) {
+            mii.fState &= ~MFS_UNCHECKED;
+            mii.fState |= MFS_CHECKED;
+        } else {
+            mii.fState &= ~MFS_CHECKED;
+            mii.fState |= MFS_UNCHECKED;
+        }
+    }
+
+    SetMenuItemInfo(hm, act->cmd, FALSE, &mii);
+
+    def = khui_find_menu(act->cmd);
+
+    if(def) {
+        MENUITEMINFO mii2;
+
+        mii2.cbSize = sizeof(mii2);
+        mii2.fMask = MIIM_SUBMENU;
+
+        if (GetMenuItemInfo(hm, act->cmd, FALSE, &mii2)) {
+            refresh_menu(mii2.hSubMenu, def);
+        }
+    }
+
+    return 0;
+}
+
+
+static void refresh_menu(HMENU hm, khui_menu_def * def) {
+    khui_action_ref * act;
+    khm_size i, n;
+
+    for(i = 0, n = khui_menu_get_size(def); i < n; i++) {
+        act = khui_menu_get_action(def, i);
+        refresh_menu_item(hm, khui_find_action(act->action), (int) i, act->flags);
+    }
+
+    refresh_menu_item(hm, NULL, (int) i, KHUI_ACTIONREF_END);
+}
+
+static HMENU mm_create_menu_from_def(khui_menu_def * def, BOOL main) {
+    HMENU hm;
+    khui_action_ref * act;
+    khm_size i, n;
+
+    if (main)
+        hm = CreateMenu();
+    else
+        hm = CreatePopupMenu();
+
+    for (i = 0, n = khui_menu_get_size(def); i < n; i++) {
+        act = khui_menu_get_action(def, i);
+        add_action_to_menu(hm, khui_find_action(act->action), (int) i, act->flags);
+    }
+
+    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, FALSE);
+
+    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 */
+        khm_statusbar_set_part(KHUI_SBPART_INFO, NULL, 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 && act->tooltip == NULL))
+            khm_statusbar_set_part(KHUI_SBPART_INFO, NULL, NULL);
+        else {
+            khm_get_action_tooltip(act->cmd, buf, sizeof(buf));
+
+            khm_statusbar_set_part(KHUI_SBPART_INFO, NULL, 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;
+}
+
+struct identity_action_map {
+    khm_handle identity;
+    khm_int32  renew_cmd;
+    khm_int32  destroy_cmd;
+    khm_int32  new_cmd;
+    int        refreshcycle;
+};
+
+#define IDMAP_ALLOC_INCR 8
+
+struct identity_action_map * id_action_map = NULL;
+khm_size n_id_action_map  = 0;
+khm_size nc_id_action_map = 0;
+
+int idcmd_refreshcycle = 0;
+
+static struct identity_action_map *
+create_identity_cmd_map(khm_handle ident) {
+
+    struct identity_action_map * actmap;
+    wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
+    wchar_t fmt[128];
+    wchar_t caption[KHUI_MAXCCH_SHORT_DESC];
+    wchar_t tooltip[KHUI_MAXCCH_SHORT_DESC];
+    wchar_t actionname[KHUI_MAXCCH_NAME];
+    khm_size cb;
+
+    if (n_id_action_map + 1 > nc_id_action_map) {
+        nc_id_action_map = UBOUNDSS(n_id_action_map + 1,
+                                    IDMAP_ALLOC_INCR,
+                                    IDMAP_ALLOC_INCR);
+#ifdef DEBUG
+        assert(nc_id_action_map > n_id_action_map + 1);
+#endif
+        id_action_map = PREALLOC(id_action_map,
+                                 nc_id_action_map * sizeof(id_action_map[0]));
+#ifdef DEBUG
+        assert(id_action_map);
+#endif
+        ZeroMemory(&id_action_map[n_id_action_map],
+                   sizeof(id_action_map[0]) * (nc_id_action_map - n_id_action_map));
+    }
+
+    actmap = &id_action_map[n_id_action_map];
+    n_id_action_map++;
+
+    cb = sizeof(idname);
+    kcdb_identity_get_name(ident, idname, &cb);
+
+    actmap->identity = ident;
+    kcdb_identity_hold(ident);
+
+#define GETFORMAT(I) do { fmt[0] = L'\0'; LoadString(khm_hInstance, I, fmt, ARRAYLENGTH(fmt)); } while(0)
+#define EXPFORMAT(d,s) do { StringCbPrintf(d, sizeof(d), fmt, s); } while(0)
+    /* renew */
+
+    GETFORMAT(IDS_IDACTIONT_RENEW);
+    EXPFORMAT(tooltip, idname);
+
+    GETFORMAT(IDS_IDACTION_RENEW);
+    EXPFORMAT(caption, idname);
+
+    StringCbPrintf(actionname, sizeof(actionname), L"R:%s", idname);
+
+    actmap->renew_cmd =
+        khui_action_create(actionname, caption, tooltip, NULL,
+                           KHUI_ACTIONTYPE_TRIGGER, NULL);
+
+    /* destroy */
+
+    GETFORMAT(IDS_IDACTIONT_DESTROY);
+    EXPFORMAT(tooltip, idname);
+
+    GETFORMAT(IDS_IDACTION_DESTROY);
+    EXPFORMAT(caption, idname);
+
+    StringCbPrintf(actionname, sizeof(actionname), L"D:%s", idname);
+
+    actmap->destroy_cmd =
+        khui_action_create(actionname, caption, tooltip, NULL,
+                           KHUI_ACTIONTYPE_TRIGGER, NULL);
+
+    /* new */
+
+    GETFORMAT(IDS_IDACTIONT_NEW);
+    EXPFORMAT(tooltip, idname);
+
+    GETFORMAT(IDS_IDACTION_NEW);
+    EXPFORMAT(caption, idname);
+
+    StringCbPrintf(actionname, sizeof(actionname), L"N:%s", idname);
+
+    actmap->new_cmd =
+        khui_action_create(actionname, caption, tooltip, NULL,
+                           KHUI_ACTIONTYPE_TRIGGER, NULL);
+
+    actmap->refreshcycle = idcmd_refreshcycle;
+
+#undef GETFORMAT
+#undef EXPFORMAT
+
+    return actmap;
+}
+
+static void
+purge_identity_cmd_map(void) {
+    khm_size i;
+
+    for (i=0; i < n_id_action_map; i++) {
+        khm_handle ident;
+
+        if (id_action_map[i].refreshcycle != idcmd_refreshcycle) {
+            ident = id_action_map[i].identity;
+            id_action_map[i].identity = NULL;
+            kcdb_identity_release(ident);
+
+            khui_action_delete(id_action_map[i].renew_cmd);
+            khui_action_delete(id_action_map[i].destroy_cmd);
+
+            id_action_map[i].renew_cmd = 0;
+            id_action_map[i].destroy_cmd = 0;
+        }
+    }
+}
+
+static struct identity_action_map *
+get_identity_cmd_map(khm_handle ident) {
+    khm_size i;
+
+    for (i=0; i < n_id_action_map; i++) {
+        if (kcdb_identity_is_equal(id_action_map[i].identity,
+                                   ident))
+            break;
+    }
+
+    if (i < n_id_action_map) {
+        id_action_map[i].refreshcycle = idcmd_refreshcycle;
+        return &id_action_map[i];
+    } else {
+        return create_identity_cmd_map(ident);
+    }
+}
+
+khm_int32
+khm_get_identity_renew_action(khm_handle ident) {
+    struct identity_action_map * map;
+
+    map = get_identity_cmd_map(ident);
+
+    if (map)
+        return map->renew_cmd;
+    else
+        return 0;
+}
+
+khm_int32
+khm_get_identity_destroy_action(khm_handle ident) {
+    struct identity_action_map * map;
+
+    map = get_identity_cmd_map(ident);
+
+    if (map)
+        return map->destroy_cmd;
+    else
+        return 0;
+}
+
+khm_int32
+khm_get_identity_new_creds_action(khm_handle ident) {
+    struct identity_action_map * map;
+
+    map = get_identity_cmd_map(ident);
+
+    if (map)
+        return map->new_cmd;
+    else
+        return 0;
+}
+
+void
+khm_refresh_identity_menus(void) {
+    khui_menu_def * renew_def = NULL;
+    khui_menu_def * dest_def = NULL;
+    wchar_t * idlist = NULL;
+    wchar_t * idname = NULL;
+    khm_size cb = 0;
+    khm_size n_idents = 0;
+    khm_size t;
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+    khm_handle csp_cw = NULL;
+    khm_int32 idflags;
+    khm_int32 def_sticky = 0;
+    khm_boolean sticky_done = FALSE;
+
+    if (KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow", 0, &csp_cw))) {
+        khc_read_int32(csp_cw, L"DefaultSticky", &def_sticky);
+        khc_close_space(csp_cw);
+        csp_cw = NULL;
+    }
+
+    kcdb_identity_refresh_all();
+
+    khui_action_lock();
+
+    idcmd_refreshcycle++;
+
+    do {
+        if (idlist)
+            PFREE(idlist);
+        idlist = NULL;
+        cb = 0;
+
+        rv = kcdb_identity_enum(KCDB_IDENT_FLAG_ACTIVE | KCDB_IDENT_FLAG_EMPTY,
+                                KCDB_IDENT_FLAG_ACTIVE,
+                                NULL,
+                                &cb,
+                                &n_idents);
+        if (rv != KHM_ERROR_TOO_LONG || cb == 0 || cb == sizeof(wchar_t) * 2)
+            break;
+
+        idlist = PMALLOC(cb);
+#ifdef DEBUG
+        assert(idlist);
+#endif
+
+        rv = kcdb_identity_enum(KCDB_IDENT_FLAG_ACTIVE | KCDB_IDENT_FLAG_EMPTY,
+                                KCDB_IDENT_FLAG_ACTIVE,
+                                idlist,
+                                &cb,
+                                &n_idents);
+        if (rv == KHM_ERROR_TOO_LONG)
+            continue;
+
+        if (KHM_FAILED(rv)) {
+            /* something else went wrong. hmm. */
+            if (idlist)
+                PFREE(idlist);
+            idlist = NULL;
+        }
+        break;
+
+    } while(TRUE);
+
+    if (idlist != NULL && n_idents > 0) {
+        khui_enable_action(KHUI_MENU_RENEW_CRED, TRUE);
+        khui_enable_action(KHUI_MENU_DESTROY_CRED, TRUE);
+        khui_enable_action(KHUI_ACTION_RENEW_CRED, TRUE);
+        khui_enable_action(KHUI_ACTION_DESTROY_CRED, TRUE);
+    } else {
+        khui_enable_action(KHUI_MENU_RENEW_CRED, FALSE);
+        khui_enable_action(KHUI_MENU_DESTROY_CRED, FALSE);
+        khui_enable_action(KHUI_ACTION_RENEW_CRED, FALSE);
+        khui_enable_action(KHUI_ACTION_DESTROY_CRED, FALSE);
+    }
+
+    renew_def = khui_find_menu(KHUI_MENU_RENEW_CRED);
+    dest_def = khui_find_menu(KHUI_MENU_DESTROY_CRED);
+#ifdef DEBUG
+    assert(renew_def);
+    assert(dest_def);
+#endif
+
+    t = khui_menu_get_size(renew_def);
+    while(t) {
+        khui_menu_remove_action(renew_def, 0);
+        t--;
+    }
+
+    t = khui_menu_get_size(dest_def);
+    while(t) {
+        khui_menu_remove_action(dest_def, 0);
+        t--;
+    }
+
+    if (idlist != NULL && n_idents > 1) {
+        khui_menu_insert_action(renew_def, 0, KHUI_ACTION_RENEW_ALL, 0);
+        khui_menu_insert_action(renew_def, 1, KHUI_MENU_SEP, 0);
+
+        khui_menu_insert_action(dest_def, 0, KHUI_ACTION_DESTROY_ALL, 0);
+        khui_menu_insert_action(dest_def,  1, KHUI_MENU_SEP, 0);
+    }
+
+    for (idname = idlist; idname && idname[0];
+         idname = multi_string_next(idname)) {
+        khm_handle identity = NULL;
+
+        if (KHM_FAILED(kcdb_identity_create(idname, 0, &identity))) {
+#ifdef DEBUG
+            assert(FALSE);
+#endif
+            continue;
+        }
+
+        khui_menu_insert_action(renew_def, 1000,
+                                khm_get_identity_renew_action(identity),
+                                0);
+
+        khui_menu_insert_action(dest_def, 1000,
+                                khm_get_identity_destroy_action(identity),
+                                0);
+
+        idflags = 0;
+        kcdb_identity_get_flags(identity, &idflags);
+
+        if (!(idflags & KCDB_IDENT_FLAG_STICKY) && def_sticky) {
+            kcdb_identity_set_flags(identity,
+                                    KCDB_IDENT_FLAG_STICKY,
+                                    KCDB_IDENT_FLAG_STICKY);
+            sticky_done = TRUE;
+        }
+    }
+
+    if (idlist)
+        PFREE(idlist);
+
+    purge_identity_cmd_map();
+
+    khui_action_unlock();
+
+    khui_refresh_actions();
+
+    if (sticky_done) {
+        InvalidateRect(khm_hwnd_main_cred, NULL, TRUE);
+    }
+}
+
+khm_boolean
+khm_check_identity_menu_action(khm_int32 act_id) {
+
+    if (act_id == KHUI_ACTION_DESTROY_ALL) {
+        khm_size i;
+
+        for (i=0; i < n_id_action_map; i++) {
+            if (id_action_map[i].identity != NULL) {
+                khm_cred_destroy_identity(id_action_map[i].identity);
+            }
+        }
+
+        return TRUE;
+    } else if (act_id == KHUI_ACTION_RENEW_ALL) {
+        khm_size i;
+
+        for (i=0; i < n_id_action_map; i++) {
+            if (id_action_map[i].identity != NULL) {
+                khm_cred_renew_identity(id_action_map[i].identity);
+            }
+        }
+
+        return TRUE;
+    } else {
+        khm_size i;
+
+        for (i=0; i < n_id_action_map; i++) {
+            if (id_action_map[i].identity == NULL)
+                continue;
+
+            if (id_action_map[i].renew_cmd == act_id) {
+                khm_cred_renew_identity(id_action_map[i].identity);
+                return TRUE;
+            }
+
+            if (id_action_map[i].destroy_cmd == act_id) {
+                khm_cred_destroy_identity(id_action_map[i].identity);
+                return TRUE;
+            }
+
+            if (id_action_map[i].new_cmd == act_id) {
+                khm_cred_obtain_new_creds_for_ident(id_action_map[i].identity,
+                                                    NULL);
+                return TRUE;
+            }
+        }
+    }
+
+    return FALSE;
+}
+
+
+HMENU khui_hmenu_main = NULL;
+
+void khm_menu_refresh_items(void) {
+    khui_menu_def * def;
+
+    if (!khui_hmenu_main)
+        return;
+
+    khui_action_lock();
+
+    def = khui_find_menu(KHUI_MENU_MAIN);
+
+    refresh_menu(khui_hmenu_main, def);
+
+    khui_action_unlock();
+
+    DrawMenuBar(khm_hwnd_main);
+}
+
+void khm_menu_create_main(HWND parent) {
+    HMENU hmenu;
+    khui_menu_def * def;
+
+    def = khui_find_menu(KHUI_MENU_MAIN);
+
+    hmenu = mm_create_menu_from_def(def, TRUE);
+
+    SetMenu(parent, hmenu);
+
+    khui_hmenu_main = hmenu;
+
+    return;
+
+#ifdef USE_EXPLORER_STYLE_MENU_BAR
+    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(0
+#if (_WIN32_IE >= 0x0501)
+                          | TBSTYLE_EX_MIXEDBUTTONS
+#endif
+                          ,
+                          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);
+#endif
+        return;
+    }
+
+    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);
+#endif
+}
index e8da77f39f0fe9cf06236704c9b29e196bdf8306..54504d5fb2c321376d054a2d1e9020bbde814cc5 100644 (file)
@@ -1,68 +1,68 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_MAINMENU_H\r
-#define __KHIMAIRA_MAINMENU_H\r
-\r
-extern HWND khui_main_menu_toolbar;\r
-\r
-#define MENU_ACTIVATE_DEFAULT   -1\r
-#define MENU_ACTIVATE_LEFT      -2\r
-#define MENU_ACTIVATE_RIGHT     -3\r
-#define MENU_ACTIVATE_NONE      -4\r
-\r
-extern int mm_last_hot_item;\r
-extern BOOL mm_hot_track;\r
-\r
-void khm_menu_create_main(HWND rebar);\r
-LRESULT khm_menu_handle_select(WPARAM wParam, LPARAM lParam);\r
-LRESULT khm_menu_notify_main(LPNMHDR notice);\r
-LRESULT khm_menu_activate(int menu_id);\r
-void khm_menu_show_panel(int id, LONG x, LONG y);\r
-void khm_menu_track_current(void);\r
-LRESULT khm_menu_measure_item(WPARAM wParam, LPARAM lparam);\r
-LRESULT khm_menu_draw_item(WPARAM wParam, LPARAM lparam);\r
-void khm_menu_refresh_items(void);\r
-khm_boolean khm_check_identity_menu_action(khm_int32 act_id);\r
-void khm_refresh_identity_menus(void);\r
-void khm_get_action_tooltip(khm_int32 action, wchar_t * buf, khm_size cb_buf);\r
-void khm_get_action_caption(khm_int32 action, wchar_t * buf, khm_size cb_buf);\r
-\r
-khm_int32 khm_get_identity_destroy_action(khm_handle ident);\r
-khm_int32 khm_get_identity_renew_action(khm_handle ident);\r
-khm_int32 khm_get_identity_new_creds_action(khm_handle ident);\r
-\r
-static HMENU mm_create_menu_from_def(khui_menu_def * def, BOOL main);\r
-static void mm_show_panel_def(khui_menu_def * def, LONG x, LONG y);\r
-\r
-void khui_init_menu(void);\r
-void khui_exit_menu(void);\r
-\r
-#define MENU_SIZE_ICON_X 16\r
-#define MENU_SIZE_ICON_Y 16\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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);
+void khm_menu_refresh_items(void);
+khm_boolean khm_check_identity_menu_action(khm_int32 act_id);
+void khm_refresh_identity_menus(void);
+void khm_get_action_tooltip(khm_int32 action, wchar_t * buf, khm_size cb_buf);
+void khm_get_action_caption(khm_int32 action, wchar_t * buf, khm_size cb_buf);
+
+khm_int32 khm_get_identity_destroy_action(khm_handle ident);
+khm_int32 khm_get_identity_renew_action(khm_handle ident);
+khm_int32 khm_get_identity_new_creds_action(khm_handle ident);
+
+static HMENU mm_create_menu_from_def(khui_menu_def * def, BOOL main);
+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
index 7b14d35a28338072ba77b6a6c8f8207b5113c4d0..5a00631c1a0e0c0ccf4de8251aee034be18b4e6b 100644 (file)
@@ -1,82 +1,82 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_MAINWND_H\r
-#define __KHIMAIRA_MAINWND_H\r
-\r
-#define KHUI_MAIN_WINDOW_CLASS  L"KhmMainWindowClass"\r
-#define KHUI_NULL_WINDOW_CLASS  L"KhmNullWindowClass"\r
-\r
-extern ATOM khm_main_window_class;\r
-extern HWND khm_hwnd_main;\r
-extern HWND khm_hwnd_rebar;\r
-extern HWND khm_hwnd_main_cred;\r
-\r
-#define KHM_MAIN_WND_NORMAL 0\r
-#define KHM_MAIN_WND_MINI   1\r
-\r
-extern int  khm_main_wnd_mode;\r
-\r
-void khm_register_main_wnd_class(void);\r
-void khm_unregister_main_wnd_class(void);\r
-void khm_create_main_window_controls(HWND);\r
-void khm_create_main_window(void);\r
-void khm_activate_main_window(void);\r
-void khm_show_main_window(void);\r
-void khm_set_main_window_mode(int mode);\r
-void khm_close_main_window(void);\r
-void khm_hide_main_window(void);\r
-BOOL khm_is_main_window_visible(void);\r
-BOOL khm_is_main_window_active(void);\r
-void khm_adjust_window_dimensions_for_display(RECT * pr, int dock);\r
-LRESULT khm_rebar_notify(LPNMHDR lpnm);\r
-\r
-void\r
-khm_set_dialog_result(HWND hwnd, LRESULT lr);\r
-\r
-LRESULT CALLBACK \r
-khm_main_wnd_proc(HWND hwnd,\r
-                  UINT uMsg,\r
-                  WPARAM wParam,\r
-                  LPARAM lParam);\r
-\r
-#define WM_KHUI_QUERY_APP_VERSION        32809\r
-#define WM_KHUI_ASSIGN_COMMANDLINE_V1    32808\r
-#define WM_KHUI_ASSIGN_COMMANDLINE_V2    32810\r
-\r
-#define COMMANDLINE_MAP_FMT              L"Local\\NetIDMgr_Cmdline_%lu"\r
-#define QUERY_APP_VER_MAP_FMT            L"Local\\NetIDMgr_QueryVer_%lu"\r
-\r
-/* dock values for window positioning */\r
-#define KHM_DOCK_NONE        0\r
-#define KHM_DOCK_TOPLEFT     1\r
-#define KHM_DOCK_TOPRIGHT    2\r
-#define KHM_DOCK_BOTTOMRIGHT 3\r
-#define KHM_DOCK_BOTTOMLEFT  4\r
-#define KHM_DOCK_AUTO        256\r
-#define KHM_DOCKF_DOCKHINT   0x0000ffff\r
-#define KHM_DOCKF_XBORDER    0x00010000\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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;
+extern HWND khm_hwnd_main_cred;
+
+#define KHM_MAIN_WND_NORMAL 0
+#define KHM_MAIN_WND_MINI   1
+
+extern int  khm_main_wnd_mode;
+
+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_activate_main_window(void);
+void khm_show_main_window(void);
+void khm_set_main_window_mode(int mode);
+void khm_close_main_window(void);
+void khm_hide_main_window(void);
+BOOL khm_is_main_window_visible(void);
+BOOL khm_is_main_window_active(void);
+void khm_adjust_window_dimensions_for_display(RECT * pr, int dock);
+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_QUERY_APP_VERSION        32809
+#define WM_KHUI_ASSIGN_COMMANDLINE_V1    32808
+#define WM_KHUI_ASSIGN_COMMANDLINE_V2    32810
+
+#define COMMANDLINE_MAP_FMT              L"Local\\NetIDMgr_Cmdline_%lu"
+#define QUERY_APP_VER_MAP_FMT            L"Local\\NetIDMgr_QueryVer_%lu"
+
+/* dock values for window positioning */
+#define KHM_DOCK_NONE        0
+#define KHM_DOCK_TOPLEFT     1
+#define KHM_DOCK_TOPRIGHT    2
+#define KHM_DOCK_BOTTOMRIGHT 3
+#define KHM_DOCK_BOTTOMLEFT  4
+#define KHM_DOCK_AUTO        256
+#define KHM_DOCKF_DOCKHINT   0x0000ffff
+#define KHM_DOCKF_XBORDER    0x00010000
+#endif
index e0582eb94abb741775f8aa72588bbbbf58052a51..afc774912936c25ebd9bdda129ae09e5716cec56 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#define OEMRESOURCE\r
-\r
-#include<khmapp.h>\r
-#include<assert.h>\r
-\r
-#define KHUI_NOTIFIER_CLASS         L"KhuiNotifierMsgWindowClass"\r
-#define KHUI_ALERTER_CLASS          L"KhuiAlerterWindowClass"\r
-#define KHUI_ALERTBIN_CLASS         L"KhuiAlertBinWindowClass"\r
-\r
-#define KHUI_NOTIFIER_WINDOW        L"KhuiNotifierMsgWindow"\r
-\r
-\r
-/* The commands that are available as default actions when the user\r
-   clicks the notification icon. */\r
-\r
-khm_int32 khm_notifier_actions[] = {\r
-    KHUI_ACTION_OPEN_APP,\r
-    KHUI_ACTION_NEW_CRED\r
-};\r
-\r
-khm_size  n_khm_notifier_actions = ARRAYLENGTH(khm_notifier_actions);\r
-\r
-/* notifier message for notification icon */\r
-#define KHUI_WM_NOTIFIER            WM_COMMAND\r
-\r
-#define DRAWTEXTOPTIONS (DT_CALCRECT | DT_NOPREFIX | DT_WORDBREAK)\r
-\r
-/* are we showing an alert? */\r
-#define ALERT_DISPLAYED() (balloon_alert != NULL || khui_alert_windows != NULL)\r
-\r
-/* Forward declarations */\r
-\r
-struct tag_alerter_wnd_data;\r
-typedef struct tag_alerter_wnd_data alerter_wnd_data;\r
-\r
-struct tag_alert_list;\r
-typedef struct tag_alert_list alert_list;\r
-\r
-static khm_int32 \r
-alert_show(khui_alert * a);\r
-\r
-static khm_int32 \r
-alert_show_minimized(khui_alert * a);\r
-\r
-static khm_int32\r
-alert_show_normal(khui_alert * a);\r
-\r
-static khm_int32\r
-alert_show_list(alert_list * alist);\r
-\r
-static khm_int32\r
-alert_enqueue(khui_alert * a);\r
-\r
-static khm_boolean\r
-alert_is_equal(khui_alert * a1, khui_alert * a2);\r
-\r
-static void\r
-check_for_queued_alerts(void);\r
-\r
-static void\r
-show_queued_alerts(void);\r
-\r
-static khm_int32\r
-alert_consolidate(alert_list * alist,\r
-                  khui_alert * alert,\r
-                  khm_boolean add_from_queue);\r
-\r
-static khm_int32\r
-get_default_notifier_action(void);\r
-\r
-/* Globals */\r
-\r
-/* window class registration atom for message only notifier window\r
-   class */\r
-ATOM atom_notifier = 0;\r
-\r
-/* window class registration atom for alert windows */\r
-ATOM atom_alerter = 0;\r
-/* window class registration atom for the alert "bin", which is the\r
-   window that holds all the alerts. */\r
-ATOM atom_alert_bin = 0;\r
-\r
-/* notifier message window */\r
-HWND hwnd_notifier = NULL;\r
-\r
-BOOL notifier_ready = FALSE;\r
-\r
-/* The list of alert windows currently active */\r
-alerter_wnd_data * khui_alert_windows = NULL;\r
-\r
-/* Notification icon for when there are no alerts to be displayed */\r
-int  iid_normal = IDI_NOTIFY_NONE;\r
-\r
-/* The alert currently being displayed in a balloon */\r
-khui_alert * balloon_alert = NULL;\r
-\r
-/**********************************************************************\r
-  Alert Queue\r
-\r
-  The alert queue is the data structure that keeps track of all the\r
-  alerts that are waiting to be displayed.  Alerts will be placed on\r
-  the queue if they cannot be immediately displayed for some reason\r
-  (e.g. another alert is being displayed, or the user is working in\r
-  another window).\r
-***********************************************************************/\r
-\r
-#define KHUI_ALERT_QUEUE_MAX        64\r
-\r
-khui_alert * alert_queue[KHUI_ALERT_QUEUE_MAX];\r
-khm_int32    alert_queue_head = 0;\r
-khm_int32    alert_queue_tail = 0;\r
-\r
-#define is_alert_queue_empty() (alert_queue_head == alert_queue_tail)\r
-#define is_alert_queue_full()  (((alert_queue_tail + 1) % KHUI_ALERT_QUEUE_MAX) == alert_queue_head)\r
-\r
-/* NOTE: the alert queue functions are unsafe to call from any thread\r
-   other than the UI thread. */\r
-\r
-static void \r
-alert_queue_put_alert(khui_alert * a) {\r
-    if (is_alert_queue_full()) return;\r
-    alert_queue[alert_queue_tail++] = a;\r
-    khui_alert_hold(a);\r
-    alert_queue_tail %= KHUI_ALERT_QUEUE_MAX;\r
-}\r
-\r
-/* the caller needs to release the alert that's returned  */\r
-static khui_alert * \r
-alert_queue_get_alert(void) {\r
-    khui_alert * a;\r
-\r
-    if (is_alert_queue_empty()) return NULL;\r
-    a = alert_queue[alert_queue_head++];\r
-    alert_queue_head %= KHUI_ALERT_QUEUE_MAX;\r
-\r
-    return a;                   /* held */\r
-}\r
-\r
-static int\r
-alert_queue_get_size(void) {\r
-    if (is_alert_queue_empty())\r
-        return 0;\r
-\r
-    if (alert_queue_tail < alert_queue_head) {\r
-        return (alert_queue_tail + KHUI_ALERT_QUEUE_MAX - alert_queue_head);\r
-    } else {\r
-        return alert_queue_tail - alert_queue_head;\r
-    }\r
-}\r
-\r
-static khui_alert *\r
-alert_queue_get_alert_by_pos(int pos) {\r
-    khui_alert * a;\r
-\r
-    if (is_alert_queue_empty() ||\r
-        pos >= alert_queue_get_size() ||\r
-        pos < 0) {\r
-        return NULL;\r
-    }\r
-\r
-    a = alert_queue[(alert_queue_head + pos) % KHUI_ALERT_QUEUE_MAX];\r
-    if (a) {\r
-        khui_alert_hold(a);\r
-    }\r
-    return a;\r
-}\r
-\r
-static int\r
-alert_queue_delete_alert(khui_alert * a) {\r
-    int idx;\r
-    int succ;\r
-\r
-    idx = alert_queue_head;\r
-    while(idx != alert_queue_tail) {\r
-        if (alert_queue[idx] == a)\r
-            break;\r
-\r
-        idx = (idx + 1) % KHUI_ALERT_QUEUE_MAX;\r
-    }\r
-\r
-    if (idx == alert_queue_tail)\r
-        return 0;\r
-\r
-#ifdef DEBUG\r
-    assert(alert_queue[idx]);\r
-#endif\r
-    khui_alert_release(alert_queue[idx]);\r
-\r
-    succ = (idx + 1) % KHUI_ALERT_QUEUE_MAX;\r
-    while(succ != alert_queue_tail) {\r
-        alert_queue[idx] = alert_queue[succ];\r
-\r
-        succ = (succ + 1) % KHUI_ALERT_QUEUE_MAX;\r
-        idx = (idx + 1) % KHUI_ALERT_QUEUE_MAX;\r
-    }\r
-\r
-    alert_queue_tail = idx;\r
-    return 1;\r
-}\r
-\r
-/* the caller needs to release the alert that's returned */\r
-static khui_alert * \r
-alert_queue_peek(void) {\r
-    khui_alert * a;\r
-\r
-    if (is_alert_queue_empty())\r
-        return NULL;\r
-\r
-    a = alert_queue[alert_queue_head];\r
-    khui_alert_hold(a);\r
-\r
-    return a;\r
-}\r
-\r
-/**********************************************************************\r
-  Alert List\r
-\r
-  A list of alerts.  Currently has a fixed upper limit, but the limit\r
-  is high enough for now.\r
-***********************************************************************/\r
-\r
-typedef struct tag_alert_list {\r
-    khui_alert * alerts[KHUI_ALERT_QUEUE_MAX];\r
-    int          n_alerts;\r
-    wchar_t      title[KHUI_MAXCCH_TITLE];\r
-} alert_list;\r
-\r
-static void\r
-alert_list_init(alert_list * alist) {\r
-    ZeroMemory(alist, sizeof(*alist));\r
-}\r
-\r
-static void\r
-alert_list_set_title(alert_list * alist, wchar_t * title) {\r
-    StringCbCopy(alist->title, sizeof(alist->title), title);\r
-}\r
-\r
-static khm_int32\r
-alert_list_add_alert(alert_list * alist,\r
-                     khui_alert * alert) {\r
-\r
-    if (alist->n_alerts == ARRAYLENGTH(alist->alerts))\r
-        return KHM_ERROR_NO_RESOURCES;\r
-\r
-    khui_alert_hold(alert);\r
-    alist->alerts[alist->n_alerts++] = alert;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-static void\r
-alert_list_destroy(alert_list * alist) {\r
-    int i;\r
-\r
-    for (i=0; i < alist->n_alerts; i++) {\r
-        if (alist->alerts[i] != NULL) {\r
-            khui_alert_release(alist->alerts[i]);\r
-            alist->alerts[i] = NULL;\r
-        }\r
-    }\r
-\r
-    alist->n_alerts = 0;\r
-}\r
-\r
-\r
-/**********************************************************************\r
-  Notifier Window\r
-\r
-  The notifier window manages the notification icon and handles\r
-  KMSG_ALERT messages sent from the UI library.  The window will exist\r
-  for the lifetime of the application.\r
-***********************************************************************/\r
-\r
-/* These are defined for APPVER >= 0x501.  We are defining them here\r
-   so that we can build with APPVER = 0x500 and use the same binaries\r
-   with Win XP. */\r
-\r
-#ifndef NIN_BALLOONSHOW\r
-#define NIN_BALLOONSHOW (WM_USER + 2)\r
-#endif\r
-\r
-#ifndef NIN_BALLOONHIDE\r
-#define NIN_BALLOONHIDE (WM_USER + 3)\r
-#endif\r
-\r
-#ifndef NIN_BALLOONTIMEOUT\r
-#define NIN_BALLOONTIMEOUT (WM_USER + 4)\r
-#endif\r
-\r
-#ifndef NIN_BALLOONUSERCLICK\r
-#define NIN_BALLOONUSERCLICK (WM_USER + 5)\r
-#endif\r
-\r
-\r
-static LRESULT CALLBACK \r
-notifier_wnd_proc(HWND hwnd,\r
-                  UINT uMsg,\r
-                  WPARAM wParam,\r
-                  LPARAM lParam)\r
-{\r
-    kmq_message * m;\r
-    khm_int32 rv;\r
-\r
-    if(uMsg == KMQ_WM_DISPATCH) {\r
-        kmq_wm_begin(lParam, &m);\r
-        rv = KHM_ERROR_SUCCESS;\r
-\r
-        if(m->type == KMSG_ALERT) {\r
-            /* handle notifier messages */\r
-            switch(m->subtype) {\r
-            case KMSG_ALERT_SHOW:\r
-                {\r
-                    khui_alert * a;\r
-\r
-                    a = (khui_alert *) m->vparam;\r
-#ifdef DEBUG\r
-                    assert(a != NULL);\r
-#endif\r
-                    rv = alert_show(a);\r
-                    khui_alert_release(a);\r
-                }\r
-                break;\r
-\r
-            case KMSG_ALERT_QUEUE:\r
-                {\r
-                    khui_alert * a;\r
-\r
-                    a = (khui_alert *) m->vparam;\r
-#ifdef DEBUG\r
-                    assert(a != NULL);\r
-#endif\r
-                    rv = alert_enqueue(a);\r
-                    khui_alert_release(a);\r
-                }\r
-                break;\r
-\r
-            case KMSG_ALERT_CHECK_QUEUE:\r
-                check_for_queued_alerts();\r
-                break;\r
-\r
-            case KMSG_ALERT_SHOW_QUEUED:\r
-                show_queued_alerts();\r
-                break;\r
-\r
-            case KMSG_ALERT_SHOW_MODAL:\r
-                {\r
-                    khui_alert * a;\r
-\r
-                    a = (khui_alert *) m->vparam;\r
-#ifdef DEBUG\r
-                    assert(a != NULL);\r
-#endif\r
-                    khui_alert_lock(a);\r
-                    a->flags |= KHUI_ALERT_FLAG_MODAL;\r
-                    khui_alert_unlock(a);\r
-\r
-                    rv = alert_show(a);\r
-\r
-                    if (KHM_SUCCEEDED(rv)) {\r
-                        khm_message_loop_int(&a->displayed);\r
-                    }\r
-\r
-                    khui_alert_release(a);\r
-                }\r
-                break;\r
-            }\r
-        } else if (m->type == KMSG_CRED &&\r
-                   m->subtype == KMSG_CRED_ROOTDELTA) {\r
-\r
-            KillTimer(hwnd, KHUI_REFRESH_TIMER_ID);\r
-            SetTimer(hwnd, KHUI_REFRESH_TIMER_ID,\r
-                     KHUI_REFRESH_TIMEOUT,\r
-                     NULL);\r
-\r
-        }\r
-\r
-        return kmq_wm_end(m, rv);\r
-    } else if (uMsg == KHUI_WM_NOTIFIER) {\r
-        /* Handle events generated from the notification icon */\r
-\r
-        /* wParam is the identifier of the notify icon, but we only\r
-           have one. */\r
-        switch(lParam) {\r
-        case WM_CONTEXTMENU: \r
-            {\r
-                POINT pt;\r
-                int menu_id;\r
-                khui_menu_def * mdef;\r
-                khui_action_ref * act;\r
-                khm_size i, n;\r
-                khm_int32 def_cmd;\r
-\r
-                /* before we show the context menu, we need to make\r
-                   sure that the default action for the notification\r
-                   icon is present in the menu and that it is marked\r
-                   as the default. */\r
-\r
-                def_cmd = get_default_notifier_action();\r
-\r
-                if (khm_is_main_window_visible()) {\r
-                    menu_id = KHUI_MENU_ICO_CTX_NORMAL;\r
-\r
-                    if (def_cmd == KHUI_ACTION_OPEN_APP)\r
-                        def_cmd = KHUI_ACTION_CLOSE_APP;\r
-                } else {\r
-                    menu_id = KHUI_MENU_ICO_CTX_MIN;\r
-                }\r
-\r
-                mdef = khui_find_menu(menu_id);\r
-\r
-#ifdef DEBUG\r
-                assert(mdef);\r
-#endif\r
-                n = khui_menu_get_size(mdef);\r
-                for (i=0; i < n; i++) {\r
-                    act = khui_menu_get_action(mdef, i);\r
-                    if (!(act->flags & KHUI_ACTIONREF_PACTION) &&\r
-                        (act->action == def_cmd))\r
-                        break;\r
-                }\r
-\r
-                if (i < n) {\r
-                    if (!(act->flags & KHUI_ACTIONREF_DEFAULT)) {\r
-                        khui_menu_remove_action(mdef, i);\r
-                        khui_menu_insert_action(mdef, i, def_cmd, KHUI_ACTIONREF_DEFAULT);\r
-                    } else {\r
-                        /* we are all set */\r
-                    }\r
-                } else {\r
-                    /* the default action was not found on the context\r
-                       menu */\r
-#ifdef DEBUG\r
-                    assert(FALSE);\r
-#endif\r
-                    khui_menu_insert_action(mdef, 0, def_cmd, KHUI_ACTIONREF_DEFAULT);\r
-                }\r
-\r
-                SetForegroundWindow(khm_hwnd_main);\r
-\r
-                GetCursorPos(&pt);\r
-                khm_menu_show_panel(menu_id, pt.x, pt.y);\r
-\r
-                PostMessage(khm_hwnd_main, WM_NULL, 0, 0);\r
-            }\r
-            break;\r
-\r
-        case NIN_SELECT:\r
-            /* fall through */\r
-        case NIN_KEYSELECT:\r
-            /* If there were any alerts waiting to be shown, we show\r
-               them.  Otherwise we perform the default action. */\r
-            khm_notify_icon_activate();\r
-            break;\r
-\r
-        case NIN_BALLOONUSERCLICK:\r
-            if (balloon_alert) {\r
-                khui_alert * a;\r
-\r
-                khm_notify_icon_change(KHERR_NONE);\r
-\r
-                a = balloon_alert;\r
-                balloon_alert = NULL;\r
-\r
-                khui_alert_lock(a);\r
-                a->displayed = FALSE;\r
-\r
-                if ((a->flags & KHUI_ALERT_FLAG_DEFACTION) &&\r
-                    !(a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW) &&\r
-                    a->n_alert_commands > 0) {\r
-                    PostMessage(khm_hwnd_main, WM_COMMAND,\r
-                                MAKEWPARAM(a->alert_commands[0], \r
-                                           0),\r
-                                0);\r
-                } else if (a->flags & \r
-                           KHUI_ALERT_FLAG_REQUEST_WINDOW) {\r
-                    khm_show_main_window();\r
-                    alert_show_normal(a);\r
-                }\r
-\r
-                khui_alert_unlock(a);\r
-                khui_alert_release(a);\r
-            } else {\r
-#ifdef DEBUG\r
-                assert(FALSE);\r
-#endif\r
-            }\r
-            break;\r
-\r
-        case NIN_BALLOONHIDE:\r
-        case NIN_BALLOONTIMEOUT:\r
-            khm_notify_icon_change(KHERR_NONE);\r
-            if (balloon_alert) {\r
-                khui_alert * a;\r
-                a = balloon_alert;\r
-                balloon_alert = NULL;\r
-\r
-                khui_alert_lock(a);\r
-                a->displayed = FALSE;\r
-                khui_alert_unlock(a);\r
-\r
-                khui_alert_release(a);\r
-            }\r
-            break;\r
-        }\r
-    } else if (uMsg == WM_TIMER) {\r
-        if (wParam == KHUI_TRIGGER_TIMER_ID) {\r
-            KillTimer(hwnd, KHUI_TRIGGER_TIMER_ID);\r
-            khm_timer_fire(hwnd);\r
-        } else if (wParam == KHUI_REFRESH_TIMER_ID) {\r
-            KillTimer(hwnd, KHUI_REFRESH_TIMER_ID);\r
-            kcdb_identity_refresh_all();\r
-            khm_timer_refresh(hwnd);\r
-        }\r
-    }\r
-\r
-    return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
-}\r
-\r
-ATOM \r
-khm_register_notifier_wnd_class(void)\r
-{\r
-    WNDCLASSEX wcx;\r
-\r
-    ZeroMemory(&wcx, sizeof(wcx));\r
-\r
-    wcx.cbSize = sizeof(wcx);\r
-    wcx.style = 0;\r
-    wcx.lpfnWndProc = notifier_wnd_proc;\r
-    wcx.cbClsExtra = 0;\r
-    wcx.cbWndExtra = 0;\r
-    wcx.hInstance = khm_hInstance;\r
-    wcx.hIcon = NULL;\r
-    wcx.hCursor = NULL;\r
-    wcx.hbrBackground = NULL;\r
-    wcx.lpszMenuName = NULL;\r
-    wcx.lpszClassName = KHUI_NOTIFIER_CLASS;\r
-    wcx.hIconSm = NULL;\r
-\r
-    atom_notifier = RegisterClassEx(&wcx);\r
-\r
-    return atom_notifier;\r
-}\r
-\r
-/*********************************************************************\r
-  Alerter\r
-**********************************************************************/\r
-\r
-typedef struct tag_alerter_alert_data {\r
-    khui_alert * alert;\r
-\r
-    BOOL         seen;          /* has the user seen this alert? */\r
-\r
-    BOOL         has_commands;  /* we cache the value here.  otherwise\r
-                                   we'll have to get a lock on the\r
-                                   alert each time we have to find out\r
-                                   whether there are any commands for\r
-                                   this alert. */\r
-\r
-    RECT         r_alert;    /* the entire alert, relative to self. */\r
-\r
-    /* the following rects are relative to the top left of r_alert. */\r
-\r
-    RECT         r_title;       /* the title.  deflate by padding to\r
-                                   get the text rect. */\r
-    RECT         r_icon;        /* rect for icon */\r
-    RECT         r_message;     /* rect for the text. no padding\r
-                                   necessary. */\r
-    RECT         r_suggestion;  /* rect for the suggestion.  deflate\r
-                                   by padding to get the suggestion\r
-                                   rect.  The suggestion rect includes\r
-                                   space for the small icon on the\r
-                                   left and padding between the icon\r
-                                   and the text. The size of the small\r
-                                   icon are as per system metrics\r
-                                   SM_C{X,Y}SMICON. Padding is\r
-                                   s_pad.cx vertical. */\r
-\r
-    int          n_cmd_buttons; /* number of command buttons in this alert. */\r
-\r
-    RECT         r_buttons[KHUI_MAX_ALERT_COMMANDS];\r
-                                /* rects for the command buttons. */\r
-\r
-    HWND         hwnd_buttons[KHUI_MAX_ALERT_COMMANDS];\r
-                                /* handles for the command buttons */\r
-\r
-    HWND         hwnd_marker;\r
-                                /* handle to the marker window used as\r
-                                   a tab-stop target when there are\r
-                                   not buttons associated with the\r
-                                   alert. */\r
-\r
-    LDCL(struct tag_alerter_alert_data);\r
-} alerter_alert_data;\r
-\r
-typedef struct tag_alerter_wnd_data {\r
-    HWND            hwnd;\r
-    HFONT           hfont;\r
-\r
-    wchar_t         caption[KHUI_MAXCCH_TITLE]; /* the original\r
-                                                   caption for the\r
-                                                   dialog. */\r
-\r
-    HWND            hw_bin;\r
-    HWND            hw_scroll;\r
-    HWND            hw_close;\r
-\r
-    int             scroll_top;\r
-\r
-    int             n_cmd_buttons; /* total number of command buttons\r
-                                      in all the alerts being shown in\r
-                                      this dialog. */\r
-\r
-    int             c_alert;    /* current selected alert. */\r
-\r
-    /* various metrics */\r
-    /* calculated during WM_CREATE */\r
-    SIZE            s_button;   /* minimum dimensions for command button */\r
-    SIZE            s_margin;\r
-    RECT            r_text;     /* only .left and .right are used. rest are 0 */\r
-    RECT            r_title;    /* only .left, .right and .bottom are used. .top=0 */\r
-    SIZE            s_icon;\r
-    SIZE            s_pad;\r
-\r
-    int             cx_wnd;\r
-    int             cy_max_wnd;\r
-\r
-    /* derived from the alert sizes */\r
-    SIZE            s_alerts;\r
-\r
-    QDCL(alerter_alert_data);   /* queue of alerts that are being\r
-                                   shown in this window. */\r
-\r
-    LDCL(struct tag_alerter_wnd_data); /* for adding to\r
-                                          khui_alert_windows list. */\r
-\r
-    int             n_alerts;\r
-\r
-} alerter_wnd_data;\r
-\r
-#define NTF_PARAM DWLP_USER\r
-\r
-/* dialog sizes in base dialog units */\r
-\r
-#define NTF_MARGIN          5\r
-#define NTF_WIDTH           200\r
-#define NTF_MAXHEIGHT       150\r
-\r
-#define NTF_TITLE_X         NTF_MARGIN\r
-#define NTF_TITLE_WIDTH     (NTF_WIDTH - NTF_MARGIN*2)\r
-#define NTF_TITLE_HEIGHT    10\r
-\r
-#define NTF_TEXT_PAD        2\r
-\r
-#define NTF_BUTTON_HEIGHT   14\r
-\r
-#define NTF_TIMEOUT 20000\r
-\r
-#define ALERT_WINDOW_EX_SYLES (WS_EX_DLGMODALFRAME | WS_EX_CONTEXTHELP)\r
-#define ALERT_WINDOW_STYLES   (WS_DLGFRAME | WS_POPUPWINDOW | WS_CLIPCHILDREN | DS_NOIDLEMSG)\r
-\r
-/* Control ids */\r
-#define IDC_NTF_ALERTBIN 998\r
-#define IDC_NTF_CLOSE    999\r
-\r
-#define IDC_NTF_CMDBUTTONS 1001\r
-#define IDC_FROM_IDX(alert, bn) ((alert) * (KHUI_MAX_ALERT_COMMANDS + 1) + (bn) + 1 + IDC_NTF_CMDBUTTONS)\r
-#define ALERT_FROM_IDC(idc)  (((idc) - IDC_NTF_CMDBUTTONS) / (KHUI_MAX_ALERT_COMMANDS + 1))\r
-#define BUTTON_FROM_IDC(idc) (((idc) - IDC_NTF_CMDBUTTONS) % (KHUI_MAX_ALERT_COMMANDS + 1) - 1)\r
-\r
-/* if the only command in an alert is "Close", we assume that the\r
-   alert has no commands. */\r
-#define ALERT_HAS_CMDS(a) ((a)->n_alert_commands > 1 || ((a)->n_alert_commands == 1 && (a)->alert_commands[0] != KHUI_PACTION_CLOSE))\r
-\r
-#define SCROLL_LINE_SIZE(d) ((d)->cy_max_wnd / 12)\r
-\r
-static void\r
-add_alert_to_wnd_data(alerter_wnd_data * d,\r
-                      khui_alert * a) {\r
-    alerter_alert_data * aiter;\r
-    khm_boolean exists = 0;\r
-\r
-    khui_alert_lock(a);\r
-\r
-    /* check if the alert is already there */\r
-    aiter = QTOP(d);\r
-    while(aiter && !exists) {\r
-        if (aiter->alert) {\r
-            khui_alert_lock(aiter->alert);\r
-\r
-            if (alert_is_equal(aiter->alert, a)) {\r
-                exists = TRUE;\r
-            }\r
-\r
-            khui_alert_unlock(aiter->alert);\r
-        }\r
-\r
-        aiter = QNEXT(aiter);\r
-    }\r
-\r
-    a->flags |= KHUI_ALERT_FLAG_DISPLAY_WINDOW;\r
-\r
-    if (!exists) {\r
-        a->displayed = TRUE;\r
-    }\r
-\r
-    khui_alert_unlock(a);\r
-\r
-    if (!exists) {\r
-        alerter_alert_data * adata;\r
-\r
-        adata = PMALLOC(sizeof(*adata));\r
-        ZeroMemory(adata, sizeof(*adata));\r
-\r
-        adata->alert = a;\r
-        khui_alert_hold(a);\r
-\r
-        QPUT(d, adata);\r
-        d->n_alerts ++;\r
-    }\r
-}\r
-\r
-static alerter_wnd_data *\r
-create_alerter_wnd_data(HWND hwnd, alert_list * l) {\r
-    alerter_wnd_data * d;\r
-    int i;\r
-    LONG dlgb;\r
-\r
-    d = PMALLOC(sizeof(*d));\r
-    ZeroMemory(d, sizeof(*d));\r
-\r
-    d->hwnd = hwnd;\r
-\r
-    GetWindowText(hwnd, d->caption, ARRAYLENGTH(d->caption));\r
-\r
-    for (i=0; i < l->n_alerts; i++) {\r
-        add_alert_to_wnd_data(d, l->alerts[i]);\r
-    }\r
-\r
-    d->n_alerts = l->n_alerts;\r
-\r
-    LPUSH(&khui_alert_windows, d);\r
-\r
-    /* Compute a few metrics first */\r
-\r
-    dlgb = GetDialogBaseUnits();\r
-\r
-#define DLG2SCNX(x) MulDiv((x), LOWORD(dlgb), 4)\r
-#define DLG2SCNY(y) MulDiv((y), HIWORD(dlgb), 8)\r
-\r
-    d->cx_wnd = DLG2SCNX(NTF_WIDTH);\r
-    d->cy_max_wnd = DLG2SCNY(NTF_MAXHEIGHT);\r
-\r
-    d->s_margin.cx = DLG2SCNX(NTF_MARGIN);\r
-    d->s_margin.cy = DLG2SCNY(NTF_MARGIN);\r
-\r
-    d->r_title.left = DLG2SCNX(NTF_TITLE_X);\r
-    d->r_title.right = DLG2SCNX(NTF_TITLE_X + NTF_TITLE_WIDTH);\r
-    d->r_title.top = 0;\r
-    d->r_title.bottom = DLG2SCNY(NTF_TITLE_HEIGHT);\r
-\r
-    d->s_pad.cx = DLG2SCNX(NTF_TEXT_PAD);\r
-    d->s_pad.cy = DLG2SCNY(NTF_TEXT_PAD);\r
-\r
-    d->s_icon.cx = GetSystemMetrics(SM_CXICON);\r
-    d->s_icon.cy = GetSystemMetrics(SM_CYICON);\r
-\r
-    d->r_text.left = d->s_margin.cx * 2 + d->s_icon.cx;\r
-    d->r_text.right = d->cx_wnd - d->s_margin.cx;\r
-    d->r_text.top = 0;\r
-    d->r_text.bottom = 0;\r
-\r
-    d->s_button.cx = ((d->r_text.right - d->r_text.left) - (KHUI_MAX_ALERT_COMMANDS - 1) * d->s_margin.cx) / KHUI_MAX_ALERT_COMMANDS;\r
-    d->s_button.cy = DLG2SCNY(NTF_BUTTON_HEIGHT);\r
-\r
-#undef DLG2SCNX\r
-#undef DLG2SCNY\r
-\r
-    d->c_alert = -1;\r
-\r
-    return d;\r
-}\r
-\r
-static void\r
-layout_alert(HDC hdc, alerter_wnd_data * d,\r
-             alerter_alert_data * adata) {\r
-    RECT r;\r
-    size_t len;\r
-    int y;\r
-    int icon_y;\r
-\r
-#ifdef DEBUG\r
-    assert(adata->alert);\r
-#endif\r
-\r
-    khui_alert_lock(adata->alert);\r
-\r
-    y = 0;\r
-\r
-    /* Title */\r
-\r
-    y += d->s_margin.cy;\r
-\r
-    /* If there is a title and it differs from the title of the\r
-       alerter window, then we have to show the alert title\r
-       separately. */\r
-    if (adata->alert->title &&\r
-        wcscmp(adata->alert->title, d->caption)) {\r
-\r
-        CopyRect(&adata->r_title, &d->r_title);\r
-        OffsetRect(&adata->r_title, 0, y);\r
-\r
-        y = adata->r_title.bottom + d->s_margin.cy;\r
-\r
-    } else {\r
-\r
-        SetRectEmpty(&adata->r_title);\r
-\r
-    }\r
-\r
-    /* Icon */\r
-\r
-    SetRect(&adata->r_icon, d->s_margin.cx, y,\r
-            d->s_margin.cx + d->s_icon.cx,\r
-            y + d->s_icon.cy);\r
-\r
-    icon_y = adata->r_icon.bottom + d->s_margin.cy; /* the bottom of the icon */\r
-\r
-    /* Message */\r
-\r
-    if (adata->alert->message &&\r
-        SUCCEEDED(StringCchLength(adata->alert->message,\r
-                                  KHUI_MAXCCH_MESSAGE,\r
-                                  &len))) {\r
-\r
-        CopyRect(&r, &d->r_text);\r
-\r
-        DrawTextEx(hdc, adata->alert->message, (int) len,\r
-                   &r,\r
-                   DRAWTEXTOPTIONS,\r
-                   NULL);\r
-\r
-        OffsetRect(&r, 0, y);\r
-        CopyRect(&adata->r_message, &r);\r
-\r
-        y = r.bottom + d->s_margin.cy;\r
-\r
-    } else {\r
-\r
-        SetRectEmpty(&adata->r_message);\r
-\r
-    }\r
-\r
-    /* Suggestion */\r
-\r
-    if (adata->alert->suggestion &&\r
-        SUCCEEDED(StringCchLength(adata->alert->suggestion,\r
-                                  KHUI_MAXCCH_SUGGESTION,\r
-                                  &len))) {\r
-        int pad = d->s_pad.cx + GetSystemMetrics(SM_CXSMICON);\r
-\r
-        CopyRect(&r, &d->r_text);\r
-        r.left += pad;\r
-\r
-        DrawTextEx(hdc, adata->alert->suggestion, (int) len,\r
-                   &r,\r
-                   DRAWTEXTOPTIONS,\r
-                   NULL);\r
-\r
-        r.left -= pad;\r
-\r
-        InflateRect(&r, d->s_pad.cx, d->s_pad.cy);\r
-        OffsetRect(&r, 0, -r.top + y);\r
-        CopyRect(&adata->r_suggestion, &r);\r
-\r
-        y = r.bottom + d->s_margin.cy;\r
-\r
-    } else {\r
-\r
-        SetRectEmpty(&adata->r_suggestion);\r
-\r
-    }\r
-\r
-    y = max(y, icon_y);\r
-\r
-    /* Buttons */\r
-\r
-    if (ALERT_HAS_CMDS(adata->alert)) {\r
-        khm_int32 i;\r
-        int x, width;\r
-        wchar_t caption[KHUI_MAXCCH_SHORT_DESC];\r
-        size_t len;\r
-        SIZE s;\r
-        int skip_close;\r
-\r
-        adata->has_commands = TRUE;\r
-\r
-        if (d->n_alerts > 1)\r
-            skip_close = TRUE;\r
-        else\r
-            skip_close = FALSE;\r
-\r
-        x = d->r_text.left;\r
-\r
-#ifdef DEBUG\r
-        assert(adata->alert->n_alert_commands <= KHUI_MAX_ALERT_COMMANDS);\r
-#endif\r
-\r
-        for (i=0; i < adata->alert->n_alert_commands; i++) {\r
-\r
-            if (adata->alert->alert_commands[i] == KHUI_PACTION_CLOSE && skip_close) {\r
-                SetRectEmpty(&adata->r_buttons[i]);\r
-                continue;\r
-            }\r
-\r
-            caption[0] = L'\0';\r
-            len = 0;\r
-            khm_get_action_caption(adata->alert->alert_commands[i],\r
-                                   caption, sizeof(caption));\r
-            StringCchLength(caption, ARRAYLENGTH(caption), &len);\r
-\r
-            if (!GetTextExtentPoint32(hdc, caption, (int) len, &s)) {\r
-                width = d->s_button.cx;\r
-            } else {\r
-                width = s.cx + d->s_margin.cx * 2;\r
-            }\r
-\r
-            if (width < d->s_button.cx)\r
-                width = d->s_button.cx;\r
-            else if (width > (d->r_text.right - d->r_text.left))\r
-                width = d->r_text.right - d->r_text.left;\r
-\r
-            if (x + width > d->r_text.right) {\r
-                /* new line */\r
-                x = d->r_text.left;\r
-                y += d->s_button.cy + d->s_pad.cy;\r
-            }\r
-\r
-            SetRect(&adata->r_buttons[i], x, y, x + width, y + d->s_button.cy);\r
-\r
-            x += width + d->s_margin.cx;\r
-        }\r
-\r
-        y += d->s_button.cy + d->s_margin.cy;\r
-    }\r
-\r
-    khui_alert_unlock(adata->alert);\r
-\r
-    /* Now set the rect for the whole alert */\r
-    SetRect(&adata->r_alert, 0, 0, d->cx_wnd, y);\r
-\r
-}\r
-\r
-static void\r
-pick_title_for_alerter_window(alerter_wnd_data * d) {\r
-    alerter_alert_data * adata;\r
-    wchar_t caption[KHUI_MAXCCH_TITLE];\r
-    khm_boolean common_caption = TRUE;\r
-    khui_alert_type ctype = KHUI_ALERTTYPE_NONE;\r
-    khm_boolean common_type = TRUE;\r
-\r
-    /* - If all the alerts have the same title, then we use the common\r
-         title.\r
-\r
-       - If all the alerts are of the same type, then we pick a title\r
-         that is suitable for the type.\r
-\r
-       - All else fails, we use a default caption for the window.\r
-    */\r
-\r
-    caption[0] = L'\0';\r
-    adata = QTOP(d);\r
-    while (adata && (common_caption || common_type)) {\r
-\r
-        if (adata->alert) {\r
-            khui_alert_lock(adata->alert);\r
-\r
-            if (common_caption) {\r
-                if (caption[0] == L'\0') {\r
-                    if (adata->alert->title)\r
-                        StringCbCopy(caption, sizeof(caption), adata->alert->title);\r
-                } else if (adata->alert->title &&\r
-                           wcscmp(caption, adata->alert->title)) {\r
-                    common_caption = FALSE;\r
-                }\r
-            }\r
-\r
-            if (common_type) {\r
-                if (ctype == KHUI_ALERTTYPE_NONE)\r
-                    ctype = adata->alert->alert_type;\r
-                else if (ctype != adata->alert->alert_type)\r
-                    common_type = FALSE;\r
-            }\r
-\r
-            khui_alert_unlock(adata->alert);\r
-        }\r
-\r
-        adata = QNEXT(adata);\r
-    }\r
-\r
-    /* just in case someone changes d->caption to a pointer from an\r
-       array */\r
-#ifdef DEBUG\r
-    assert(sizeof(d->caption) > sizeof(wchar_t *));\r
-#endif\r
-\r
-    if (common_caption && caption[0] != L'\0') {\r
-        StringCbCopy(d->caption, sizeof(d->caption), caption);\r
-    } else if (common_type && ctype != KHUI_ALERTTYPE_NONE) {\r
-        switch(ctype) {\r
-        case KHUI_ALERTTYPE_PLUGIN:\r
-            LoadString(khm_hInstance, IDS_ALERTTYPE_PLUGIN,\r
-                       d->caption, ARRAYLENGTH(d->caption));\r
-            break;\r
-\r
-        case KHUI_ALERTTYPE_EXPIRE:\r
-            LoadString(khm_hInstance, IDS_ALERTTYPE_EXPIRE,\r
-                       d->caption, ARRAYLENGTH(d->caption));\r
-            break;\r
-\r
-        case KHUI_ALERTTYPE_RENEWFAIL:\r
-            LoadString(khm_hInstance, IDS_ALERTTYPE_RENEWFAIL,\r
-                       d->caption, ARRAYLENGTH(d->caption));\r
-            break;\r
-\r
-        case KHUI_ALERTTYPE_ACQUIREFAIL:\r
-            LoadString(khm_hInstance, IDS_ALERTTYPE_ACQUIREFAIL,\r
-                       d->caption, ARRAYLENGTH(d->caption));\r
-            break;\r
-\r
-        case KHUI_ALERTTYPE_CHPW:\r
-            LoadString(khm_hInstance, IDS_ALERTTYPE_CHPW,\r
-                       d->caption, ARRAYLENGTH(d->caption));\r
-            break;\r
-\r
-        default:\r
-            LoadString(khm_hInstance, IDS_ALERT_DEFAULT,\r
-                       d->caption, ARRAYLENGTH(d->caption));\r
-        }\r
-    } else {\r
-        LoadString(khm_hInstance, IDS_ALERT_DEFAULT,\r
-                   d->caption, ARRAYLENGTH(d->caption));\r
-    }\r
-\r
-    SetWindowText(d->hwnd, d->caption);\r
-}\r
-\r
-static void\r
-estimate_alerter_wnd_sizes(alerter_wnd_data * d) {\r
-    HDC hdc;\r
-    HFONT hf_old;\r
-    int height = 0;\r
-\r
-    alerter_alert_data * adata;\r
-\r
-    pick_title_for_alerter_window(d);\r
-\r
-    hdc = GetDC(d->hwnd);\r
-#ifdef DEBUG\r
-    assert(hdc);\r
-#endif\r
-\r
-    if (d->hfont == NULL)\r
-        d->hfont = (HFONT) GetStockObject(DEFAULT_GUI_FONT);\r
-\r
-#ifdef DEBUG\r
-    assert(d->hfont);\r
-#endif\r
-\r
-    hf_old = SelectFont(hdc, d->hfont);\r
-\r
-    adata = QTOP(d);\r
-    while(adata) {\r
-        layout_alert(hdc, d, adata);\r
-\r
-        height += adata->r_alert.bottom;\r
-\r
-        adata = QNEXT(adata);\r
-    }\r
-\r
-    SelectFont(hdc, hf_old);\r
-    ReleaseDC(d->hwnd, hdc);\r
-\r
-    d->s_alerts.cx = d->cx_wnd;\r
-    d->s_alerts.cy = height;\r
-}\r
-\r
-static void\r
-layout_command_buttons(alerter_wnd_data * d) {\r
-\r
-    alerter_alert_data * adata;\r
-    HDWP hdefer;\r
-    int y;\r
-\r
-    hdefer = BeginDeferWindowPos(d->n_cmd_buttons);\r
-\r
-    y = 0;\r
-    adata = QTOP(d);\r
-    while (adata) {\r
-        RECT r;\r
-        int i;\r
-\r
-        if (!adata->has_commands)\r
-            goto done;\r
-\r
-        for (i=0; i < adata->n_cmd_buttons; i++) {\r
-            if (IsRectEmpty(&adata->r_buttons[i])) {\r
-                /* the button is no longer needed */\r
-                if (adata->hwnd_buttons[i] != NULL) {\r
-                    DestroyWindow(adata->hwnd_buttons[i]);\r
-                    adata->hwnd_buttons[i] = NULL;\r
-                }\r
-\r
-                continue;\r
-            }\r
-\r
-            if (adata->hwnd_buttons[i] == NULL) {\r
-                continue;\r
-            }\r
-\r
-            CopyRect(&r, &adata->r_buttons[i]);\r
-            OffsetRect(&r, 0, y - d->scroll_top);\r
-\r
-            DeferWindowPos(hdefer,\r
-                           adata->hwnd_buttons[i], NULL,\r
-                           r.left, r.top, 0, 0,\r
-                           SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER |\r
-                           SWP_NOSIZE);\r
-        }\r
-\r
-    done:\r
-        y += adata->r_alert.bottom;\r
-        adata = QNEXT(adata);\r
-    }\r
-\r
-    EndDeferWindowPos(hdefer);\r
-}\r
-\r
-static void\r
-setup_alerter_window_controls(alerter_wnd_data * d) {\r
-\r
-    RECT r_alerts;\r
-    RECT r_window;\r
-    RECT r_client;\r
-    RECT r_parent;\r
-    HWND hw_parent;\r
-    HWND hw_focus = NULL;\r
-    BOOL close_button = FALSE;\r
-    BOOL scrollbar = FALSE;\r
-    BOOL redraw_scollbar = FALSE;\r
-\r
-    /* estimate_alerter_wnd_sizes() must be called before calling\r
-       this. */\r
-#ifdef DEBUG\r
-    assert(d->s_alerts.cy > 0);\r
-#endif\r
-\r
-    r_alerts.left = 0;\r
-    r_alerts.top = 0;\r
-    r_alerts.right = d->cx_wnd;\r
-\r
-    if (d->s_alerts.cy > d->cy_max_wnd) {\r
-\r
-        BOOL redraw = FALSE;\r
-\r
-        r_alerts.right += GetSystemMetrics(SM_CXVSCROLL);\r
-        r_alerts.bottom = d->cy_max_wnd;\r
-\r
-        CopyRect(&r_client, &r_alerts);\r
-        r_client.bottom += d->s_margin.cy + d->s_button.cy + d->s_pad.cy;\r
-        close_button = TRUE;\r
-\r
-        if (d->scroll_top > d->s_alerts.cy - d->cy_max_wnd)\r
-            d->scroll_top = d->s_alerts.cy - d->cy_max_wnd;\r
-\r
-        scrollbar = TRUE;\r
-    } else {\r
-        r_alerts.bottom = d->s_alerts.cy;\r
-\r
-        CopyRect(&r_client, &r_alerts);\r
-\r
-        if (d->n_alerts == 1) {\r
-\r
-            if (!QTOP(d)->has_commands) {\r
-                r_client.bottom += d->s_margin.cy * 2 + d->s_button.cy;\r
-                close_button = TRUE;\r
-            }\r
-\r
-        } else {\r
-\r
-            r_client.bottom += d->s_margin.cy * 2 + d->s_button.cy;\r
-            close_button = TRUE;\r
-        }\r
-\r
-        d->scroll_top = 0;\r
-    }\r
-\r
-    if (d->hw_bin == NULL) {\r
-        d->hw_bin = CreateWindowEx(WS_EX_CONTROLPARENT,\r
-                                   MAKEINTATOM(atom_alert_bin),\r
-                                   L"Alert Container",\r
-                                   WS_CHILD | WS_CLIPCHILDREN |\r
-                                   WS_VISIBLE |\r
-                                   ((scrollbar)? WS_VSCROLL : 0),\r
-                                   r_alerts.left, r_alerts.top,\r
-                                   r_alerts.right - r_alerts.left,\r
-                                   r_alerts.bottom - r_alerts.top,\r
-                                   d->hwnd,\r
-                                   (HMENU) IDC_NTF_ALERTBIN,\r
-                                   khm_hInstance,\r
-                                   (LPVOID) d);\r
-    } else {\r
-        redraw_scollbar = TRUE;\r
-        SetWindowLongPtr(d->hw_bin, GWL_STYLE,\r
-                         WS_CHILD | WS_CLIPCHILDREN |\r
-                         WS_VISIBLE |\r
-                         ((scrollbar)? WS_VSCROLL : 0));\r
-        SetWindowPos(d->hw_bin, NULL,\r
-                     r_alerts.left, r_alerts.top,\r
-                     r_alerts.right - r_alerts.left,\r
-                     r_alerts.bottom - r_alerts.top,\r
-                     SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOACTIVATE);\r
-    }\r
-\r
-    if (scrollbar) {\r
-        SCROLLINFO si;\r
-\r
-        ZeroMemory(&si, sizeof(si));\r
-        si.cbSize = sizeof(si);\r
-        si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;\r
-        si.nMin = 0;\r
-        si.nMax = d->s_alerts.cy;\r
-        si.nPage = d->cy_max_wnd;\r
-        si.nPos = d->scroll_top;\r
-\r
-        SetScrollInfo(d->hw_bin, SB_VERT, &si, redraw_scollbar);\r
-    }\r
-\r
-    /* create the action buttons */\r
-    {\r
-        alerter_alert_data * adata;\r
-        int y;\r
-        int idx;\r
-        HWND last_window = HWND_TOP;\r
-        int n_buttons = 0;\r
-\r
-        idx = 0;\r
-        y = - d->scroll_top;\r
-        adata = QTOP(d);\r
-        while(adata) {\r
-            if (adata->has_commands) {\r
-                int i;\r
-                wchar_t caption[KHUI_MAXCCH_SHORT_DESC];\r
-                RECT r;\r
-\r
-                if (adata->hwnd_marker) {\r
-                    DestroyWindow(adata->hwnd_marker);\r
-                    adata->hwnd_marker = NULL;\r
-                }\r
-\r
-                khui_alert_lock(adata->alert);\r
-\r
-                adata->n_cmd_buttons = adata->alert->n_alert_commands;\r
-\r
-                for (i=0; i < adata->alert->n_alert_commands; i++) {\r
-\r
-                    n_buttons ++;\r
-\r
-                    if (IsRectEmpty(&adata->r_buttons[i])) {\r
-                        /* this button is not necessary */\r
-                        if (adata->hwnd_buttons[i]) {\r
-                            DestroyWindow(adata->hwnd_buttons[i]);\r
-                            adata->hwnd_buttons[i] = NULL;\r
-                        }\r
-\r
-                        continue;\r
-                    }\r
-\r
-                    if (adata->hwnd_buttons[i] != NULL) {\r
-                        /* already there */\r
-                        CopyRect(&r, &adata->r_buttons[i]);\r
-                        OffsetRect(&r, 0, y);\r
-\r
-                        SetWindowPos(adata->hwnd_buttons[i], last_window,\r
-                                     r.left, r.top,\r
-                                     r.right - r.left,\r
-                                     r.bottom - r.top,\r
-                                     SWP_NOACTIVATE | SWP_NOOWNERZORDER |\r
-                                     SWP_SHOWWINDOW);\r
-\r
-                        last_window = adata->hwnd_buttons[i];\r
-\r
-                        if (hw_focus == NULL)\r
-                            hw_focus = adata->hwnd_buttons[i];\r
-\r
-                        continue;\r
-                    }\r
-\r
-                    khm_get_action_caption(adata->alert->alert_commands[i],\r
-                                           caption, sizeof(caption));\r
-\r
-                    CopyRect(&r, &adata->r_buttons[i]);\r
-                    OffsetRect(&r, 0, y);\r
-\r
-                    adata->hwnd_buttons[i] =\r
-                        CreateWindowEx(0,\r
-                                       L"BUTTON",\r
-                                       caption,\r
-                                       WS_CHILD | WS_TABSTOP | BS_NOTIFY,\r
-                                       r.left, r.top,\r
-                                       r.right - r.left,\r
-                                       r.bottom - r.top,\r
-                                       d->hw_bin,\r
-                                       (HMENU) (INT_PTR) IDC_FROM_IDX(idx, i),\r
-                                       khm_hInstance,\r
-                                       NULL);\r
-#ifdef DEBUG\r
-                    assert(adata->hwnd_buttons[i]);\r
-#endif\r
-\r
-                    if (d->hfont) {\r
-                        SendMessage(adata->hwnd_buttons[i], WM_SETFONT,\r
-                                    (WPARAM) d->hfont, FALSE);\r
-                    }\r
-\r
-                    SetWindowPos(adata->hwnd_buttons[i], last_window,\r
-                                 0, 0, 0, 0,\r
-                                 SWP_NOACTIVATE | SWP_NOOWNERZORDER |\r
-                                 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);\r
-\r
-                    last_window = adata->hwnd_buttons[i];\r
-\r
-                    if (hw_focus == NULL)\r
-                        hw_focus = adata->hwnd_buttons[i];\r
-                }\r
-\r
-                khui_alert_unlock(adata->alert);\r
-            } else {\r
-                int i;\r
-\r
-                /* Destroy any buttons that belong to the alert. We\r
-                   might have some left over, if there were command\r
-                   belonging to the alert that were ignored.*/\r
-\r
-                for (i=0; i < adata->n_cmd_buttons; i++) {\r
-                    if (adata->hwnd_buttons[i]) {\r
-                        DestroyWindow(adata->hwnd_buttons[i]);\r
-                        adata->hwnd_buttons[i] = NULL;\r
-                    }\r
-                }\r
-\r
-                adata->n_cmd_buttons = 0;\r
-\r
-                if (adata->hwnd_marker == NULL) {\r
-                    adata->hwnd_marker =\r
-                        CreateWindowEx(0,\r
-                                       L"BUTTON",\r
-                                       L"Marker",\r
-                                       WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_NOTIFY,\r
-                                       -10, 0,\r
-                                       5, 5,\r
-                                       d->hw_bin,\r
-                                       (HMENU) (INT_PTR) IDC_FROM_IDX(idx, -1),\r
-                                       khm_hInstance,\r
-                                       NULL);\r
-#ifdef DEBUG\r
-                    assert(adata->hwnd_marker);\r
-#endif\r
-                }\r
-\r
-                SetWindowPos(adata->hwnd_marker, last_window,\r
-                             0, 0, 0, 0,\r
-                             SWP_NOACTIVATE | SWP_NOOWNERZORDER |\r
-                             SWP_NOMOVE | SWP_NOSIZE);\r
-\r
-                last_window = adata->hwnd_marker;\r
-\r
-                if (scrollbar) {\r
-                    EnableWindow(adata->hwnd_marker, TRUE);\r
-                    if (hw_focus == NULL)\r
-                        hw_focus = adata->hwnd_marker;\r
-                } else {\r
-                    EnableWindow(adata->hwnd_marker, FALSE);\r
-                }\r
-            }\r
-\r
-            y += adata->r_alert.bottom;\r
-            adata = QNEXT(adata);\r
-            idx++;\r
-        }\r
-\r
-        d->n_cmd_buttons = n_buttons;\r
-    }\r
-\r
-    if (close_button) {\r
-        if (d->hw_close == NULL) {\r
-            wchar_t caption[256];\r
-\r
-            khm_get_action_caption(KHUI_PACTION_CLOSE, caption, sizeof(caption));\r
-\r
-            d->hw_close = CreateWindowEx(0,\r
-                                         L"BUTTON",\r
-                                         caption,\r
-                                         WS_CHILD | BS_DEFPUSHBUTTON | WS_TABSTOP | BS_NOTIFY,\r
-                                         0,0,100,100,\r
-                                         d->hwnd,\r
-                                         (HMENU) IDC_NTF_CLOSE,\r
-                                         khm_hInstance,\r
-                                         NULL);\r
-\r
-#ifdef DEBUG\r
-            assert(d->hw_close);\r
-            assert(d->hfont);\r
-#endif\r
-            if (d->hfont)\r
-                SendMessage(d->hw_close, WM_SETFONT, (WPARAM) d->hfont, FALSE);\r
-        }\r
-\r
-        {\r
-            int x,y,width,height;\r
-\r
-            x = d->r_text.left;\r
-            y = r_client.bottom - (d->s_margin.cy + d->s_button.cy);\r
-            width = d->s_button.cx;\r
-            height = d->s_button.cy;\r
-\r
-            SetWindowPos(d->hw_close, NULL,\r
-                         x, y, width, height,\r
-                         SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER |\r
-                         SWP_SHOWWINDOW);\r
-        }\r
-\r
-        if (hw_focus == NULL || d->n_cmd_buttons == 0)\r
-            hw_focus = d->hw_close;\r
-\r
-    } else {\r
-        if (d->hw_close != NULL) {\r
-            DestroyWindow(d->hw_close);\r
-            d->hw_close = NULL;\r
-        }\r
-    }\r
-\r
-    CopyRect(&r_window, &r_client);\r
-    AdjustWindowRectEx(&r_window, ALERT_WINDOW_STYLES,\r
-                       FALSE, ALERT_WINDOW_EX_SYLES);\r
-    OffsetRect(&r_window, -r_window.left, -r_window.top);\r
-\r
-    /* center the window above the parent window. */\r
-\r
-    hw_parent = GetWindow(d->hwnd, GW_OWNER);\r
-    GetWindowRect(hw_parent, &r_parent);\r
-\r
-    {\r
-        int x,y;\r
-\r
-        x = (r_parent.left + r_parent.right - (r_window.right - r_window.left)) / 2;\r
-        y = (r_parent.top + r_parent.bottom - (r_window.bottom - r_window.top)) / 2;\r
-\r
-        SetWindowPos(d->hwnd,\r
-                     HWND_TOP,\r
-                     x, y,\r
-                     r_window.right - r_window.left,\r
-                     r_window.bottom - r_window.top,\r
-                     SWP_SHOWWINDOW | SWP_NOOWNERZORDER);\r
-    }\r
-\r
-    if (hw_focus != NULL)\r
-        PostMessage(d->hwnd, WM_NEXTDLGCTL, (WPARAM) hw_focus, MAKELPARAM(TRUE, 0));\r
-}\r
-\r
-static void\r
-scroll_to_position(alerter_wnd_data * d, int new_pos, khm_boolean redraw_scrollbar) {\r
-    int delta;\r
-    SCROLLINFO si;\r
-    HWND hwnd = d->hw_bin;\r
-\r
-    if (new_pos < 0)\r
-        new_pos = 0;\r
-    else if (new_pos > d->s_alerts.cy - d->cy_max_wnd)\r
-        new_pos = d->s_alerts.cy - d->cy_max_wnd;\r
-\r
-    if (new_pos == d->scroll_top)\r
-        return;\r
-\r
-    delta = d->scroll_top - new_pos;\r
-\r
-    d->scroll_top -= delta;\r
-\r
-    ScrollWindowEx(hwnd, 0, delta,\r
-                   NULL, NULL, NULL, NULL,\r
-                   SW_INVALIDATE | SW_ERASE);\r
-\r
-    layout_command_buttons(d);\r
-\r
-    ZeroMemory(&si, sizeof(si));\r
-\r
-    si.fMask = SIF_POS;\r
-    si.nPos = d->scroll_top;\r
-\r
-    SetScrollInfo(hwnd, SB_VERT, &si, redraw_scrollbar);\r
-}\r
-\r
-static void\r
-select_alert(alerter_wnd_data * d, int alert) {\r
-\r
-    int y;\r
-    RECT old_sel, new_sel;\r
-    alerter_alert_data * adata;\r
-    int idx;\r
-\r
-    if (d->n_alerts == 1 ||\r
-        alert < 0 ||\r
-        alert > d->n_alerts ||\r
-        d->c_alert == alert)\r
-        return;\r
-\r
-    SetRectEmpty(&old_sel);\r
-    SetRectEmpty(&new_sel);\r
-    idx = 0; y = -d->scroll_top;\r
-    adata = QTOP(d);\r
-    while(adata && (idx <= d->c_alert || idx <= alert)) {\r
-\r
-        if (idx == d->c_alert) {\r
-            CopyRect(&old_sel, &adata->r_alert);\r
-            OffsetRect(&old_sel, 0, y);\r
-        }\r
-\r
-        if (idx == alert) {\r
-            CopyRect(&new_sel, &adata->r_alert);\r
-            OffsetRect(&new_sel, 0, y);\r
-        }\r
-\r
-        y += adata->r_alert.bottom;\r
-        idx ++;\r
-        adata = QNEXT(adata);\r
-    }\r
-\r
-    d->c_alert = alert;\r
-    if (!IsRectEmpty(&old_sel))\r
-        InvalidateRect(d->hw_bin, &old_sel, TRUE);\r
-    if (!IsRectEmpty(&new_sel))\r
-        InvalidateRect(d->hw_bin, &new_sel, TRUE);\r
-}\r
-\r
-static void\r
-ensure_command_is_visible(alerter_wnd_data * d, int id) {\r
-    int alert_idx;\r
-    int y = 0;\r
-    alerter_alert_data * adata;\r
-    int new_pos = 0;\r
-\r
-    alert_idx = ALERT_FROM_IDC(id);\r
-\r
-#ifdef DEBUG\r
-    assert(alert_idx >= 0 && alert_idx < d->n_alerts);\r
-#endif\r
-    if (alert_idx >= d->n_alerts || alert_idx < 0)\r
-        return;\r
-\r
-    adata = QTOP(d);\r
-    while(adata && alert_idx > 0) {\r
-        y += adata->r_alert.bottom;\r
-        alert_idx--;\r
-        adata = QNEXT(adata);\r
-    }\r
-\r
-#ifdef DEBUG\r
-    assert(alert_idx == 0);\r
-    assert(adata);\r
-    assert(adata->alert);\r
-#endif\r
-    if (adata == NULL || alert_idx != 0)\r
-        return;\r
-\r
-    new_pos = d->scroll_top;\r
-    if (y < d->scroll_top) {\r
-        new_pos = y;\r
-    } else if (y + adata->r_alert.bottom > d->scroll_top + d->cy_max_wnd) {\r
-        new_pos = y + adata->r_alert.bottom - d->cy_max_wnd;\r
-    }\r
-\r
-    if (new_pos != d->scroll_top)\r
-        scroll_to_position(d, new_pos, TRUE);\r
-\r
-    select_alert(d, ALERT_FROM_IDC(id));\r
-}\r
-\r
-static void\r
-handle_mouse_select(alerter_wnd_data * d, int mouse_x, int mouse_y) {\r
-    int y;\r
-    alerter_alert_data * adata;\r
-\r
-    y = -d->scroll_top;\r
-    adata = QTOP(d);\r
-    while(adata) {\r
-        if (y <= mouse_y && (y + adata->r_alert.bottom) > mouse_y) {\r
-            HWND hw = NULL;\r
-\r
-            if (adata->n_cmd_buttons > 0)\r
-                hw = adata->hwnd_buttons[0];\r
-            else\r
-                hw = adata->hwnd_marker;\r
-\r
-            if (hw && !IsWindowEnabled(hw))\r
-                hw = GetNextDlgTabItem(d->hwnd, hw, FALSE);\r
-\r
-            if (hw)\r
-                PostMessage(d->hwnd, WM_NEXTDLGCTL, (WPARAM) hw, MAKELPARAM(TRUE, 0));\r
-\r
-            return;\r
-        }\r
-\r
-        y += adata->r_alert.bottom;\r
-        adata = QNEXT(adata);\r
-    }\r
-}\r
-\r
-static void\r
-process_command_button(alerter_wnd_data * d, int id) {\r
-    int alert_idx;\r
-    int cmd_idx;\r
-    khm_int32 flags = 0;\r
-    khm_int32 cmd = 0;\r
-    alerter_alert_data * adata;\r
-    int i;\r
-\r
-    alert_idx = ALERT_FROM_IDC(id);\r
-    cmd_idx = BUTTON_FROM_IDC(id);\r
-\r
-#ifdef DEBUG\r
-    assert(alert_idx >= 0 && alert_idx < d->n_alerts);\r
-#endif\r
-    if (alert_idx >= d->n_alerts || alert_idx < 0)\r
-        return;\r
-\r
-    if (cmd_idx < 0) {\r
-        /* the user selected a marker button.  Nothing to do. */\r
-        return;\r
-    }\r
-\r
-    adata = QTOP(d);\r
-    while(adata && alert_idx > 0) {\r
-        alert_idx--;\r
-        adata = QNEXT(adata);\r
-    }\r
-\r
-#ifdef DEBUG\r
-    assert(alert_idx == 0);\r
-    assert(adata);\r
-    assert(adata->alert);\r
-#endif\r
-    if (adata == NULL || alert_idx != 0)\r
-        return;\r
-\r
-    khui_alert_lock(adata->alert);\r
-#ifdef DEBUG\r
-    assert(cmd_idx >= 0 && cmd_idx < adata->alert->n_alert_commands);\r
-#endif\r
-\r
-    if (cmd_idx >= 0 && cmd_idx < adata->alert->n_alert_commands) {\r
-        cmd = adata->alert->alert_commands[cmd_idx];\r
-    }\r
-\r
-    flags = adata->alert->flags;\r
-\r
-    adata->alert->response = cmd;\r
-\r
-    khui_alert_unlock(adata->alert);\r
-\r
-    /* if we were supposed to dispatch the command, do so */\r
-    if (cmd != 0 &&\r
-        cmd != KHUI_PACTION_CLOSE &&\r
-        (flags & KHUI_ALERT_FLAG_DISPATCH_CMD)) {\r
-        PostMessage(khm_hwnd_main, WM_COMMAND,\r
-                    MAKEWPARAM(cmd, 0), 0);\r
-    }\r
-\r
-    /* if this was the only alert in the alert group and its close\r
-       button was clicked, we close the alert window.  Otherwise, the\r
-       alert window creates its own close button that closes the\r
-       window. */\r
-    if (d->n_alerts == 1) {\r
-        PostMessage(d->hwnd, WM_CLOSE, 0, 0);\r
-    }\r
-\r
-    /* While we are at it, we should disable the buttons for this\r
-       alert since we have already dispatched the command for it. */\r
-    if (cmd != 0) {\r
-        HWND hw_focus = GetFocus();\r
-        khm_boolean focus_trapped = FALSE;\r
-\r
-        for (i=0; i < adata->n_cmd_buttons; i++) {\r
-            if (adata->hwnd_buttons[i]) {\r
-                if (hw_focus == adata->hwnd_buttons[i])\r
-                    focus_trapped = TRUE;\r
-\r
-                EnableWindow(adata->hwnd_buttons[i], FALSE);\r
-            }\r
-        }\r
-\r
-        if (focus_trapped) {\r
-            hw_focus = GetNextDlgTabItem(d->hwnd, hw_focus, FALSE);\r
-            if (hw_focus)\r
-                PostMessage(d->hwnd, WM_NEXTDLGCTL, (WPARAM) hw_focus, MAKELPARAM(TRUE,0));\r
-        }\r
-    }\r
-}\r
-\r
-static void\r
-destroy_alerter_wnd_data(alerter_wnd_data * d) {\r
-    alerter_alert_data * adata;\r
-\r
-    LDELETE(&khui_alert_windows, d);\r
-\r
-    QGET(d, &adata);\r
-    while(adata) {\r
-\r
-        if (adata->alert) {\r
-\r
-            khui_alert_lock(adata->alert);\r
-\r
-            adata->alert->displayed = FALSE;\r
-\r
-            khui_alert_unlock(adata->alert);\r
-\r
-            khui_alert_release(adata->alert);\r
-            adata->alert = NULL;\r
-        }\r
-\r
-        PFREE(adata);\r
-\r
-        QGET(d, &adata);\r
-    }\r
-\r
-    PFREE(d);\r
-}\r
-\r
-/* both ref and to_add must be locked and held */\r
-static khm_boolean\r
-alert_can_consolidate(khui_alert * ref,\r
-                      khui_alert * to_add,\r
-                      alert_list * alist) {\r
-\r
-    /* first check if we can add anything */\r
-    if (alist->n_alerts == ARRAYLENGTH(alist->alerts))\r
-        return FALSE;\r
-\r
-#ifdef DEBUG\r
-    assert(to_add != NULL);\r
-#endif\r
-\r
-    if (ref == NULL) {\r
-        /* we are testing whether to_add should be added to the alist\r
-           on its own. */\r
-        if ((to_add->flags & KHUI_ALERT_FLAG_DISPLAY_BALLOON) &&\r
-            !(to_add->flags & KHUI_ALERT_FLAG_DISPLAY_WINDOW)) {\r
-            /* already displayed */\r
-            return FALSE;\r
-        }\r
-\r
-        if ((to_add->flags & (KHUI_ALERT_FLAG_REQUEST_BALLOON |\r
-                              KHUI_ALERT_FLAG_REQUEST_WINDOW)) == KHUI_ALERT_FLAG_REQUEST_BALLOON) {\r
-            /* needs to be shown in a balloon */\r
-            return FALSE;\r
-        }\r
-\r
-        return TRUE;\r
-    }\r
-\r
-    /* if the ref or to_add are marked for modal, then we can't\r
-       consolidate them */\r
-    if ((ref->flags & KHUI_ALERT_FLAG_MODAL) ||\r
-        (to_add->flags & KHUI_ALERT_FLAG_MODAL))\r
-        return FALSE;\r
-\r
-    /* also, if either of them have requested to be exclusively shown\r
-       in a balloon, then we can't consolidate them. */\r
-    if (((ref->flags & (KHUI_ALERT_FLAG_REQUEST_BALLOON |\r
-                        KHUI_ALERT_FLAG_REQUEST_WINDOW)) == KHUI_ALERT_FLAG_REQUEST_BALLOON)\r
-\r
-        ||\r
-\r
-        ((to_add->flags & (KHUI_ALERT_FLAG_REQUEST_BALLOON |\r
-                           KHUI_ALERT_FLAG_REQUEST_WINDOW)) == KHUI_ALERT_FLAG_REQUEST_BALLOON))\r
-        return FALSE;\r
-\r
-    /* for now, all we check if whether they are of the same type. */\r
-    if (ref->alert_type != KHUI_ALERTTYPE_NONE &&\r
-        ref->alert_type == to_add->alert_type)\r
-        return TRUE;\r
-    else\r
-        return FALSE;\r
-}\r
-\r
-/* both a1 and a2 must be locked */\r
-static khm_boolean\r
-alert_is_equal(khui_alert * a1, khui_alert * a2) {\r
-    khm_int32 i;\r
-\r
-    if ((a1->severity != a2->severity) ||\r
-        (a1->n_alert_commands != a2->n_alert_commands) ||\r
-        (a1->title && (!a2->title || wcscmp(a1->title, a2->title))) ||\r
-        (!a1->title && a2->title) ||\r
-        (a1->message && (!a2->message || wcscmp(a1->message, a2->message))) ||\r
-        (!a1->message && a2->message) ||\r
-        (a1->suggestion && (!a2->suggestion || wcscmp(a1->suggestion, a2->suggestion))) ||\r
-        (!a1->suggestion && a2->suggestion)) {\r
-\r
-        return FALSE;\r
-\r
-    }\r
-\r
-    for (i=0; i < a1->n_alert_commands; i++) {\r
-        if (a1->alert_commands[i] != a2->alert_commands[i])\r
-            return FALSE;\r
-    }\r
-\r
-    return TRUE;\r
-}\r
-\r
-/* the return value is the number of alerts added to alist */\r
-static khm_int32\r
-alert_consolidate(alert_list * alist,\r
-                  khui_alert * alert,\r
-                  khm_boolean add_from_queue) {\r
-\r
-    khui_alert * listtop;\r
-    int queue_size = 0;\r
-    int i;\r
-    khm_int32 n_added = 0;\r
-\r
-#ifdef DEBUG\r
-    assert(alist);\r
-#endif\r
-\r
-    if (alist->n_alerts == ARRAYLENGTH(alist->alerts)) {\r
-        /* can't add anything */\r
-\r
-        return 0;\r
-    }\r
-\r
-    /* if the list is empty, we just add one alert */\r
-    if (alist->n_alerts == 0) {\r
-\r
-        if (alert) {\r
-            khui_alert_lock(alert);\r
-            if (alert_can_consolidate(NULL, alert, alist)) {\r
-                alert_list_add_alert(alist, alert);\r
-                n_added ++;\r
-                alert = NULL;\r
-            }\r
-            khui_alert_unlock(alert);\r
-        }\r
-\r
-        if (n_added == 0 && add_from_queue) {\r
-            khui_alert * q;\r
-            int i;\r
-\r
-            queue_size = alert_queue_get_size();\r
-            for (i=0; i < queue_size && n_added == 0; i++) {\r
-                q = alert_queue_get_alert_by_pos(i);\r
-                if (q) {\r
-                    khui_alert_lock(q);\r
-                    if (alert_can_consolidate(NULL, q, alist)) {\r
-                        alert_list_add_alert(alist, q);\r
-                        n_added++;\r
-                        alert_queue_delete_alert(q);\r
-                    }\r
-                    khui_alert_unlock(q);\r
-                    khui_alert_release(q);\r
-                }\r
-            }\r
-        }\r
-\r
-        if (n_added == 0) {\r
-            /* nothing to add */\r
-            return 0;\r
-        }\r
-    }\r
-\r
-    /* at this point, the alert list is not empty */\r
-#ifdef DEBUG\r
-    assert(alist->n_alerts != 0);\r
-    assert(alist->alerts[0]);\r
-#endif\r
-\r
-    listtop = alist->alerts[0];\r
-    khui_alert_hold(listtop);\r
-    khui_alert_lock(listtop);\r
-\r
-    queue_size = alert_queue_get_size();\r
-\r
-    if (alert) {\r
-        khui_alert_lock(alert);\r
-        if (alert_can_consolidate(listtop, alert, alist)) {\r
-            alert_list_add_alert(alist, alert);\r
-            n_added ++;\r
-        }\r
-        khui_alert_unlock(alert);\r
-    }\r
-\r
-    if (add_from_queue) {\r
-        for (i=0; i < queue_size; i++) {\r
-            khui_alert * a;\r
-\r
-            a = alert_queue_get_alert_by_pos(i);\r
-            if (a == NULL)\r
-                continue;\r
-\r
-            khui_alert_lock(a);\r
-            if (alert_can_consolidate(listtop, a, alist)) {\r
-                alert_queue_delete_alert(a);\r
-                alert_list_add_alert(alist, a);\r
-                n_added ++;\r
-\r
-                queue_size--;\r
-                i--;\r
-#ifdef DEBUG\r
-                assert(alert_queue_get_size() == queue_size);\r
-#endif\r
-            }\r
-            khui_alert_unlock(a);\r
-            khui_alert_release(a);\r
-        }\r
-    }\r
-\r
-    khui_alert_unlock(listtop);\r
-    khui_alert_release(listtop);\r
-\r
-    return n_added;\r
-}\r
-\r
-static khm_int32\r
-alert_check_consolidate_window(alerter_wnd_data * d, khui_alert * a) {\r
-    alert_list alist;\r
-    alerter_alert_data * adata;\r
-    int n_added;\r
-\r
-    alert_list_init(&alist);\r
-\r
-    adata = QTOP(d);\r
-    while(adata) {\r
-\r
-#ifdef DEBUG\r
-        assert(adata->alert);\r
-#endif\r
-        alert_list_add_alert(&alist, adata->alert);\r
-\r
-        adata = QNEXT(adata);\r
-    }\r
-\r
-    n_added = alert_consolidate(&alist, a, FALSE);\r
-\r
-    alert_list_destroy(&alist);\r
-\r
-    return n_added;\r
-}\r
-\r
-static khm_int32 \r
-alert_show_minimized(khui_alert * a) {\r
-    wchar_t tbuf[64];           /* corresponds to NOTIFYICONDATA::szInfoTitle[] */\r
-    wchar_t mbuf[256];          /* corresponds to NOTIFYICONDATA::szInfo[] */\r
-\r
-#ifdef DEBUG\r
-    assert(a);\r
-#endif\r
-    if (a == NULL)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    khui_alert_lock(a);\r
-\r
-    if (a->message == NULL)\r
-        goto done;\r
-\r
-    if (a->title == NULL) {\r
-        LoadString(khm_hInstance, IDS_ALERT_DEFAULT,\r
-                   tbuf, ARRAYLENGTH(tbuf));\r
-    } else {\r
-        StringCbCopy(tbuf, sizeof(tbuf), a->title);\r
-    }\r
-\r
-    if (FAILED(StringCbCopy(mbuf, sizeof(mbuf), a->message)) ||\r
-        (!(a->flags & KHUI_ALERT_FLAG_DEFACTION) &&\r
-         (a->n_alert_commands > 0 ||\r
-          a->suggestion ||\r
-          (a->flags & KHUI_ALERT_FLAG_VALID_ERROR)))) {\r
-        /* if mbuf wasn't big enough, this should have copied a\r
-           truncated version of it */\r
-        size_t cch_m, cch_p;\r
-        wchar_t postfix[256];\r
-\r
-        cch_p = LoadString(khm_hInstance, IDS_ALERT_MOREINFO, postfix,\r
-                           ARRAYLENGTH(postfix));\r
-        cch_p++;                /* account for NULL */\r
-\r
-        StringCchLength(mbuf, ARRAYLENGTH(mbuf), &cch_m);\r
-        cch_m = min(cch_m, ARRAYLENGTH(mbuf) - cch_p);\r
-\r
-        StringCchCopy(mbuf + cch_m, ARRAYLENGTH(mbuf) - cch_m,\r
-                      postfix);\r
-\r
-        a->flags |= KHUI_ALERT_FLAG_REQUEST_WINDOW;\r
-    }\r
-\r
-    a->flags |= KHUI_ALERT_FLAG_DISPLAY_BALLOON;\r
-\r
-#ifdef DEBUG\r
-    assert(balloon_alert == NULL);\r
-#endif\r
-\r
-    if (balloon_alert) {\r
-        khui_alert_lock(balloon_alert);\r
-        balloon_alert->displayed = FALSE;\r
-        khui_alert_unlock(balloon_alert);\r
-        khui_alert_release(balloon_alert);\r
-        balloon_alert = NULL;\r
-    }\r
-\r
-    balloon_alert = a;\r
-    khui_alert_hold(a);\r
-\r
-    a->displayed = TRUE;\r
-\r
-    khm_notify_icon_balloon(a->severity,\r
-                            tbuf,\r
-                            mbuf,\r
-                            NTF_TIMEOUT);\r
-\r
- done:\r
-    khui_alert_unlock(a);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-static khm_int32 \r
-alert_show_normal(khui_alert * a) {\r
-    wchar_t buf[256];\r
-    wchar_t * title;\r
-    alert_list alist;\r
-\r
-    khui_alert_lock(a);\r
-\r
-    if(a->title == NULL) {\r
-        LoadString(khm_hInstance, IDS_ALERT_DEFAULT, \r
-                   buf, ARRAYLENGTH(buf));\r
-        title = buf;\r
-    } else\r
-        title = a->title;\r
-\r
-    khui_alert_unlock(a);\r
-\r
-    alert_list_init(&alist);\r
-    alert_list_set_title(&alist, title);\r
-    alert_list_add_alert(&alist, a);\r
-\r
-    alert_show_list(&alist);\r
-\r
-    alert_list_destroy(&alist);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-static khm_int32\r
-alert_show_list(alert_list * alist) {\r
-    HWND hwa;\r
-\r
-    /* we don't need to keep track of the window handle\r
-       because the window procedure adds it to the dialog\r
-       list automatically */\r
-\r
-    hwa = \r
-        CreateWindowEx(ALERT_WINDOW_EX_SYLES,\r
-                       MAKEINTATOM(atom_alerter),\r
-                       alist->title,\r
-                       ALERT_WINDOW_STYLES,\r
-                       0, 0, 300, 300, // bogus values\r
-                       khm_hwnd_main,\r
-                       (HMENU) NULL,\r
-                       khm_hInstance,\r
-                       (LPVOID) alist);\r
-\r
-    ShowWindow(hwa, SW_SHOW);\r
-\r
-    return (hwa != NULL);\r
-}\r
-\r
-static khm_int32 \r
-alert_show(khui_alert * a) {\r
-    khm_boolean show_normal = FALSE;\r
-    khm_boolean show_mini = FALSE;\r
-\r
-    khui_alert_lock(a);\r
-\r
-    /* is there an alert already?  If so, we just enqueue the message\r
-       and let it sit. */\r
-    if (ALERT_DISPLAYED() &&\r
-        !(a->flags & KHUI_ALERT_FLAG_MODAL)) {\r
-        khm_int32 rv;\r
-        alerter_wnd_data * wdata;\r
-\r
-        khui_alert_unlock(a);\r
-\r
-        /* if there are any alerter windows displayed, check if this\r
-           alert can be consolidated with any of them.  If so, we\r
-           should consolidate it.  Otherwise, just enqueue it. */\r
-        for(wdata = khui_alert_windows;\r
-            wdata;\r
-            wdata = LNEXT(wdata)) {\r
-            if (alert_check_consolidate_window(wdata, a)) {\r
-\r
-                add_alert_to_wnd_data(wdata, a);\r
-                estimate_alerter_wnd_sizes(wdata);\r
-                setup_alerter_window_controls(wdata);\r
-\r
-                return KHM_ERROR_SUCCESS;\r
-\r
-            }\r
-        }\r
-\r
-        rv = alert_enqueue(a);\r
-\r
-        if (KHM_SUCCEEDED(rv))\r
-            return KHM_ERROR_HELD;\r
-        else\r
-            return rv;\r
-    }\r
-\r
-    if((a->flags & KHUI_ALERT_FLAG_DISPLAY_WINDOW) ||\r
-       ((a->flags & KHUI_ALERT_FLAG_DISPLAY_BALLOON) &&\r
-        !(a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW))) {\r
-\r
-        /* The alert has already been displayed. */\r
-\r
-        show_normal = FALSE;\r
-        show_mini = FALSE;\r
-\r
-    } else {\r
-\r
-        if(a->err_context != NULL ||\r
-           a->err_event != NULL) {\r
-            a->flags |= KHUI_ALERT_FLAG_VALID_ERROR;\r
-        }\r
-\r
-        /* depending on the state of the main window, we\r
-           need to either show a window or a balloon */\r
-        if ((a->flags & KHUI_ALERT_FLAG_MODAL) ||\r
-            (khm_is_main_window_active() &&\r
-             !(a->flags & KHUI_ALERT_FLAG_REQUEST_BALLOON)) ||\r
-            (a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW)) {\r
-\r
-            show_normal = TRUE;\r
-\r
-        } else {\r
-\r
-            show_mini = TRUE;\r
-\r
-        }\r
-    }\r
-\r
-    khui_alert_unlock(a);\r
-\r
-    if (show_normal)\r
-        return alert_show_normal(a);\r
-    else if (show_mini)\r
-        return alert_show_minimized(a);\r
-    else\r
-        return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-static void\r
-show_queued_alerts(void) {\r
-\r
-    if (!ALERT_DISPLAYED()) {\r
-\r
-        /* show next consolidated batch */\r
-        alert_list alist;\r
-        int n;\r
-\r
-        alert_list_init(&alist);\r
-        n = alert_consolidate(&alist, NULL, TRUE);\r
-\r
-        if (n) {\r
-            if (n == 1) {\r
-                khui_alert_lock(alist.alerts[0]);\r
-\r
-                if (alist.alerts[0]->title) {\r
-                    alert_list_set_title(&alist, alist.alerts[0]->title);\r
-                } else {\r
-                    wchar_t title[KHUI_MAXCCH_TITLE];\r
-                    LoadString(khm_hInstance, IDS_ALERT_DEFAULT,\r
-                               title, ARRAYLENGTH(title));\r
-                    alert_list_set_title(&alist, title);\r
-                }\r
-\r
-                khui_alert_unlock(alist.alerts[0]);\r
-            } else {\r
-                wchar_t title[KHUI_MAXCCH_TITLE];\r
-                LoadString(khm_hInstance, IDS_ALERT_DEFAULT,\r
-                           title, ARRAYLENGTH(title));\r
-                alert_list_set_title(&alist, title);\r
-            }\r
-\r
-            alert_show_list(&alist);\r
-        }\r
-\r
-        alert_list_destroy(&alist);\r
-\r
-        if (n == 0) {\r
-            khui_alert * a;\r
-\r
-            /* no alerts were shown above.  This maybe because none of\r
-               the alerts were consolidatable or they were requested\r
-               to be shown in a balloon.  In this case, we just take\r
-               the first alert from the queue and show it manually. */\r
-\r
-            a = alert_queue_get_alert();\r
-            if (a) {\r
-                alert_show(a);\r
-                khui_alert_release(a);\r
-            }\r
-        }\r
-\r
-        check_for_queued_alerts();\r
-    }\r
-}\r
-\r
-\r
-static void\r
-check_for_queued_alerts(void) {\r
-    if (!is_alert_queue_empty()) {\r
-        khui_alert * a;\r
-\r
-        a = alert_queue_peek();\r
-\r
-        khui_alert_lock(a);\r
-\r
-        if (a->title) {\r
-            HICON hi;\r
-            int res;\r
-\r
-            if (a->severity == KHERR_ERROR)\r
-                res = OIC_ERROR;\r
-            else if (a->severity == KHERR_WARNING)\r
-                res = OIC_WARNING;\r
-            else\r
-                res = OIC_INFORMATION;\r
-\r
-            hi = LoadImage(0, MAKEINTRESOURCE(res),\r
-                           IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),\r
-                           LR_SHARED);\r
-\r
-            khm_statusbar_set_part(KHUI_SBPART_NOTICE,\r
-                                   hi,\r
-                                   a->title);\r
-        } else {\r
-            khm_statusbar_set_part(KHUI_SBPART_NOTICE,\r
-                                   NULL, NULL);\r
-#ifdef DEBUG\r
-            DebugBreak();\r
-#endif\r
-        }\r
-\r
-        khui_alert_unlock(a);\r
-        khui_alert_release(a);\r
-\r
-    } else {\r
-        khm_statusbar_set_part(KHUI_SBPART_NOTICE,\r
-                               NULL, NULL);\r
-    }\r
-}\r
-\r
-static khm_int32\r
-alert_enqueue(khui_alert * a) {\r
-    if (is_alert_queue_full())\r
-        return KHM_ERROR_NO_RESOURCES;\r
-\r
-    alert_queue_put_alert(a);\r
-    check_for_queued_alerts();\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* the alerter window is actually a dialog */\r
-static LRESULT CALLBACK \r
-alerter_wnd_proc(HWND hwnd,\r
-                 UINT uMsg,\r
-                 WPARAM wParam,\r
-                 LPARAM lParam)\r
-{\r
-    switch(uMsg) {\r
-    case WM_CREATE:\r
-        {\r
-            LPCREATESTRUCT lpcs;\r
-            alert_list * alist;\r
-            alerter_wnd_data * d;\r
-\r
-            lpcs = (LPCREATESTRUCT) lParam;\r
-            alist = (alert_list *) lpcs->lpCreateParams;\r
-\r
-            d = create_alerter_wnd_data(hwnd, alist);\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-            SetWindowLongPtr(hwnd, NTF_PARAM, (LONG_PTR) d);\r
-#pragma warning(pop)\r
-\r
-            khm_add_dialog(hwnd);\r
-            khm_enter_modal(hwnd);\r
-\r
-            estimate_alerter_wnd_sizes(d);\r
-            setup_alerter_window_controls(d);\r
-\r
-            if (d->hw_close) {\r
-                SetFocus(d->hw_close);\r
-            }\r
-\r
-            return TRUE;\r
-        }\r
-        break; /* not reached */\r
-\r
-    case WM_DESTROY:\r
-        {\r
-            alerter_wnd_data * d;\r
-\r
-            /* khm_leave_modal() could be here, but instead it is in\r
-               the WM_COMMAND handler.  This is because the modal loop\r
-               has to be exited before DestroyWindow() is issued. */\r
-            //khm_leave_modal();\r
-            khm_del_dialog(hwnd);\r
-\r
-            d = (alerter_wnd_data *)(LONG_PTR) \r
-                GetWindowLongPtr(hwnd, NTF_PARAM);\r
-\r
-            destroy_alerter_wnd_data(d);\r
-\r
-            return TRUE;\r
-        }\r
-        break;\r
-\r
-    case WM_COMMAND:\r
-        {\r
-            alerter_wnd_data * d;\r
-\r
-            d = (alerter_wnd_data *)(LONG_PTR) \r
-                GetWindowLongPtr(hwnd, NTF_PARAM);\r
-\r
-            if(HIWORD(wParam) == BN_CLICKED) {\r
-                if (LOWORD(wParam) == IDC_NTF_CLOSE ||\r
-                    LOWORD(wParam) == KHUI_PACTION_NEXT) {\r
-\r
-                    khm_leave_modal();\r
-\r
-                    DestroyWindow(hwnd);\r
-\r
-                    return 0;\r
-                }\r
-            }\r
-        }\r
-        break;\r
-\r
-    case WM_CLOSE:\r
-        {\r
-            khm_leave_modal();\r
-\r
-            DestroyWindow(hwnd);\r
-\r
-            return 0;\r
-        }\r
-    }\r
-\r
-    /* Since this is a custom built dialog, we use DefDlgProc instead\r
-       of DefWindowProc. */\r
-    return DefDlgProc(hwnd, uMsg, wParam, lParam);\r
-}\r
-\r
-static LRESULT CALLBACK \r
-alert_bin_wnd_proc(HWND hwnd,\r
-                   UINT uMsg,\r
-                   WPARAM wParam,\r
-                   LPARAM lParam)\r
-{\r
-    BOOL in_printclient = FALSE;\r
-\r
-    switch(uMsg) {\r
-    case WM_CREATE:\r
-        {\r
-            LPCREATESTRUCT lpcs;\r
-            alerter_wnd_data * d;\r
-\r
-            lpcs = (LPCREATESTRUCT) lParam;\r
-            d = (alerter_wnd_data *) lpcs->lpCreateParams;\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-            SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) d);\r
-#pragma warning(pop)\r
-        }\r
-        return 0;\r
-\r
-    case WM_ERASEBKGND:\r
-        /* we erase the background when we are drawing the alerts\r
-           anyway. */\r
-        return 0;\r
-\r
-    case WM_PRINTCLIENT:\r
-        in_printclient = TRUE;\r
-        /* fallthrough */\r
-    case WM_PAINT:\r
-        {\r
-            HDC hdc;\r
-            PAINTSTRUCT ps;\r
-            RECT r;\r
-            HFONT hf_old;\r
-            int y;\r
-            alerter_wnd_data * d;\r
-            alerter_alert_data * adata;\r
-            size_t len;\r
-            int idx;\r
-\r
-            d = (alerter_wnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, GWLP_USERDATA);\r
-#ifdef DEBUG\r
-            assert(d);\r
-#endif\r
-\r
-            if (in_printclient) {\r
-                hdc = (HDC) wParam;\r
-            } else {\r
-                hdc = BeginPaint(hwnd, &ps);\r
-            }\r
-\r
-#ifdef DEBUG\r
-            assert(hdc);\r
-            assert(d->hfont);\r
-#endif\r
-\r
-#ifdef ALERT_STATIC_BACKGROUND\r
-            if (in_printclient || ps.fErase) {\r
-                HBRUSH hb_background;\r
-\r
-                hb_background = GetSysColorBrush(COLOR_BTNFACE);\r
-\r
-                GetClientRect(hwnd, &r);\r
-                FillRect(hdc, &r, hb_background);\r
-            }\r
-#endif\r
-\r
-            SetBkMode(hdc, TRANSPARENT);\r
-\r
-            hf_old = SelectFont(hdc, d->hfont);\r
-\r
-            y = -d->scroll_top;\r
-            idx = 0;\r
-            /* go through the alerts and display them */\r
-            adata = QTOP(d);\r
-            while(adata) {\r
-                khui_alert * a;\r
-\r
-#ifndef ALERT_STATIC_BACKGROUND\r
-#define MIX_C(v1, v2, p) (((int)v1) * p + (((int) v2) * (256 - p)))\r
-#define ALPHA 50\r
-                if (in_printclient || ps.fErase) {\r
-                    TRIVERTEX v[2];\r
-                    GRADIENT_RECT gr;\r
-                    COLORREF clr;\r
-                    COLORREF clr2;\r
-\r
-                    CopyRect(&r, &adata->r_alert);\r
-                    OffsetRect(&r, 0, y);\r
-\r
-                    v[0].x = r.left;\r
-                    v[0].y = r.top;\r
-                    v[0].Alpha = 0;\r
-\r
-                    v[1].x = r.right;\r
-                    v[1].y = r.bottom;\r
-                    v[1].Alpha = 0;\r
-\r
-                    if (idx == d->c_alert) {\r
-                        clr = GetSysColor(COLOR_HOTLIGHT);\r
-\r
-                        clr2 = GetSysColor(COLOR_BTNHIGHLIGHT);\r
-                        v[0].Red =   MIX_C(GetRValue(clr), GetRValue(clr2), ALPHA);\r
-                        v[0].Green = MIX_C(GetGValue(clr), GetGValue(clr2), ALPHA);\r
-                        v[0].Blue =  MIX_C(GetBValue(clr), GetBValue(clr2), ALPHA);\r
-\r
-                        clr2 = GetSysColor(COLOR_BTNFACE);\r
-                        v[1].Red =   MIX_C(GetRValue(clr), GetRValue(clr2), ALPHA);\r
-                        v[1].Green = MIX_C(GetGValue(clr), GetGValue(clr2), ALPHA);\r
-                        v[1].Blue =  MIX_C(GetBValue(clr), GetBValue(clr2), ALPHA);\r
-                    } else {\r
-                        clr = GetSysColor(COLOR_BTNHIGHLIGHT);\r
-                        v[0].Red =   ((int)GetRValue(clr)) << 8;\r
-                        v[0].Green = ((int)GetGValue(clr)) << 8;\r
-                        v[0].Blue =  ((int)GetBValue(clr)) << 8;\r
-\r
-                        clr = GetSysColor(COLOR_BTNFACE);\r
-                        v[1].Red =   ((int)GetRValue(clr)) << 8;\r
-                        v[1].Green = ((int)GetGValue(clr)) << 8;\r
-                        v[1].Blue =  ((int)GetBValue(clr)) << 8;\r
-                    }\r
-\r
-                    gr.UpperLeft = 0;\r
-                    gr.LowerRight = 1;\r
-                    GradientFill(hdc, v, 2, &gr, 1, GRADIENT_FILL_RECT_V);\r
-                }\r
-#undef ALPHA\r
-#undef MIX_C\r
-#endif\r
-\r
-                a = adata->alert;\r
-#ifdef DEBUG\r
-                assert(a != NULL);\r
-#endif\r
-                khui_alert_lock(a);\r
-\r
-                if (!IsRectEmpty(&adata->r_title)) {\r
-\r
-                    CopyRect(&r, &adata->r_title);\r
-                    OffsetRect(&r, 0, y);\r
-\r
-                    StringCchLength(a->title, KHUI_MAXCCH_TITLE, &len);\r
-\r
-                    DrawEdge(hdc, &r, EDGE_RAISED, BF_RECT | BF_MIDDLE);\r
-\r
-                    InflateRect(&r, -d->s_pad.cx, -d->s_pad.cy);\r
-\r
-                    DrawText(hdc, a->title, (int) len, &r,\r
-                             DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);\r
-                }\r
-\r
-                {\r
-                    HICON hicon;\r
-                    int iid;\r
-\r
-                    CopyRect(&r, &adata->r_icon);\r
-                    OffsetRect(&r, 0, y);\r
-\r
-                    if(a->severity == KHERR_ERROR)\r
-                        iid = OIC_HAND;\r
-                    else if(a->severity == KHERR_WARNING)\r
-                        iid = OIC_BANG;\r
-                    else\r
-                        iid = OIC_NOTE;\r
-\r
-                    hicon = (HICON) LoadImage(NULL, \r
-                                              MAKEINTRESOURCE(iid), \r
-                                              IMAGE_ICON,\r
-                                              GetSystemMetrics(SM_CXICON),\r
-                                              GetSystemMetrics(SM_CYICON),\r
-                                              LR_SHARED);\r
-\r
-                    DrawIcon(hdc, r.left, r.top, hicon);\r
-                }\r
-\r
-                if (a->message) {\r
-\r
-                    CopyRect(&r, &adata->r_message);\r
-                    OffsetRect(&r, 0, y);\r
-\r
-                    StringCchLength(a->message, KHUI_MAXCCH_MESSAGE, &len);\r
-\r
-                    DrawText(hdc, a->message, (int) len, &r,\r
-                             DT_WORDBREAK);\r
-                }\r
-\r
-                if (a->suggestion) {\r
-                    HICON hicon;\r
-                    SIZE sz;\r
-\r
-                    CopyRect(&r, &adata->r_suggestion);\r
-                    OffsetRect(&r, 0, y);\r
-\r
-                    DrawEdge(hdc, &r, EDGE_SUNKEN, BF_RECT | BF_MIDDLE);\r
-\r
-                    InflateRect(&r, -d->s_pad.cx, -d->s_pad.cy);\r
-\r
-                    sz.cx = GetSystemMetrics(SM_CXSMICON);\r
-                    sz.cy = GetSystemMetrics(SM_CYSMICON);\r
-\r
-                    hicon = (HICON) LoadImage(NULL,\r
-                                              MAKEINTRESOURCE(OIC_NOTE),\r
-                                              IMAGE_ICON,\r
-                                              sz.cx,\r
-                                              sz.cy,\r
-                                              LR_SHARED);\r
-\r
-                    DrawIconEx(hdc, r.left, r.top, hicon, sz.cx, sz.cy, 0, NULL,\r
-                               DI_NORMAL);\r
-\r
-                    r.left += d->s_pad.cx + GetSystemMetrics(SM_CXSMICON);\r
-\r
-                    StringCchLength(a->suggestion, KHUI_MAXCCH_SUGGESTION, &len);\r
-\r
-                    DrawText(hdc, a->suggestion, (int) len, &r,\r
-                             DT_WORDBREAK);\r
-                }\r
-                khui_alert_unlock(a);\r
-\r
-                y += adata->r_alert.bottom;\r
-                idx++;\r
-\r
-                adata = QNEXT(adata);\r
-            }\r
-\r
-            SelectFont(hdc, hf_old);\r
-\r
-            if (!in_printclient) {\r
-                EndPaint(hwnd, &ps);\r
-            }\r
-        }\r
-        return 0;\r
-\r
-    case WM_VSCROLL:\r
-        {\r
-            alerter_wnd_data * d;\r
-            int new_pos = 0;\r
-            SCROLLINFO si;\r
-\r
-            d = (alerter_wnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, GWLP_USERDATA);\r
-#ifdef DEBUG\r
-            assert(d);\r
-#endif\r
-            if (d == NULL)\r
-                break;          /* we can't handle the message */\r
-\r
-            ZeroMemory(&si, sizeof(si));\r
-\r
-            switch(LOWORD(wParam)) {\r
-            case SB_BOTTOM:\r
-                new_pos = d->s_alerts.cy  - d->cy_max_wnd;\r
-                break;\r
-\r
-            case SB_LINEDOWN:\r
-                new_pos = d->scroll_top + SCROLL_LINE_SIZE(d);\r
-                break;\r
-\r
-            case SB_LINEUP:\r
-                new_pos = d->scroll_top - SCROLL_LINE_SIZE(d);\r
-                break;\r
-\r
-            case SB_PAGEDOWN:\r
-                new_pos = d->scroll_top + d->cy_max_wnd;\r
-                break;\r
-\r
-            case SB_PAGEUP:\r
-                new_pos = d->scroll_top - d->cy_max_wnd;\r
-                break;\r
-\r
-            case SB_THUMBPOSITION:\r
-            case SB_THUMBTRACK:\r
-                si.fMask = SIF_TRACKPOS;\r
-                GetScrollInfo(hwnd, SB_VERT, &si);\r
-                new_pos = si.nTrackPos;\r
-                break;\r
-\r
-            case SB_TOP:\r
-                new_pos = 0;\r
-                break;\r
-\r
-            case SB_ENDSCROLL:\r
-                si.fMask = SIF_POS;\r
-                si.nPos = d->scroll_top;\r
-                SetScrollInfo(hwnd, SB_VERT, &si, TRUE);\r
-                return 0;\r
-\r
-            default:\r
-                return 0;\r
-            }\r
-\r
-            scroll_to_position(d, new_pos, FALSE);\r
-        }\r
-        return 0;\r
-\r
-    case WM_COMMAND:\r
-        {\r
-            alerter_wnd_data * d;\r
-\r
-            d = (alerter_wnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, GWLP_USERDATA);\r
-#ifdef DEBUG\r
-            assert(d);\r
-#endif\r
-            if (d == NULL)\r
-                break;\r
-\r
-            if (HIWORD(wParam) == BN_CLICKED) {\r
-                process_command_button(d, LOWORD(wParam));\r
-                return 0;\r
-            } else if (HIWORD(wParam) == BN_SETFOCUS) {\r
-                ensure_command_is_visible(d, LOWORD(wParam));\r
-                return 0;\r
-            }\r
-        }\r
-        break;\r
-\r
-    case WM_LBUTTONUP:\r
-        {\r
-            alerter_wnd_data * d;\r
-            int x,y;\r
-\r
-            d = (alerter_wnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, GWLP_USERDATA);\r
-#ifdef DEBUG\r
-            assert(d);\r
-#endif\r
-            if (d == NULL)\r
-                break;\r
-\r
-            x = GET_X_LPARAM(lParam);\r
-            y = GET_Y_LPARAM(lParam);\r
-\r
-            handle_mouse_select(d, x, y);\r
-        }\r
-        break;\r
-\r
-    case WM_SIZE:\r
-        {\r
-            InvalidateRect(hwnd, NULL, TRUE);\r
-        }\r
-        break;\r
-\r
-    case WM_DESTROY:\r
-        {\r
-            /* nothing needs to be done here */\r
-        }\r
-        return 0;\r
-    }\r
-\r
-    return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
-}\r
-\r
-ATOM khm_register_alerter_wnd_class(void)\r
-{\r
-    WNDCLASSEX wcx;\r
-\r
-    ZeroMemory(&wcx, sizeof(wcx));\r
-\r
-    wcx.cbSize = sizeof(wcx);\r
-    wcx.style =\r
-        CS_OWNDC |\r
-#if(_WIN32_WINNT >= 0x0501)\r
-        ((IS_COMMCTL6())? CS_DROPSHADOW: 0) |\r
-#endif\r
-        0;\r
-    wcx.lpfnWndProc = alerter_wnd_proc;\r
-    wcx.cbClsExtra = 0;\r
-    wcx.cbWndExtra = DLGWINDOWEXTRA + sizeof(LONG_PTR);\r
-    wcx.hInstance = khm_hInstance;\r
-    wcx.hIcon = LoadIcon(khm_hInstance, MAKEINTRESOURCE(IDI_MAIN_APP));\r
-    wcx.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));\r
-    wcx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);\r
-    wcx.lpszMenuName = NULL;\r
-    wcx.lpszClassName = KHUI_ALERTER_CLASS;\r
-    wcx.hIconSm = NULL;\r
-\r
-    atom_alerter = RegisterClassEx(&wcx);\r
-\r
-    return atom_alerter;\r
-}\r
-\r
-ATOM khm_register_alert_bin_wnd_class(void)\r
-{\r
-    WNDCLASSEX wcx;\r
-\r
-    ZeroMemory(&wcx, sizeof(wcx));\r
-\r
-    wcx.cbSize = sizeof(wcx);\r
-    wcx.style = CS_OWNDC;\r
-\r
-    wcx.lpfnWndProc = alert_bin_wnd_proc;\r
-    wcx.cbClsExtra = 0;\r
-    wcx.cbWndExtra = sizeof(LONG_PTR);\r
-    wcx.hInstance = khm_hInstance;\r
-    wcx.hIcon = NULL;\r
-    wcx.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));\r
-    wcx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);\r
-    wcx.lpszMenuName = NULL;\r
-    wcx.lpszClassName = KHUI_ALERTBIN_CLASS;\r
-    wcx.hIconSm = NULL;\r
-\r
-    atom_alert_bin = RegisterClassEx(&wcx);\r
-\r
-    return atom_alert_bin;\r
-}\r
-\r
-/**********************************************************************\r
-  Notification Icon\r
-***********************************************************************/\r
-\r
-#define KHUI_NOTIFY_ICON_ID 0\r
-\r
-void khm_notify_icon_add(void) {\r
-    NOTIFYICONDATA ni;\r
-    wchar_t buf[256];\r
-\r
-    ZeroMemory(&ni, sizeof(ni));\r
-\r
-    ni.cbSize = sizeof(ni);\r
-    ni.hWnd = hwnd_notifier;\r
-    ni.uID = KHUI_NOTIFY_ICON_ID;\r
-    ni.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;\r
-    ni.hIcon = LoadIcon(khm_hInstance, MAKEINTRESOURCE(iid_normal));\r
-    ni.uCallbackMessage = KHUI_WM_NOTIFIER;\r
-    LoadString(khm_hInstance, IDS_NOTIFY_PREFIX, buf, ARRAYLENGTH(buf));\r
-    StringCbCopy(ni.szTip, sizeof(ni.szTip), buf);\r
-    LoadString(khm_hInstance, IDS_NOTIFY_READY, buf, ARRAYLENGTH(buf));\r
-    StringCbCat(ni.szTip, sizeof(ni.szTip), buf);\r
-\r
-    Shell_NotifyIcon(NIM_ADD, &ni);\r
-\r
-    DestroyIcon(ni.hIcon);\r
-\r
-    ni.cbSize = sizeof(ni);\r
-    ni.uVersion = NOTIFYICON_VERSION;\r
-    Shell_NotifyIcon(NIM_SETVERSION, &ni);\r
-}\r
-\r
-void \r
-khm_notify_icon_balloon(khm_int32 severity,\r
-                         wchar_t * title,\r
-                         wchar_t * msg,\r
-                         khm_int32 timeout) {\r
-    NOTIFYICONDATA ni;\r
-    int iid;\r
-\r
-    if (!msg || !title)\r
-        return;\r
-\r
-    ZeroMemory(&ni, sizeof(ni));\r
-    ni.cbSize = sizeof(ni);\r
-\r
-    if (severity == KHERR_INFO) {\r
-        ni.dwInfoFlags = NIIF_INFO;\r
-        iid = IDI_NOTIFY_INFO;\r
-    } else if (severity == KHERR_WARNING) {\r
-        ni.dwInfoFlags = NIIF_WARNING;\r
-        iid = IDI_NOTIFY_WARN;\r
-    } else if (severity == KHERR_ERROR) {\r
-        ni.dwInfoFlags = NIIF_ERROR;\r
-        iid = IDI_NOTIFY_ERROR;\r
-    } else {\r
-        ni.dwInfoFlags = NIIF_NONE;\r
-        iid = iid_normal;\r
-    }\r
-\r
-    ni.hWnd = hwnd_notifier;\r
-    ni.uID = KHUI_NOTIFY_ICON_ID;\r
-    ni.uFlags = NIF_INFO | NIF_ICON;\r
-    ni.hIcon = LoadIcon(khm_hInstance, MAKEINTRESOURCE(iid));\r
-\r
-    if (FAILED(StringCbCopy(ni.szInfo, sizeof(ni.szInfo), msg))) {\r
-        /* too long? */\r
-        StringCchCopyN(ni.szInfo, ARRAYLENGTH(ni.szInfo),\r
-                       msg, \r
-                       ARRAYLENGTH(ni.szInfo) - ARRAYLENGTH(ELLIPSIS));\r
-        StringCchCat(ni.szInfo, ARRAYLENGTH(ni.szInfo),\r
-                     ELLIPSIS);\r
-    }\r
-\r
-    if (FAILED(StringCbCopy(ni.szInfoTitle, sizeof(ni.szInfoTitle), \r
-                            title))) {\r
-        StringCchCopyN(ni.szInfoTitle, ARRAYLENGTH(ni.szInfoTitle),\r
-                       title, \r
-                       ARRAYLENGTH(ni.szInfoTitle) - ARRAYLENGTH(ELLIPSIS));\r
-        StringCchCat(ni.szInfoTitle, ARRAYLENGTH(ni.szInfoTitle),\r
-                     ELLIPSIS);\r
-    }\r
-\r
-    ni.uTimeout = timeout;\r
-\r
-    Shell_NotifyIcon(NIM_MODIFY, &ni);\r
-\r
-    DestroyIcon(ni.hIcon);\r
-}\r
-\r
-void khm_notify_icon_expstate(enum khm_notif_expstate expseverity) {\r
-    int new_iid;\r
-\r
-    if (expseverity == KHM_NOTIF_OK)\r
-        new_iid = IDI_APPICON_OK;\r
-    else if (expseverity == KHM_NOTIF_WARN)\r
-        new_iid = IDI_APPICON_WARN;\r
-    else if (expseverity == KHM_NOTIF_EXP)\r
-        new_iid = IDI_APPICON_EXP;\r
-    else\r
-        new_iid = IDI_NOTIFY_NONE;\r
-\r
-    if (iid_normal == new_iid)\r
-        return;\r
-\r
-    iid_normal = new_iid;\r
-\r
-    if (balloon_alert == NULL)\r
-        khm_notify_icon_change(KHERR_NONE);\r
-}\r
-\r
-void khm_notify_icon_change(khm_int32 severity) {\r
-    NOTIFYICONDATA ni;\r
-    wchar_t buf[256];\r
-    int iid;\r
-\r
-    if (severity == KHERR_INFO)\r
-        iid = IDI_NOTIFY_INFO;\r
-    else if (severity == KHERR_WARNING)\r
-        iid = IDI_NOTIFY_WARN;\r
-    else if (severity == KHERR_ERROR)\r
-        iid = IDI_NOTIFY_ERROR;\r
-    else\r
-        iid = iid_normal;\r
-\r
-    ZeroMemory(&ni, sizeof(ni));\r
-\r
-    ni.cbSize = sizeof(ni);\r
-    ni.hWnd = hwnd_notifier;\r
-    ni.uID = KHUI_NOTIFY_ICON_ID;\r
-    ni.uFlags = NIF_ICON | NIF_TIP;\r
-    ni.hIcon = LoadIcon(khm_hInstance, MAKEINTRESOURCE(iid));\r
-    LoadString(khm_hInstance, IDS_NOTIFY_PREFIX, buf, ARRAYLENGTH(buf));\r
-    StringCbCopy(ni.szTip, sizeof(ni.szTip), buf);\r
-    if(severity == KHERR_NONE)\r
-        LoadString(khm_hInstance, IDS_NOTIFY_READY, buf, ARRAYLENGTH(buf));\r
-    else\r
-        LoadString(khm_hInstance, IDS_NOTIFY_ATTENTION, buf, ARRAYLENGTH(buf));\r
-    StringCbCat(ni.szTip, sizeof(ni.szTip), buf);\r
-\r
-    Shell_NotifyIcon(NIM_MODIFY, &ni);\r
-\r
-    DestroyIcon(ni.hIcon);\r
-}\r
-\r
-void khm_notify_icon_remove(void) {\r
-    NOTIFYICONDATA ni;\r
-\r
-    ZeroMemory(&ni, sizeof(ni));\r
-\r
-    ni.cbSize = sizeof(ni);\r
-    ni.hWnd = hwnd_notifier;\r
-    ni.uID = KHUI_NOTIFY_ICON_ID;\r
-\r
-    Shell_NotifyIcon(NIM_DELETE, &ni);\r
-}\r
-\r
-static khm_int32\r
-get_default_notifier_action(void) {\r
-    khm_int32 def_cmd = KHUI_ACTION_OPEN_APP;\r
-    khm_handle csp_cw = NULL;\r
-    khm_size i;\r
-\r
-    if (KHM_FAILED(khc_open_space(NULL, L"CredWindow", KHM_PERM_READ,\r
-                                  &csp_cw)))\r
-        def_cmd;\r
-\r
-    khc_read_int32(csp_cw, L"NotificationAction", &def_cmd);\r
-\r
-    khc_close_space(csp_cw);\r
-\r
-    for (i=0; i < n_khm_notifier_actions; i++) {\r
-        if (khm_notifier_actions[i] == def_cmd)\r
-            break;\r
-    }\r
-\r
-    if (i < n_khm_notifier_actions)\r
-        return def_cmd;\r
-    else\r
-        return KHUI_ACTION_OPEN_APP;\r
-}\r
-\r
-void khm_notify_icon_activate(void) {\r
-    /* if there are any notifications waiting to be shown and there\r
-       are no alerts already being shown, we show them.  Otherwise we\r
-       execute the default action. */\r
-\r
-    khm_notify_icon_change(KHERR_NONE);\r
-\r
-    if (balloon_alert != NULL && khui_alert_windows == NULL) {\r
-\r
-        khui_alert * a;\r
-        khm_boolean alert_done = FALSE;\r
-\r
-        a = balloon_alert;\r
-        balloon_alert = NULL;\r
-\r
-        khui_alert_lock(a);\r
-\r
-        a->displayed = FALSE;\r
-\r
-        if ((a->flags & KHUI_ALERT_FLAG_DEFACTION) &&\r
-            (a->n_alert_commands > 0)) {\r
-\r
-            PostMessage(khm_hwnd_main, WM_COMMAND,\r
-                        MAKEWPARAM(a->alert_commands[0], \r
-                                   0),\r
-                        0);\r
-            alert_done = TRUE;\r
-\r
-        } else if (a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW) {\r
-\r
-            alert_show_normal(a);\r
-            alert_done = TRUE;\r
-\r
-        }\r
-        khui_alert_unlock(a);\r
-        khui_alert_release(a);\r
-\r
-        if (alert_done)\r
-            return;\r
-    }\r
-\r
-    if (!is_alert_queue_empty() && !ALERT_DISPLAYED()) {\r
-\r
-        khm_show_main_window();\r
-        show_queued_alerts();\r
-\r
-        return;\r
-    }\r
-\r
-\r
-    /* if none of the above applied, then we perform the default\r
-       action for the notification icon. */\r
-    {\r
-        khm_int32 cmd = 0;\r
-\r
-        cmd = get_default_notifier_action();\r
-\r
-        if (cmd == KHUI_ACTION_OPEN_APP) {\r
-            if (khm_is_main_window_visible()) {\r
-                khm_hide_main_window();\r
-            } else {\r
-                khm_show_main_window();\r
-            }\r
-        } else {\r
-            khui_action_trigger(cmd, NULL);\r
-        }\r
-\r
-        check_for_queued_alerts();\r
-    }\r
-}\r
-\r
-/*********************************************************************\r
-  Initialization\r
-**********************************************************************/\r
-\r
-void khm_init_notifier(void)\r
-{\r
-    if(!khm_register_notifier_wnd_class())\r
-        return;\r
-\r
-    if(!khm_register_alerter_wnd_class())\r
-        return;\r
-\r
-    if(!khm_register_alert_bin_wnd_class())\r
-        return;\r
-\r
-    hwnd_notifier = CreateWindowEx(0,\r
-                                   MAKEINTATOM(atom_notifier),\r
-                                   KHUI_NOTIFIER_WINDOW,\r
-                                   0,\r
-                                   0,0,0,0,\r
-                                   HWND_MESSAGE,\r
-                                   NULL,\r
-                                   khm_hInstance,\r
-                                   NULL);\r
-\r
-    if(hwnd_notifier != NULL) {\r
-        kmq_subscribe_hwnd(KMSG_ALERT, hwnd_notifier);\r
-        kmq_subscribe_hwnd(KMSG_CRED, hwnd_notifier);\r
-        notifier_ready = TRUE;\r
-\r
-        khm_notify_icon_add();\r
-    } else {\r
-#ifdef DEBUG\r
-        assert(hwnd_notifier != NULL);\r
-#endif\r
-    }\r
-    khm_timer_init();\r
-\r
-    khm_addr_change_notifier_init();\r
-}\r
-\r
-void khm_exit_notifier(void)\r
-{\r
-    khm_addr_change_notifier_exit();\r
-\r
-    khm_timer_exit();\r
-\r
-    if(hwnd_notifier != NULL) {\r
-        khm_notify_icon_remove();\r
-        kmq_unsubscribe_hwnd(KMSG_ALERT, hwnd_notifier);\r
-        kmq_unsubscribe_hwnd(KMSG_CRED, hwnd_notifier);\r
-        DestroyWindow(hwnd_notifier);\r
-        hwnd_notifier = NULL;\r
-    }\r
-\r
-    if(atom_notifier != 0) {\r
-        UnregisterClass(MAKEINTATOM(atom_notifier), khm_hInstance);\r
-        atom_notifier = 0;\r
-    }\r
-\r
-    if(atom_alerter != 0) {\r
-        UnregisterClass(MAKEINTATOM(atom_alerter), khm_hInstance);\r
-        atom_alerter = 0;\r
-    }\r
-\r
-    if(atom_alert_bin != 0) {\r
-        UnregisterClass(MAKEINTATOM(atom_alert_bin), khm_hInstance);\r
-        atom_alert_bin = 0;\r
-    }\r
-\r
-    notifier_ready = FALSE;\r
-}\r
-\r
-/***** testing *****/\r
-\r
-void\r
-create_test_alerts(void) {\r
-\r
-    khui_alert * a;\r
-    int i;\r
-\r
-    for (i=0; i < 50; i++) {\r
-        wchar_t buf[128];\r
-\r
-        StringCbPrintf(buf, sizeof(buf), L"Foo bar baz.  This is alert number %d", i);\r
-        khui_alert_create_simple(L"Title", buf, KHERR_INFO, &a);\r
-        khui_alert_set_type(a, KHUI_ALERTTYPE_PLUGIN);\r
-        khui_alert_set_suggestion(a, L"This is a suggestion.  It is kinda long to see if the word wrapping actually works as we expect it to.  Just in case, here's a line feed.\n\nDoes this show up on a different line? Cool!");\r
-\r
-        khui_alert_add_command(a, KHUI_ACTION_NEW_CRED);\r
-        khui_alert_add_command(a, KHUI_ACTION_CLOSE_APP);\r
-        khui_alert_add_command(a, KHUI_ACTION_PROPERTIES);\r
-        khui_alert_add_command(a, KHUI_ACTION_OPEN_APP);\r
-        khui_alert_add_command(a, KHUI_ACTION_VIEW_REFRESH);\r
-\r
-        khui_alert_show(a);\r
-        khui_alert_release(a);\r
-    }\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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_ALERTBIN_CLASS         L"KhuiAlertBinWindowClass"
+
+#define KHUI_NOTIFIER_WINDOW        L"KhuiNotifierMsgWindow"
+
+
+/* The commands that are available as default actions when the user
+   clicks the notification icon. */
+
+khm_int32 khm_notifier_actions[] = {
+    KHUI_ACTION_OPEN_APP,
+    KHUI_ACTION_NEW_CRED
+};
+
+khm_size  n_khm_notifier_actions = ARRAYLENGTH(khm_notifier_actions);
+
+/* notifier message for notification icon */
+#define KHUI_WM_NOTIFIER            WM_COMMAND
+
+#define DRAWTEXTOPTIONS (DT_CALCRECT | DT_NOPREFIX | DT_WORDBREAK)
+
+/* are we showing an alert? */
+#define ALERT_DISPLAYED() (balloon_alert != NULL || khui_alert_windows != NULL)
+
+/* Forward declarations */
+
+struct tag_alerter_wnd_data;
+typedef struct tag_alerter_wnd_data alerter_wnd_data;
+
+struct tag_alert_list;
+typedef struct tag_alert_list alert_list;
+
+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);
+
+static khm_int32
+alert_show_list(alert_list * alist);
+
+static khm_int32
+alert_enqueue(khui_alert * a);
+
+static khm_boolean
+alert_is_equal(khui_alert * a1, khui_alert * a2);
+
+static void
+check_for_queued_alerts(void);
+
+static void
+show_queued_alerts(void);
+
+static khm_int32
+alert_consolidate(alert_list * alist,
+                  khui_alert * alert,
+                  khm_boolean add_from_queue);
+
+static khm_int32
+get_default_notifier_action(void);
+
+/* Globals */
+
+/* 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;
+/* window class registration atom for the alert "bin", which is the
+   window that holds all the alerts. */
+ATOM atom_alert_bin = 0;
+
+/* notifier message window */
+HWND hwnd_notifier = NULL;
+
+BOOL notifier_ready = FALSE;
+
+/* The list of alert windows currently active */
+alerter_wnd_data * khui_alert_windows = NULL;
+
+/* Notification icon for when there are no alerts to be displayed */
+int  iid_normal = IDI_NOTIFY_NONE;
+
+/* The alert currently being displayed in a balloon */
+khui_alert * balloon_alert = NULL;
+
+/**********************************************************************
+  Alert Queue
+
+  The alert queue is the data structure that keeps track of all the
+  alerts that are waiting to be displayed.  Alerts will be placed on
+  the queue if they cannot be immediately displayed for some reason
+  (e.g. another alert is being displayed, or the user is working in
+  another window).
+***********************************************************************/
+
+#define KHUI_ALERT_QUEUE_MAX        64
+
+khui_alert * alert_queue[KHUI_ALERT_QUEUE_MAX];
+khm_int32    alert_queue_head = 0;
+khm_int32    alert_queue_tail = 0;
+
+#define is_alert_queue_empty() (alert_queue_head == alert_queue_tail)
+#define is_alert_queue_full()  (((alert_queue_tail + 1) % KHUI_ALERT_QUEUE_MAX) == alert_queue_head)
+
+/* NOTE: the alert queue functions are unsafe to call from any thread
+   other than the UI thread. */
+
+static void 
+alert_queue_put_alert(khui_alert * a) {
+    if (is_alert_queue_full()) return;
+    alert_queue[alert_queue_tail++] = a;
+    khui_alert_hold(a);
+    alert_queue_tail %= KHUI_ALERT_QUEUE_MAX;
+}
+
+/* the caller needs to release the alert that's returned  */
+static khui_alert * 
+alert_queue_get_alert(void) {
+    khui_alert * a;
+
+    if (is_alert_queue_empty()) return NULL;
+    a = alert_queue[alert_queue_head++];
+    alert_queue_head %= KHUI_ALERT_QUEUE_MAX;
+
+    return a;                   /* held */
+}
+
+static int
+alert_queue_get_size(void) {
+    if (is_alert_queue_empty())
+        return 0;
+
+    if (alert_queue_tail < alert_queue_head) {
+        return (alert_queue_tail + KHUI_ALERT_QUEUE_MAX - alert_queue_head);
+    } else {
+        return alert_queue_tail - alert_queue_head;
+    }
+}
+
+static khui_alert *
+alert_queue_get_alert_by_pos(int pos) {
+    khui_alert * a;
+
+    if (is_alert_queue_empty() ||
+        pos >= alert_queue_get_size() ||
+        pos < 0) {
+        return NULL;
+    }
+
+    a = alert_queue[(alert_queue_head + pos) % KHUI_ALERT_QUEUE_MAX];
+    if (a) {
+        khui_alert_hold(a);
+    }
+    return a;
+}
+
+static int
+alert_queue_delete_alert(khui_alert * a) {
+    int idx;
+    int succ;
+
+    idx = alert_queue_head;
+    while(idx != alert_queue_tail) {
+        if (alert_queue[idx] == a)
+            break;
+
+        idx = (idx + 1) % KHUI_ALERT_QUEUE_MAX;
+    }
+
+    if (idx == alert_queue_tail)
+        return 0;
+
+#ifdef DEBUG
+    assert(alert_queue[idx]);
+#endif
+    khui_alert_release(alert_queue[idx]);
+
+    succ = (idx + 1) % KHUI_ALERT_QUEUE_MAX;
+    while(succ != alert_queue_tail) {
+        alert_queue[idx] = alert_queue[succ];
+
+        succ = (succ + 1) % KHUI_ALERT_QUEUE_MAX;
+        idx = (idx + 1) % KHUI_ALERT_QUEUE_MAX;
+    }
+
+    alert_queue_tail = idx;
+    return 1;
+}
+
+/* the caller needs to release the alert that's returned */
+static khui_alert * 
+alert_queue_peek(void) {
+    khui_alert * a;
+
+    if (is_alert_queue_empty())
+        return NULL;
+
+    a = alert_queue[alert_queue_head];
+    khui_alert_hold(a);
+
+    return a;
+}
+
+/**********************************************************************
+  Alert List
+
+  A list of alerts.  Currently has a fixed upper limit, but the limit
+  is high enough for now.
+***********************************************************************/
+
+typedef struct tag_alert_list {
+    khui_alert * alerts[KHUI_ALERT_QUEUE_MAX];
+    int          n_alerts;
+    wchar_t      title[KHUI_MAXCCH_TITLE];
+} alert_list;
+
+static void
+alert_list_init(alert_list * alist) {
+    ZeroMemory(alist, sizeof(*alist));
+}
+
+static void
+alert_list_set_title(alert_list * alist, wchar_t * title) {
+    StringCbCopy(alist->title, sizeof(alist->title), title);
+}
+
+static khm_int32
+alert_list_add_alert(alert_list * alist,
+                     khui_alert * alert) {
+
+    if (alist->n_alerts == ARRAYLENGTH(alist->alerts))
+        return KHM_ERROR_NO_RESOURCES;
+
+    khui_alert_hold(alert);
+    alist->alerts[alist->n_alerts++] = alert;
+
+    return KHM_ERROR_SUCCESS;
+}
+
+static void
+alert_list_destroy(alert_list * alist) {
+    int i;
+
+    for (i=0; i < alist->n_alerts; i++) {
+        if (alist->alerts[i] != NULL) {
+            khui_alert_release(alist->alerts[i]);
+            alist->alerts[i] = NULL;
+        }
+    }
+
+    alist->n_alerts = 0;
+}
+
+
+/**********************************************************************
+  Notifier Window
+
+  The notifier window manages the notification icon and handles
+  KMSG_ALERT messages sent from the UI library.  The window will exist
+  for the lifetime of the application.
+***********************************************************************/
+
+/* These are defined for APPVER >= 0x501.  We are defining them here
+   so that we can build with APPVER = 0x500 and use the same binaries
+   with Win XP. */
+
+#ifndef NIN_BALLOONSHOW
+#define NIN_BALLOONSHOW (WM_USER + 2)
+#endif
+
+#ifndef NIN_BALLOONHIDE
+#define NIN_BALLOONHIDE (WM_USER + 3)
+#endif
+
+#ifndef NIN_BALLOONTIMEOUT
+#define NIN_BALLOONTIMEOUT (WM_USER + 4)
+#endif
+
+#ifndef NIN_BALLOONUSERCLICK
+#define NIN_BALLOONUSERCLICK (WM_USER + 5)
+#endif
+
+
+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:
+                {
+                    khui_alert * a;
+
+                    a = (khui_alert *) m->vparam;
+#ifdef DEBUG
+                    assert(a != NULL);
+#endif
+                    rv = alert_show(a);
+                    khui_alert_release(a);
+                }
+                break;
+
+            case KMSG_ALERT_QUEUE:
+                {
+                    khui_alert * a;
+
+                    a = (khui_alert *) m->vparam;
+#ifdef DEBUG
+                    assert(a != NULL);
+#endif
+                    rv = alert_enqueue(a);
+                    khui_alert_release(a);
+                }
+                break;
+
+            case KMSG_ALERT_CHECK_QUEUE:
+                check_for_queued_alerts();
+                break;
+
+            case KMSG_ALERT_SHOW_QUEUED:
+                show_queued_alerts();
+                break;
+
+            case KMSG_ALERT_SHOW_MODAL:
+                {
+                    khui_alert * a;
+
+                    a = (khui_alert *) m->vparam;
+#ifdef DEBUG
+                    assert(a != NULL);
+#endif
+                    khui_alert_lock(a);
+                    a->flags |= KHUI_ALERT_FLAG_MODAL;
+                    khui_alert_unlock(a);
+
+                    rv = alert_show(a);
+
+                    if (KHM_SUCCEEDED(rv)) {
+                        khm_message_loop_int(&a->displayed);
+                    }
+
+                    khui_alert_release(a);
+                }
+                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;
+                khui_menu_def * mdef;
+                khui_action_ref * act;
+                khm_size i, n;
+                khm_int32 def_cmd;
+
+                /* before we show the context menu, we need to make
+                   sure that the default action for the notification
+                   icon is present in the menu and that it is marked
+                   as the default. */
+
+                def_cmd = get_default_notifier_action();
+
+                if (khm_is_main_window_visible()) {
+                    menu_id = KHUI_MENU_ICO_CTX_NORMAL;
+
+                    if (def_cmd == KHUI_ACTION_OPEN_APP)
+                        def_cmd = KHUI_ACTION_CLOSE_APP;
+                } else {
+                    menu_id = KHUI_MENU_ICO_CTX_MIN;
+                }
+
+                mdef = khui_find_menu(menu_id);
+
+#ifdef DEBUG
+                assert(mdef);
+#endif
+                n = khui_menu_get_size(mdef);
+                for (i=0; i < n; i++) {
+                    act = khui_menu_get_action(mdef, i);
+                    if (!(act->flags & KHUI_ACTIONREF_PACTION) &&
+                        (act->action == def_cmd))
+                        break;
+                }
+
+                if (i < n) {
+                    if (!(act->flags & KHUI_ACTIONREF_DEFAULT)) {
+                        khui_menu_remove_action(mdef, i);
+                        khui_menu_insert_action(mdef, i, def_cmd, KHUI_ACTIONREF_DEFAULT);
+                    } else {
+                        /* we are all set */
+                    }
+                } else {
+                    /* the default action was not found on the context
+                       menu */
+#ifdef DEBUG
+                    assert(FALSE);
+#endif
+                    khui_menu_insert_action(mdef, 0, def_cmd, KHUI_ACTIONREF_DEFAULT);
+                }
+
+                SetForegroundWindow(khm_hwnd_main);
+
+                GetCursorPos(&pt);
+                khm_menu_show_panel(menu_id, pt.x, pt.y);
+
+                PostMessage(khm_hwnd_main, WM_NULL, 0, 0);
+            }
+            break;
+
+        case NIN_SELECT:
+            /* fall through */
+        case NIN_KEYSELECT:
+            /* If there were any alerts waiting to be shown, we show
+               them.  Otherwise we perform the default action. */
+            khm_notify_icon_activate();
+            break;
+
+        case NIN_BALLOONUSERCLICK:
+            if (balloon_alert) {
+                khui_alert * a;
+
+                khm_notify_icon_change(KHERR_NONE);
+
+                a = balloon_alert;
+                balloon_alert = NULL;
+
+                khui_alert_lock(a);
+                a->displayed = FALSE;
+
+                if ((a->flags & KHUI_ALERT_FLAG_DEFACTION) &&
+                    !(a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW) &&
+                    a->n_alert_commands > 0) {
+                    PostMessage(khm_hwnd_main, WM_COMMAND,
+                                MAKEWPARAM(a->alert_commands[0], 
+                                           0),
+                                0);
+                } else if (a->flags & 
+                           KHUI_ALERT_FLAG_REQUEST_WINDOW) {
+                    khm_show_main_window();
+                    alert_show_normal(a);
+                }
+
+                khui_alert_unlock(a);
+                khui_alert_release(a);
+            } else {
+#ifdef DEBUG
+                assert(FALSE);
+#endif
+            }
+            break;
+
+        case NIN_BALLOONHIDE:
+        case NIN_BALLOONTIMEOUT:
+            khm_notify_icon_change(KHERR_NONE);
+            if (balloon_alert) {
+                khui_alert * a;
+                a = balloon_alert;
+                balloon_alert = NULL;
+
+                khui_alert_lock(a);
+                a->displayed = FALSE;
+                khui_alert_unlock(a);
+
+                khui_alert_release(a);
+            }
+            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);
+            kcdb_identity_refresh_all();
+            khm_timer_refresh(hwnd);
+        }
+    }
+
+    return DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+
+ATOM 
+khm_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_alert_data {
+    khui_alert * alert;
+
+    BOOL         seen;          /* has the user seen this alert? */
+
+    BOOL         has_commands;  /* we cache the value here.  otherwise
+                                   we'll have to get a lock on the
+                                   alert each time we have to find out
+                                   whether there are any commands for
+                                   this alert. */
+
+    RECT         r_alert;    /* the entire alert, relative to self. */
+
+    /* the following rects are relative to the top left of r_alert. */
+
+    RECT         r_title;       /* the title.  deflate by padding to
+                                   get the text rect. */
+    RECT         r_icon;        /* rect for icon */
+    RECT         r_message;     /* rect for the text. no padding
+                                   necessary. */
+    RECT         r_suggestion;  /* rect for the suggestion.  deflate
+                                   by padding to get the suggestion
+                                   rect.  The suggestion rect includes
+                                   space for the small icon on the
+                                   left and padding between the icon
+                                   and the text. The size of the small
+                                   icon are as per system metrics
+                                   SM_C{X,Y}SMICON. Padding is
+                                   s_pad.cx vertical. */
+
+    int          n_cmd_buttons; /* number of command buttons in this alert. */
+
+    RECT         r_buttons[KHUI_MAX_ALERT_COMMANDS];
+                                /* rects for the command buttons. */
+
+    HWND         hwnd_buttons[KHUI_MAX_ALERT_COMMANDS];
+                                /* handles for the command buttons */
+
+    HWND         hwnd_marker;
+                                /* handle to the marker window used as
+                                   a tab-stop target when there are
+                                   not buttons associated with the
+                                   alert. */
+
+    LDCL(struct tag_alerter_alert_data);
+} alerter_alert_data;
+
+typedef struct tag_alerter_wnd_data {
+    HWND            hwnd;
+    HFONT           hfont;
+
+    wchar_t         caption[KHUI_MAXCCH_TITLE]; /* the original
+                                                   caption for the
+                                                   dialog. */
+
+    HWND            hw_bin;
+    HWND            hw_scroll;
+    HWND            hw_close;
+
+    int             scroll_top;
+
+    int             n_cmd_buttons; /* total number of command buttons
+                                      in all the alerts being shown in
+                                      this dialog. */
+
+    int             c_alert;    /* current selected alert. */
+
+    /* various metrics */
+    /* calculated during WM_CREATE */
+    SIZE            s_button;   /* minimum dimensions for command button */
+    SIZE            s_margin;
+    RECT            r_text;     /* only .left and .right are used. rest are 0 */
+    RECT            r_title;    /* only .left, .right and .bottom are used. .top=0 */
+    SIZE            s_icon;
+    SIZE            s_pad;
+
+    int             cx_wnd;
+    int             cy_max_wnd;
+
+    /* derived from the alert sizes */
+    SIZE            s_alerts;
+
+    QDCL(alerter_alert_data);   /* queue of alerts that are being
+                                   shown in this window. */
+
+    LDCL(struct tag_alerter_wnd_data); /* for adding to
+                                          khui_alert_windows list. */
+
+    int             n_alerts;
+
+} alerter_wnd_data;
+
+#define NTF_PARAM DWLP_USER
+
+/* dialog sizes in base dialog units */
+
+#define NTF_MARGIN          5
+#define NTF_WIDTH           200
+#define NTF_MAXHEIGHT       150
+
+#define NTF_TITLE_X         NTF_MARGIN
+#define NTF_TITLE_WIDTH     (NTF_WIDTH - NTF_MARGIN*2)
+#define NTF_TITLE_HEIGHT    10
+
+#define NTF_TEXT_PAD        2
+
+#define NTF_BUTTON_HEIGHT   14
+
+#define NTF_TIMEOUT 20000
+
+#define ALERT_WINDOW_EX_SYLES (WS_EX_DLGMODALFRAME | WS_EX_CONTEXTHELP)
+#define ALERT_WINDOW_STYLES   (WS_DLGFRAME | WS_POPUPWINDOW | WS_CLIPCHILDREN | DS_NOIDLEMSG)
+
+/* Control ids */
+#define IDC_NTF_ALERTBIN 998
+#define IDC_NTF_CLOSE    999
+
+#define IDC_NTF_CMDBUTTONS 1001
+#define IDC_FROM_IDX(alert, bn) ((alert) * (KHUI_MAX_ALERT_COMMANDS + 1) + (bn) + 1 + IDC_NTF_CMDBUTTONS)
+#define ALERT_FROM_IDC(idc)  (((idc) - IDC_NTF_CMDBUTTONS) / (KHUI_MAX_ALERT_COMMANDS + 1))
+#define BUTTON_FROM_IDC(idc) (((idc) - IDC_NTF_CMDBUTTONS) % (KHUI_MAX_ALERT_COMMANDS + 1) - 1)
+
+/* if the only command in an alert is "Close", we assume that the
+   alert has no commands. */
+#define ALERT_HAS_CMDS(a) ((a)->n_alert_commands > 1 || ((a)->n_alert_commands == 1 && (a)->alert_commands[0] != KHUI_PACTION_CLOSE))
+
+#define SCROLL_LINE_SIZE(d) ((d)->cy_max_wnd / 12)
+
+static void
+add_alert_to_wnd_data(alerter_wnd_data * d,
+                      khui_alert * a) {
+    alerter_alert_data * aiter;
+    khm_boolean exists = 0;
+
+    khui_alert_lock(a);
+
+    /* check if the alert is already there */
+    aiter = QTOP(d);
+    while(aiter && !exists) {
+        if (aiter->alert) {
+            khui_alert_lock(aiter->alert);
+
+            if (alert_is_equal(aiter->alert, a)) {
+                exists = TRUE;
+            }
+
+            khui_alert_unlock(aiter->alert);
+        }
+
+        aiter = QNEXT(aiter);
+    }
+
+    a->flags |= KHUI_ALERT_FLAG_DISPLAY_WINDOW;
+
+    if (!exists) {
+        a->displayed = TRUE;
+    }
+
+    khui_alert_unlock(a);
+
+    if (!exists) {
+        alerter_alert_data * adata;
+
+        adata = PMALLOC(sizeof(*adata));
+        ZeroMemory(adata, sizeof(*adata));
+
+        adata->alert = a;
+        khui_alert_hold(a);
+
+        QPUT(d, adata);
+        d->n_alerts ++;
+    }
+}
+
+static alerter_wnd_data *
+create_alerter_wnd_data(HWND hwnd, alert_list * l) {
+    alerter_wnd_data * d;
+    int i;
+    LONG dlgb;
+
+    d = PMALLOC(sizeof(*d));
+    ZeroMemory(d, sizeof(*d));
+
+    d->hwnd = hwnd;
+
+    GetWindowText(hwnd, d->caption, ARRAYLENGTH(d->caption));
+
+    for (i=0; i < l->n_alerts; i++) {
+        add_alert_to_wnd_data(d, l->alerts[i]);
+    }
+
+    d->n_alerts = l->n_alerts;
+
+    LPUSH(&khui_alert_windows, d);
+
+    /* Compute a few metrics first */
+
+    dlgb = GetDialogBaseUnits();
+
+#define DLG2SCNX(x) MulDiv((x), LOWORD(dlgb), 4)
+#define DLG2SCNY(y) MulDiv((y), HIWORD(dlgb), 8)
+
+    d->cx_wnd = DLG2SCNX(NTF_WIDTH);
+    d->cy_max_wnd = DLG2SCNY(NTF_MAXHEIGHT);
+
+    d->s_margin.cx = DLG2SCNX(NTF_MARGIN);
+    d->s_margin.cy = DLG2SCNY(NTF_MARGIN);
+
+    d->r_title.left = DLG2SCNX(NTF_TITLE_X);
+    d->r_title.right = DLG2SCNX(NTF_TITLE_X + NTF_TITLE_WIDTH);
+    d->r_title.top = 0;
+    d->r_title.bottom = DLG2SCNY(NTF_TITLE_HEIGHT);
+
+    d->s_pad.cx = DLG2SCNX(NTF_TEXT_PAD);
+    d->s_pad.cy = DLG2SCNY(NTF_TEXT_PAD);
+
+    d->s_icon.cx = GetSystemMetrics(SM_CXICON);
+    d->s_icon.cy = GetSystemMetrics(SM_CYICON);
+
+    d->r_text.left = d->s_margin.cx * 2 + d->s_icon.cx;
+    d->r_text.right = d->cx_wnd - d->s_margin.cx;
+    d->r_text.top = 0;
+    d->r_text.bottom = 0;
+
+    d->s_button.cx = ((d->r_text.right - d->r_text.left) - (KHUI_MAX_ALERT_COMMANDS - 1) * d->s_margin.cx) / KHUI_MAX_ALERT_COMMANDS;
+    d->s_button.cy = DLG2SCNY(NTF_BUTTON_HEIGHT);
+
+#undef DLG2SCNX
+#undef DLG2SCNY
+
+    d->c_alert = -1;
+
+    return d;
+}
+
+static void
+layout_alert(HDC hdc, alerter_wnd_data * d,
+             alerter_alert_data * adata) {
+    RECT r;
+    size_t len;
+    int y;
+    int icon_y;
+
+#ifdef DEBUG
+    assert(adata->alert);
+#endif
+
+    khui_alert_lock(adata->alert);
+
+    y = 0;
+
+    /* Title */
+
+    y += d->s_margin.cy;
+
+    /* If there is a title and it differs from the title of the
+       alerter window, then we have to show the alert title
+       separately. */
+    if (adata->alert->title &&
+        wcscmp(adata->alert->title, d->caption)) {
+
+        CopyRect(&adata->r_title, &d->r_title);
+        OffsetRect(&adata->r_title, 0, y);
+
+        y = adata->r_title.bottom + d->s_margin.cy;
+
+    } else {
+
+        SetRectEmpty(&adata->r_title);
+
+    }
+
+    /* Icon */
+
+    SetRect(&adata->r_icon, d->s_margin.cx, y,
+            d->s_margin.cx + d->s_icon.cx,
+            y + d->s_icon.cy);
+
+    icon_y = adata->r_icon.bottom + d->s_margin.cy; /* the bottom of the icon */
+
+    /* Message */
+
+    if (adata->alert->message &&
+        SUCCEEDED(StringCchLength(adata->alert->message,
+                                  KHUI_MAXCCH_MESSAGE,
+                                  &len))) {
+
+        CopyRect(&r, &d->r_text);
+
+        DrawTextEx(hdc, adata->alert->message, (int) len,
+                   &r,
+                   DRAWTEXTOPTIONS,
+                   NULL);
+
+        OffsetRect(&r, 0, y);
+        CopyRect(&adata->r_message, &r);
+
+        y = r.bottom + d->s_margin.cy;
+
+    } else {
+
+        SetRectEmpty(&adata->r_message);
+
+    }
+
+    /* Suggestion */
+
+    if (adata->alert->suggestion &&
+        SUCCEEDED(StringCchLength(adata->alert->suggestion,
+                                  KHUI_MAXCCH_SUGGESTION,
+                                  &len))) {
+        int pad = d->s_pad.cx + GetSystemMetrics(SM_CXSMICON);
+
+        CopyRect(&r, &d->r_text);
+        r.left += pad;
+
+        DrawTextEx(hdc, adata->alert->suggestion, (int) len,
+                   &r,
+                   DRAWTEXTOPTIONS,
+                   NULL);
+
+        r.left -= pad;
+
+        InflateRect(&r, d->s_pad.cx, d->s_pad.cy);
+        OffsetRect(&r, 0, -r.top + y);
+        CopyRect(&adata->r_suggestion, &r);
+
+        y = r.bottom + d->s_margin.cy;
+
+    } else {
+
+        SetRectEmpty(&adata->r_suggestion);
+
+    }
+
+    y = max(y, icon_y);
+
+    /* Buttons */
+
+    if (ALERT_HAS_CMDS(adata->alert)) {
+        khm_int32 i;
+        int x, width;
+        wchar_t caption[KHUI_MAXCCH_SHORT_DESC];
+        size_t len;
+        SIZE s;
+        int skip_close;
+
+        adata->has_commands = TRUE;
+
+        if (d->n_alerts > 1)
+            skip_close = TRUE;
+        else
+            skip_close = FALSE;
+
+        x = d->r_text.left;
+
+#ifdef DEBUG
+        assert(adata->alert->n_alert_commands <= KHUI_MAX_ALERT_COMMANDS);
+#endif
+
+        for (i=0; i < adata->alert->n_alert_commands; i++) {
+
+            if (adata->alert->alert_commands[i] == KHUI_PACTION_CLOSE && skip_close) {
+                SetRectEmpty(&adata->r_buttons[i]);
+                continue;
+            }
+
+            caption[0] = L'\0';
+            len = 0;
+            khm_get_action_caption(adata->alert->alert_commands[i],
+                                   caption, sizeof(caption));
+            StringCchLength(caption, ARRAYLENGTH(caption), &len);
+
+            if (!GetTextExtentPoint32(hdc, caption, (int) len, &s)) {
+                width = d->s_button.cx;
+            } else {
+                width = s.cx + d->s_margin.cx * 2;
+            }
+
+            if (width < d->s_button.cx)
+                width = d->s_button.cx;
+            else if (width > (d->r_text.right - d->r_text.left))
+                width = d->r_text.right - d->r_text.left;
+
+            if (x + width > d->r_text.right) {
+                /* new line */
+                x = d->r_text.left;
+                y += d->s_button.cy + d->s_pad.cy;
+            }
+
+            SetRect(&adata->r_buttons[i], x, y, x + width, y + d->s_button.cy);
+
+            x += width + d->s_margin.cx;
+        }
+
+        y += d->s_button.cy + d->s_margin.cy;
+    }
+
+    khui_alert_unlock(adata->alert);
+
+    /* Now set the rect for the whole alert */
+    SetRect(&adata->r_alert, 0, 0, d->cx_wnd, y);
+
+}
+
+static void
+pick_title_for_alerter_window(alerter_wnd_data * d) {
+    alerter_alert_data * adata;
+    wchar_t caption[KHUI_MAXCCH_TITLE];
+    khm_boolean common_caption = TRUE;
+    khui_alert_type ctype = KHUI_ALERTTYPE_NONE;
+    khm_boolean common_type = TRUE;
+
+    /* - If all the alerts have the same title, then we use the common
+         title.
+
+       - If all the alerts are of the same type, then we pick a title
+         that is suitable for the type.
+
+       - All else fails, we use a default caption for the window.
+    */
+
+    caption[0] = L'\0';
+    adata = QTOP(d);
+    while (adata && (common_caption || common_type)) {
+
+        if (adata->alert) {
+            khui_alert_lock(adata->alert);
+
+            if (common_caption) {
+                if (caption[0] == L'\0') {
+                    if (adata->alert->title)
+                        StringCbCopy(caption, sizeof(caption), adata->alert->title);
+                } else if (adata->alert->title &&
+                           wcscmp(caption, adata->alert->title)) {
+                    common_caption = FALSE;
+                }
+            }
+
+            if (common_type) {
+                if (ctype == KHUI_ALERTTYPE_NONE)
+                    ctype = adata->alert->alert_type;
+                else if (ctype != adata->alert->alert_type)
+                    common_type = FALSE;
+            }
+
+            khui_alert_unlock(adata->alert);
+        }
+
+        adata = QNEXT(adata);
+    }
+
+    /* just in case someone changes d->caption to a pointer from an
+       array */
+#ifdef DEBUG
+    assert(sizeof(d->caption) > sizeof(wchar_t *));
+#endif
+
+    if (common_caption && caption[0] != L'\0') {
+        StringCbCopy(d->caption, sizeof(d->caption), caption);
+    } else if (common_type && ctype != KHUI_ALERTTYPE_NONE) {
+        switch(ctype) {
+        case KHUI_ALERTTYPE_PLUGIN:
+            LoadString(khm_hInstance, IDS_ALERTTYPE_PLUGIN,
+                       d->caption, ARRAYLENGTH(d->caption));
+            break;
+
+        case KHUI_ALERTTYPE_EXPIRE:
+            LoadString(khm_hInstance, IDS_ALERTTYPE_EXPIRE,
+                       d->caption, ARRAYLENGTH(d->caption));
+            break;
+
+        case KHUI_ALERTTYPE_RENEWFAIL:
+            LoadString(khm_hInstance, IDS_ALERTTYPE_RENEWFAIL,
+                       d->caption, ARRAYLENGTH(d->caption));
+            break;
+
+        case KHUI_ALERTTYPE_ACQUIREFAIL:
+            LoadString(khm_hInstance, IDS_ALERTTYPE_ACQUIREFAIL,
+                       d->caption, ARRAYLENGTH(d->caption));
+            break;
+
+        case KHUI_ALERTTYPE_CHPW:
+            LoadString(khm_hInstance, IDS_ALERTTYPE_CHPW,
+                       d->caption, ARRAYLENGTH(d->caption));
+            break;
+
+        default:
+            LoadString(khm_hInstance, IDS_ALERT_DEFAULT,
+                       d->caption, ARRAYLENGTH(d->caption));
+        }
+    } else {
+        LoadString(khm_hInstance, IDS_ALERT_DEFAULT,
+                   d->caption, ARRAYLENGTH(d->caption));
+    }
+
+    SetWindowText(d->hwnd, d->caption);
+}
+
+static void
+estimate_alerter_wnd_sizes(alerter_wnd_data * d) {
+    HDC hdc;
+    HFONT hf_old;
+    int height = 0;
+
+    alerter_alert_data * adata;
+
+    pick_title_for_alerter_window(d);
+
+    hdc = GetDC(d->hwnd);
+#ifdef DEBUG
+    assert(hdc);
+#endif
+
+    if (d->hfont == NULL)
+        d->hfont = (HFONT) GetStockObject(DEFAULT_GUI_FONT);
+
+#ifdef DEBUG
+    assert(d->hfont);
+#endif
+
+    hf_old = SelectFont(hdc, d->hfont);
+
+    adata = QTOP(d);
+    while(adata) {
+        layout_alert(hdc, d, adata);
+
+        height += adata->r_alert.bottom;
+
+        adata = QNEXT(adata);
+    }
+
+    SelectFont(hdc, hf_old);
+    ReleaseDC(d->hwnd, hdc);
+
+    d->s_alerts.cx = d->cx_wnd;
+    d->s_alerts.cy = height;
+}
+
+static void
+layout_command_buttons(alerter_wnd_data * d) {
+
+    alerter_alert_data * adata;
+    HDWP hdefer;
+    int y;
+
+    hdefer = BeginDeferWindowPos(d->n_cmd_buttons);
+
+    y = 0;
+    adata = QTOP(d);
+    while (adata) {
+        RECT r;
+        int i;
+
+        if (!adata->has_commands)
+            goto done;
+
+        for (i=0; i < adata->n_cmd_buttons; i++) {
+            if (IsRectEmpty(&adata->r_buttons[i])) {
+                /* the button is no longer needed */
+                if (adata->hwnd_buttons[i] != NULL) {
+                    DestroyWindow(adata->hwnd_buttons[i]);
+                    adata->hwnd_buttons[i] = NULL;
+                }
+
+                continue;
+            }
+
+            if (adata->hwnd_buttons[i] == NULL) {
+                continue;
+            }
+
+            CopyRect(&r, &adata->r_buttons[i]);
+            OffsetRect(&r, 0, y - d->scroll_top);
+
+            DeferWindowPos(hdefer,
+                           adata->hwnd_buttons[i], NULL,
+                           r.left, r.top, 0, 0,
+                           SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER |
+                           SWP_NOSIZE);
+        }
+
+    done:
+        y += adata->r_alert.bottom;
+        adata = QNEXT(adata);
+    }
+
+    EndDeferWindowPos(hdefer);
+}
+
+static void
+setup_alerter_window_controls(alerter_wnd_data * d) {
+
+    RECT r_alerts;
+    RECT r_window;
+    RECT r_client;
+    RECT r_parent;
+    HWND hw_parent;
+    HWND hw_focus = NULL;
+    BOOL close_button = FALSE;
+    BOOL scrollbar = FALSE;
+    BOOL redraw_scollbar = FALSE;
+
+    /* estimate_alerter_wnd_sizes() must be called before calling
+       this. */
+#ifdef DEBUG
+    assert(d->s_alerts.cy > 0);
+#endif
+
+    r_alerts.left = 0;
+    r_alerts.top = 0;
+    r_alerts.right = d->cx_wnd;
+
+    if (d->s_alerts.cy > d->cy_max_wnd) {
+
+        BOOL redraw = FALSE;
+
+        r_alerts.right += GetSystemMetrics(SM_CXVSCROLL);
+        r_alerts.bottom = d->cy_max_wnd;
+
+        CopyRect(&r_client, &r_alerts);
+        r_client.bottom += d->s_margin.cy + d->s_button.cy + d->s_pad.cy;
+        close_button = TRUE;
+
+        if (d->scroll_top > d->s_alerts.cy - d->cy_max_wnd)
+            d->scroll_top = d->s_alerts.cy - d->cy_max_wnd;
+
+        scrollbar = TRUE;
+    } else {
+        r_alerts.bottom = d->s_alerts.cy;
+
+        CopyRect(&r_client, &r_alerts);
+
+        if (d->n_alerts == 1) {
+
+            if (!QTOP(d)->has_commands) {
+                r_client.bottom += d->s_margin.cy * 2 + d->s_button.cy;
+                close_button = TRUE;
+            }
+
+        } else {
+
+            r_client.bottom += d->s_margin.cy * 2 + d->s_button.cy;
+            close_button = TRUE;
+        }
+
+        d->scroll_top = 0;
+    }
+
+    if (d->hw_bin == NULL) {
+        d->hw_bin = CreateWindowEx(WS_EX_CONTROLPARENT,
+                                   MAKEINTATOM(atom_alert_bin),
+                                   L"Alert Container",
+                                   WS_CHILD | WS_CLIPCHILDREN |
+                                   WS_VISIBLE |
+                                   ((scrollbar)? WS_VSCROLL : 0),
+                                   r_alerts.left, r_alerts.top,
+                                   r_alerts.right - r_alerts.left,
+                                   r_alerts.bottom - r_alerts.top,
+                                   d->hwnd,
+                                   (HMENU) IDC_NTF_ALERTBIN,
+                                   khm_hInstance,
+                                   (LPVOID) d);
+    } else {
+        redraw_scollbar = TRUE;
+        SetWindowLongPtr(d->hw_bin, GWL_STYLE,
+                         WS_CHILD | WS_CLIPCHILDREN |
+                         WS_VISIBLE |
+                         ((scrollbar)? WS_VSCROLL : 0));
+        SetWindowPos(d->hw_bin, NULL,
+                     r_alerts.left, r_alerts.top,
+                     r_alerts.right - r_alerts.left,
+                     r_alerts.bottom - r_alerts.top,
+                     SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOACTIVATE);
+    }
+
+    if (scrollbar) {
+        SCROLLINFO si;
+
+        ZeroMemory(&si, sizeof(si));
+        si.cbSize = sizeof(si);
+        si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
+        si.nMin = 0;
+        si.nMax = d->s_alerts.cy;
+        si.nPage = d->cy_max_wnd;
+        si.nPos = d->scroll_top;
+
+        SetScrollInfo(d->hw_bin, SB_VERT, &si, redraw_scollbar);
+    }
+
+    /* create the action buttons */
+    {
+        alerter_alert_data * adata;
+        int y;
+        int idx;
+        HWND last_window = HWND_TOP;
+        int n_buttons = 0;
+
+        idx = 0;
+        y = - d->scroll_top;
+        adata = QTOP(d);
+        while(adata) {
+            if (adata->has_commands) {
+                int i;
+                wchar_t caption[KHUI_MAXCCH_SHORT_DESC];
+                RECT r;
+
+                if (adata->hwnd_marker) {
+                    DestroyWindow(adata->hwnd_marker);
+                    adata->hwnd_marker = NULL;
+                }
+
+                khui_alert_lock(adata->alert);
+
+                adata->n_cmd_buttons = adata->alert->n_alert_commands;
+
+                for (i=0; i < adata->alert->n_alert_commands; i++) {
+
+                    n_buttons ++;
+
+                    if (IsRectEmpty(&adata->r_buttons[i])) {
+                        /* this button is not necessary */
+                        if (adata->hwnd_buttons[i]) {
+                            DestroyWindow(adata->hwnd_buttons[i]);
+                            adata->hwnd_buttons[i] = NULL;
+                        }
+
+                        continue;
+                    }
+
+                    if (adata->hwnd_buttons[i] != NULL) {
+                        /* already there */
+                        CopyRect(&r, &adata->r_buttons[i]);
+                        OffsetRect(&r, 0, y);
+
+                        SetWindowPos(adata->hwnd_buttons[i], last_window,
+                                     r.left, r.top,
+                                     r.right - r.left,
+                                     r.bottom - r.top,
+                                     SWP_NOACTIVATE | SWP_NOOWNERZORDER |
+                                     SWP_SHOWWINDOW);
+
+                        last_window = adata->hwnd_buttons[i];
+
+                        if (hw_focus == NULL)
+                            hw_focus = adata->hwnd_buttons[i];
+
+                        continue;
+                    }
+
+                    khm_get_action_caption(adata->alert->alert_commands[i],
+                                           caption, sizeof(caption));
+
+                    CopyRect(&r, &adata->r_buttons[i]);
+                    OffsetRect(&r, 0, y);
+
+                    adata->hwnd_buttons[i] =
+                        CreateWindowEx(0,
+                                       L"BUTTON",
+                                       caption,
+                                       WS_CHILD | WS_TABSTOP | BS_NOTIFY,
+                                       r.left, r.top,
+                                       r.right - r.left,
+                                       r.bottom - r.top,
+                                       d->hw_bin,
+                                       (HMENU) (INT_PTR) IDC_FROM_IDX(idx, i),
+                                       khm_hInstance,
+                                       NULL);
+#ifdef DEBUG
+                    assert(adata->hwnd_buttons[i]);
+#endif
+
+                    if (d->hfont) {
+                        SendMessage(adata->hwnd_buttons[i], WM_SETFONT,
+                                    (WPARAM) d->hfont, FALSE);
+                    }
+
+                    SetWindowPos(adata->hwnd_buttons[i], last_window,
+                                 0, 0, 0, 0,
+                                 SWP_NOACTIVATE | SWP_NOOWNERZORDER |
+                                 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
+
+                    last_window = adata->hwnd_buttons[i];
+
+                    if (hw_focus == NULL)
+                        hw_focus = adata->hwnd_buttons[i];
+                }
+
+                khui_alert_unlock(adata->alert);
+            } else {
+                int i;
+
+                /* Destroy any buttons that belong to the alert. We
+                   might have some left over, if there were command
+                   belonging to the alert that were ignored.*/
+
+                for (i=0; i < adata->n_cmd_buttons; i++) {
+                    if (adata->hwnd_buttons[i]) {
+                        DestroyWindow(adata->hwnd_buttons[i]);
+                        adata->hwnd_buttons[i] = NULL;
+                    }
+                }
+
+                adata->n_cmd_buttons = 0;
+
+                if (adata->hwnd_marker == NULL) {
+                    adata->hwnd_marker =
+                        CreateWindowEx(0,
+                                       L"BUTTON",
+                                       L"Marker",
+                                       WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_NOTIFY,
+                                       -10, 0,
+                                       5, 5,
+                                       d->hw_bin,
+                                       (HMENU) (INT_PTR) IDC_FROM_IDX(idx, -1),
+                                       khm_hInstance,
+                                       NULL);
+#ifdef DEBUG
+                    assert(adata->hwnd_marker);
+#endif
+                }
+
+                SetWindowPos(adata->hwnd_marker, last_window,
+                             0, 0, 0, 0,
+                             SWP_NOACTIVATE | SWP_NOOWNERZORDER |
+                             SWP_NOMOVE | SWP_NOSIZE);
+
+                last_window = adata->hwnd_marker;
+
+                if (scrollbar) {
+                    EnableWindow(adata->hwnd_marker, TRUE);
+                    if (hw_focus == NULL)
+                        hw_focus = adata->hwnd_marker;
+                } else {
+                    EnableWindow(adata->hwnd_marker, FALSE);
+                }
+            }
+
+            y += adata->r_alert.bottom;
+            adata = QNEXT(adata);
+            idx++;
+        }
+
+        d->n_cmd_buttons = n_buttons;
+    }
+
+    if (close_button) {
+        if (d->hw_close == NULL) {
+            wchar_t caption[256];
+
+            khm_get_action_caption(KHUI_PACTION_CLOSE, caption, sizeof(caption));
+
+            d->hw_close = CreateWindowEx(0,
+                                         L"BUTTON",
+                                         caption,
+                                         WS_CHILD | BS_DEFPUSHBUTTON | WS_TABSTOP | BS_NOTIFY,
+                                         0,0,100,100,
+                                         d->hwnd,
+                                         (HMENU) IDC_NTF_CLOSE,
+                                         khm_hInstance,
+                                         NULL);
+
+#ifdef DEBUG
+            assert(d->hw_close);
+            assert(d->hfont);
+#endif
+            if (d->hfont)
+                SendMessage(d->hw_close, WM_SETFONT, (WPARAM) d->hfont, FALSE);
+        }
+
+        {
+            int x,y,width,height;
+
+            x = d->r_text.left;
+            y = r_client.bottom - (d->s_margin.cy + d->s_button.cy);
+            width = d->s_button.cx;
+            height = d->s_button.cy;
+
+            SetWindowPos(d->hw_close, NULL,
+                         x, y, width, height,
+                         SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER |
+                         SWP_SHOWWINDOW);
+        }
+
+        if (hw_focus == NULL || d->n_cmd_buttons == 0)
+            hw_focus = d->hw_close;
+
+    } else {
+        if (d->hw_close != NULL) {
+            DestroyWindow(d->hw_close);
+            d->hw_close = NULL;
+        }
+    }
+
+    CopyRect(&r_window, &r_client);
+    AdjustWindowRectEx(&r_window, ALERT_WINDOW_STYLES,
+                       FALSE, ALERT_WINDOW_EX_SYLES);
+    OffsetRect(&r_window, -r_window.left, -r_window.top);
+
+    /* center the window above the parent window. */
+
+    hw_parent = GetWindow(d->hwnd, GW_OWNER);
+    GetWindowRect(hw_parent, &r_parent);
+
+    {
+        int x,y;
+
+        x = (r_parent.left + r_parent.right - (r_window.right - r_window.left)) / 2;
+        y = (r_parent.top + r_parent.bottom - (r_window.bottom - r_window.top)) / 2;
+
+        SetWindowPos(d->hwnd,
+                     HWND_TOP,
+                     x, y,
+                     r_window.right - r_window.left,
+                     r_window.bottom - r_window.top,
+                     SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
+    }
+
+    if (hw_focus != NULL)
+        PostMessage(d->hwnd, WM_NEXTDLGCTL, (WPARAM) hw_focus, MAKELPARAM(TRUE, 0));
+}
+
+static void
+scroll_to_position(alerter_wnd_data * d, int new_pos, khm_boolean redraw_scrollbar) {
+    int delta;
+    SCROLLINFO si;
+    HWND hwnd = d->hw_bin;
+
+    if (new_pos < 0)
+        new_pos = 0;
+    else if (new_pos > d->s_alerts.cy - d->cy_max_wnd)
+        new_pos = d->s_alerts.cy - d->cy_max_wnd;
+
+    if (new_pos == d->scroll_top)
+        return;
+
+    delta = d->scroll_top - new_pos;
+
+    d->scroll_top -= delta;
+
+    ScrollWindowEx(hwnd, 0, delta,
+                   NULL, NULL, NULL, NULL,
+                   SW_INVALIDATE | SW_ERASE);
+
+    layout_command_buttons(d);
+
+    ZeroMemory(&si, sizeof(si));
+
+    si.fMask = SIF_POS;
+    si.nPos = d->scroll_top;
+
+    SetScrollInfo(hwnd, SB_VERT, &si, redraw_scrollbar);
+}
+
+static void
+select_alert(alerter_wnd_data * d, int alert) {
+
+    int y;
+    RECT old_sel, new_sel;
+    alerter_alert_data * adata;
+    int idx;
+
+    if (d->n_alerts == 1 ||
+        alert < 0 ||
+        alert > d->n_alerts ||
+        d->c_alert == alert)
+        return;
+
+    SetRectEmpty(&old_sel);
+    SetRectEmpty(&new_sel);
+    idx = 0; y = -d->scroll_top;
+    adata = QTOP(d);
+    while(adata && (idx <= d->c_alert || idx <= alert)) {
+
+        if (idx == d->c_alert) {
+            CopyRect(&old_sel, &adata->r_alert);
+            OffsetRect(&old_sel, 0, y);
+        }
+
+        if (idx == alert) {
+            CopyRect(&new_sel, &adata->r_alert);
+            OffsetRect(&new_sel, 0, y);
+        }
+
+        y += adata->r_alert.bottom;
+        idx ++;
+        adata = QNEXT(adata);
+    }
+
+    d->c_alert = alert;
+    if (!IsRectEmpty(&old_sel))
+        InvalidateRect(d->hw_bin, &old_sel, TRUE);
+    if (!IsRectEmpty(&new_sel))
+        InvalidateRect(d->hw_bin, &new_sel, TRUE);
+}
+
+static void
+ensure_command_is_visible(alerter_wnd_data * d, int id) {
+    int alert_idx;
+    int y = 0;
+    alerter_alert_data * adata;
+    int new_pos = 0;
+
+    alert_idx = ALERT_FROM_IDC(id);
+
+#ifdef DEBUG
+    assert(alert_idx >= 0 && alert_idx < d->n_alerts);
+#endif
+    if (alert_idx >= d->n_alerts || alert_idx < 0)
+        return;
+
+    adata = QTOP(d);
+    while(adata && alert_idx > 0) {
+        y += adata->r_alert.bottom;
+        alert_idx--;
+        adata = QNEXT(adata);
+    }
+
+#ifdef DEBUG
+    assert(alert_idx == 0);
+    assert(adata);
+    assert(adata->alert);
+#endif
+    if (adata == NULL || alert_idx != 0)
+        return;
+
+    new_pos = d->scroll_top;
+    if (y < d->scroll_top) {
+        new_pos = y;
+    } else if (y + adata->r_alert.bottom > d->scroll_top + d->cy_max_wnd) {
+        new_pos = y + adata->r_alert.bottom - d->cy_max_wnd;
+    }
+
+    if (new_pos != d->scroll_top)
+        scroll_to_position(d, new_pos, TRUE);
+
+    select_alert(d, ALERT_FROM_IDC(id));
+}
+
+static void
+handle_mouse_select(alerter_wnd_data * d, int mouse_x, int mouse_y) {
+    int y;
+    alerter_alert_data * adata;
+
+    y = -d->scroll_top;
+    adata = QTOP(d);
+    while(adata) {
+        if (y <= mouse_y && (y + adata->r_alert.bottom) > mouse_y) {
+            HWND hw = NULL;
+
+            if (adata->n_cmd_buttons > 0)
+                hw = adata->hwnd_buttons[0];
+            else
+                hw = adata->hwnd_marker;
+
+            if (hw && !IsWindowEnabled(hw))
+                hw = GetNextDlgTabItem(d->hwnd, hw, FALSE);
+
+            if (hw)
+                PostMessage(d->hwnd, WM_NEXTDLGCTL, (WPARAM) hw, MAKELPARAM(TRUE, 0));
+
+            return;
+        }
+
+        y += adata->r_alert.bottom;
+        adata = QNEXT(adata);
+    }
+}
+
+static void
+process_command_button(alerter_wnd_data * d, int id) {
+    int alert_idx;
+    int cmd_idx;
+    khm_int32 flags = 0;
+    khm_int32 cmd = 0;
+    alerter_alert_data * adata;
+    int i;
+
+    alert_idx = ALERT_FROM_IDC(id);
+    cmd_idx = BUTTON_FROM_IDC(id);
+
+#ifdef DEBUG
+    assert(alert_idx >= 0 && alert_idx < d->n_alerts);
+#endif
+    if (alert_idx >= d->n_alerts || alert_idx < 0)
+        return;
+
+    if (cmd_idx < 0) {
+        /* the user selected a marker button.  Nothing to do. */
+        return;
+    }
+
+    adata = QTOP(d);
+    while(adata && alert_idx > 0) {
+        alert_idx--;
+        adata = QNEXT(adata);
+    }
+
+#ifdef DEBUG
+    assert(alert_idx == 0);
+    assert(adata);
+    assert(adata->alert);
+#endif
+    if (adata == NULL || alert_idx != 0)
+        return;
+
+    khui_alert_lock(adata->alert);
+#ifdef DEBUG
+    assert(cmd_idx >= 0 && cmd_idx < adata->alert->n_alert_commands);
+#endif
+
+    if (cmd_idx >= 0 && cmd_idx < adata->alert->n_alert_commands) {
+        cmd = adata->alert->alert_commands[cmd_idx];
+    }
+
+    flags = adata->alert->flags;
+
+    adata->alert->response = cmd;
+
+    khui_alert_unlock(adata->alert);
+
+    /* if we were supposed to dispatch the command, do so */
+    if (cmd != 0 &&
+        cmd != KHUI_PACTION_CLOSE &&
+        (flags & KHUI_ALERT_FLAG_DISPATCH_CMD)) {
+        PostMessage(khm_hwnd_main, WM_COMMAND,
+                    MAKEWPARAM(cmd, 0), 0);
+    }
+
+    /* if this was the only alert in the alert group and its close
+       button was clicked, we close the alert window.  Otherwise, the
+       alert window creates its own close button that closes the
+       window. */
+    if (d->n_alerts == 1) {
+        PostMessage(d->hwnd, WM_CLOSE, 0, 0);
+    }
+
+    /* While we are at it, we should disable the buttons for this
+       alert since we have already dispatched the command for it. */
+    if (cmd != 0) {
+        HWND hw_focus = GetFocus();
+        khm_boolean focus_trapped = FALSE;
+
+        for (i=0; i < adata->n_cmd_buttons; i++) {
+            if (adata->hwnd_buttons[i]) {
+                if (hw_focus == adata->hwnd_buttons[i])
+                    focus_trapped = TRUE;
+
+                EnableWindow(adata->hwnd_buttons[i], FALSE);
+            }
+        }
+
+        if (focus_trapped) {
+            hw_focus = GetNextDlgTabItem(d->hwnd, hw_focus, FALSE);
+            if (hw_focus)
+                PostMessage(d->hwnd, WM_NEXTDLGCTL, (WPARAM) hw_focus, MAKELPARAM(TRUE,0));
+        }
+    }
+}
+
+static void
+destroy_alerter_wnd_data(alerter_wnd_data * d) {
+    alerter_alert_data * adata;
+
+    LDELETE(&khui_alert_windows, d);
+
+    QGET(d, &adata);
+    while(adata) {
+
+        if (adata->alert) {
+
+            khui_alert_lock(adata->alert);
+
+            adata->alert->displayed = FALSE;
+
+            khui_alert_unlock(adata->alert);
+
+            khui_alert_release(adata->alert);
+            adata->alert = NULL;
+        }
+
+        PFREE(adata);
+
+        QGET(d, &adata);
+    }
+
+    PFREE(d);
+}
+
+/* both ref and to_add must be locked and held */
+static khm_boolean
+alert_can_consolidate(khui_alert * ref,
+                      khui_alert * to_add,
+                      alert_list * alist) {
+
+    /* first check if we can add anything */
+    if (alist->n_alerts == ARRAYLENGTH(alist->alerts))
+        return FALSE;
+
+#ifdef DEBUG
+    assert(to_add != NULL);
+#endif
+
+    if (ref == NULL) {
+        /* we are testing whether to_add should be added to the alist
+           on its own. */
+        if ((to_add->flags & KHUI_ALERT_FLAG_DISPLAY_BALLOON) &&
+            !(to_add->flags & KHUI_ALERT_FLAG_DISPLAY_WINDOW)) {
+            /* already displayed */
+            return FALSE;
+        }
+
+        if ((to_add->flags & (KHUI_ALERT_FLAG_REQUEST_BALLOON |
+                              KHUI_ALERT_FLAG_REQUEST_WINDOW)) == KHUI_ALERT_FLAG_REQUEST_BALLOON) {
+            /* needs to be shown in a balloon */
+            return FALSE;
+        }
+
+        return TRUE;
+    }
+
+    /* if the ref or to_add are marked for modal, then we can't
+       consolidate them */
+    if ((ref->flags & KHUI_ALERT_FLAG_MODAL) ||
+        (to_add->flags & KHUI_ALERT_FLAG_MODAL))
+        return FALSE;
+
+    /* also, if either of them have requested to be exclusively shown
+       in a balloon, then we can't consolidate them. */
+    if (((ref->flags & (KHUI_ALERT_FLAG_REQUEST_BALLOON |
+                        KHUI_ALERT_FLAG_REQUEST_WINDOW)) == KHUI_ALERT_FLAG_REQUEST_BALLOON)
+
+        ||
+
+        ((to_add->flags & (KHUI_ALERT_FLAG_REQUEST_BALLOON |
+                           KHUI_ALERT_FLAG_REQUEST_WINDOW)) == KHUI_ALERT_FLAG_REQUEST_BALLOON))
+        return FALSE;
+
+    /* for now, all we check if whether they are of the same type. */
+    if (ref->alert_type != KHUI_ALERTTYPE_NONE &&
+        ref->alert_type == to_add->alert_type)
+        return TRUE;
+    else
+        return FALSE;
+}
+
+/* both a1 and a2 must be locked */
+static khm_boolean
+alert_is_equal(khui_alert * a1, khui_alert * a2) {
+    khm_int32 i;
+
+    if ((a1->severity != a2->severity) ||
+        (a1->n_alert_commands != a2->n_alert_commands) ||
+        (a1->title && (!a2->title || wcscmp(a1->title, a2->title))) ||
+        (!a1->title && a2->title) ||
+        (a1->message && (!a2->message || wcscmp(a1->message, a2->message))) ||
+        (!a1->message && a2->message) ||
+        (a1->suggestion && (!a2->suggestion || wcscmp(a1->suggestion, a2->suggestion))) ||
+        (!a1->suggestion && a2->suggestion)) {
+
+        return FALSE;
+
+    }
+
+    for (i=0; i < a1->n_alert_commands; i++) {
+        if (a1->alert_commands[i] != a2->alert_commands[i])
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+/* the return value is the number of alerts added to alist */
+static khm_int32
+alert_consolidate(alert_list * alist,
+                  khui_alert * alert,
+                  khm_boolean add_from_queue) {
+
+    khui_alert * listtop;
+    int queue_size = 0;
+    int i;
+    khm_int32 n_added = 0;
+
+#ifdef DEBUG
+    assert(alist);
+#endif
+
+    if (alist->n_alerts == ARRAYLENGTH(alist->alerts)) {
+        /* can't add anything */
+
+        return 0;
+    }
+
+    /* if the list is empty, we just add one alert */
+    if (alist->n_alerts == 0) {
+
+        if (alert) {
+            khui_alert_lock(alert);
+            if (alert_can_consolidate(NULL, alert, alist)) {
+                alert_list_add_alert(alist, alert);
+                n_added ++;
+                alert = NULL;
+            }
+            khui_alert_unlock(alert);
+        }
+
+        if (n_added == 0 && add_from_queue) {
+            khui_alert * q;
+            int i;
+
+            queue_size = alert_queue_get_size();
+            for (i=0; i < queue_size && n_added == 0; i++) {
+                q = alert_queue_get_alert_by_pos(i);
+                if (q) {
+                    khui_alert_lock(q);
+                    if (alert_can_consolidate(NULL, q, alist)) {
+                        alert_list_add_alert(alist, q);
+                        n_added++;
+                        alert_queue_delete_alert(q);
+                    }
+                    khui_alert_unlock(q);
+                    khui_alert_release(q);
+                }
+            }
+        }
+
+        if (n_added == 0) {
+            /* nothing to add */
+            return 0;
+        }
+    }
+
+    /* at this point, the alert list is not empty */
+#ifdef DEBUG
+    assert(alist->n_alerts != 0);
+    assert(alist->alerts[0]);
+#endif
+
+    listtop = alist->alerts[0];
+    khui_alert_hold(listtop);
+    khui_alert_lock(listtop);
+
+    queue_size = alert_queue_get_size();
+
+    if (alert) {
+        khui_alert_lock(alert);
+        if (alert_can_consolidate(listtop, alert, alist)) {
+            alert_list_add_alert(alist, alert);
+            n_added ++;
+        }
+        khui_alert_unlock(alert);
+    }
+
+    if (add_from_queue) {
+        for (i=0; i < queue_size; i++) {
+            khui_alert * a;
+
+            a = alert_queue_get_alert_by_pos(i);
+            if (a == NULL)
+                continue;
+
+            khui_alert_lock(a);
+            if (alert_can_consolidate(listtop, a, alist)) {
+                alert_queue_delete_alert(a);
+                alert_list_add_alert(alist, a);
+                n_added ++;
+
+                queue_size--;
+                i--;
+#ifdef DEBUG
+                assert(alert_queue_get_size() == queue_size);
+#endif
+            }
+            khui_alert_unlock(a);
+            khui_alert_release(a);
+        }
+    }
+
+    khui_alert_unlock(listtop);
+    khui_alert_release(listtop);
+
+    return n_added;
+}
+
+static khm_int32
+alert_check_consolidate_window(alerter_wnd_data * d, khui_alert * a) {
+    alert_list alist;
+    alerter_alert_data * adata;
+    int n_added;
+
+    alert_list_init(&alist);
+
+    adata = QTOP(d);
+    while(adata) {
+
+#ifdef DEBUG
+        assert(adata->alert);
+#endif
+        alert_list_add_alert(&alist, adata->alert);
+
+        adata = QNEXT(adata);
+    }
+
+    n_added = alert_consolidate(&alist, a, FALSE);
+
+    alert_list_destroy(&alist);
+
+    return n_added;
+}
+
+static khm_int32 
+alert_show_minimized(khui_alert * a) {
+    wchar_t tbuf[64];           /* corresponds to NOTIFYICONDATA::szInfoTitle[] */
+    wchar_t mbuf[256];          /* corresponds to NOTIFYICONDATA::szInfo[] */
+
+#ifdef DEBUG
+    assert(a);
+#endif
+    if (a == NULL)
+        return KHM_ERROR_INVALID_PARAM;
+
+    khui_alert_lock(a);
+
+    if (a->message == NULL)
+        goto done;
+
+    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;
+
+#ifdef DEBUG
+    assert(balloon_alert == NULL);
+#endif
+
+    if (balloon_alert) {
+        khui_alert_lock(balloon_alert);
+        balloon_alert->displayed = FALSE;
+        khui_alert_unlock(balloon_alert);
+        khui_alert_release(balloon_alert);
+        balloon_alert = NULL;
+    }
+
+    balloon_alert = a;
+    khui_alert_hold(a);
+
+    a->displayed = TRUE;
+
+    khm_notify_icon_balloon(a->severity,
+                            tbuf,
+                            mbuf,
+                            NTF_TIMEOUT);
+
+ done:
+    khui_alert_unlock(a);
+
+    return KHM_ERROR_SUCCESS;
+}
+
+static khm_int32 
+alert_show_normal(khui_alert * a) {
+    wchar_t buf[256];
+    wchar_t * title;
+    alert_list alist;
+
+    khui_alert_lock(a);
+
+    if(a->title == NULL) {
+        LoadString(khm_hInstance, IDS_ALERT_DEFAULT, 
+                   buf, ARRAYLENGTH(buf));
+        title = buf;
+    } else
+        title = a->title;
+
+    khui_alert_unlock(a);
+
+    alert_list_init(&alist);
+    alert_list_set_title(&alist, title);
+    alert_list_add_alert(&alist, a);
+
+    alert_show_list(&alist);
+
+    alert_list_destroy(&alist);
+
+    return KHM_ERROR_SUCCESS;
+}
+
+static khm_int32
+alert_show_list(alert_list * alist) {
+    HWND hwa;
+
+    /* we don't need to keep track of the window handle
+       because the window procedure adds it to the dialog
+       list automatically */
+
+    hwa = 
+        CreateWindowEx(ALERT_WINDOW_EX_SYLES,
+                       MAKEINTATOM(atom_alerter),
+                       alist->title,
+                       ALERT_WINDOW_STYLES,
+                       0, 0, 300, 300, // bogus values
+                       khm_hwnd_main,
+                       (HMENU) NULL,
+                       khm_hInstance,
+                       (LPVOID) alist);
+
+    ShowWindow(hwa, SW_SHOW);
+
+    return (hwa != NULL);
+}
+
+static khm_int32 
+alert_show(khui_alert * a) {
+    khm_boolean show_normal = FALSE;
+    khm_boolean show_mini = FALSE;
+
+    khui_alert_lock(a);
+
+    /* is there an alert already?  If so, we just enqueue the message
+       and let it sit. */
+    if (ALERT_DISPLAYED() &&
+        !(a->flags & KHUI_ALERT_FLAG_MODAL)) {
+        khm_int32 rv;
+        alerter_wnd_data * wdata;
+
+        khui_alert_unlock(a);
+
+        /* if there are any alerter windows displayed, check if this
+           alert can be consolidated with any of them.  If so, we
+           should consolidate it.  Otherwise, just enqueue it. */
+        for(wdata = khui_alert_windows;
+            wdata;
+            wdata = LNEXT(wdata)) {
+            if (alert_check_consolidate_window(wdata, a)) {
+
+                add_alert_to_wnd_data(wdata, a);
+                estimate_alerter_wnd_sizes(wdata);
+                setup_alerter_window_controls(wdata);
+
+                return KHM_ERROR_SUCCESS;
+
+            }
+        }
+
+        rv = alert_enqueue(a);
+
+        if (KHM_SUCCEEDED(rv))
+            return KHM_ERROR_HELD;
+        else
+            return rv;
+    }
+
+    if((a->flags & KHUI_ALERT_FLAG_DISPLAY_WINDOW) ||
+       ((a->flags & KHUI_ALERT_FLAG_DISPLAY_BALLOON) &&
+        !(a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW))) {
+
+        /* The alert has already been displayed. */
+
+        show_normal = FALSE;
+        show_mini = FALSE;
+
+    } else {
+
+        if(a->err_context != NULL ||
+           a->err_event != NULL) {
+            a->flags |= KHUI_ALERT_FLAG_VALID_ERROR;
+        }
+
+        /* depending on the state of the main window, we
+           need to either show a window or a balloon */
+        if ((a->flags & KHUI_ALERT_FLAG_MODAL) ||
+            (khm_is_main_window_active() &&
+             !(a->flags & KHUI_ALERT_FLAG_REQUEST_BALLOON)) ||
+            (a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW)) {
+
+            show_normal = TRUE;
+
+        } else {
+
+            show_mini = TRUE;
+
+        }
+    }
+
+    khui_alert_unlock(a);
+
+    if (show_normal)
+        return alert_show_normal(a);
+    else if (show_mini)
+        return alert_show_minimized(a);
+    else
+        return KHM_ERROR_SUCCESS;
+}
+
+static void
+show_queued_alerts(void) {
+
+    if (!ALERT_DISPLAYED()) {
+
+        /* show next consolidated batch */
+        alert_list alist;
+        int n;
+
+        alert_list_init(&alist);
+        n = alert_consolidate(&alist, NULL, TRUE);
+
+        if (n) {
+            if (n == 1) {
+                khui_alert_lock(alist.alerts[0]);
+
+                if (alist.alerts[0]->title) {
+                    alert_list_set_title(&alist, alist.alerts[0]->title);
+                } else {
+                    wchar_t title[KHUI_MAXCCH_TITLE];
+                    LoadString(khm_hInstance, IDS_ALERT_DEFAULT,
+                               title, ARRAYLENGTH(title));
+                    alert_list_set_title(&alist, title);
+                }
+
+                khui_alert_unlock(alist.alerts[0]);
+            } else {
+                wchar_t title[KHUI_MAXCCH_TITLE];
+                LoadString(khm_hInstance, IDS_ALERT_DEFAULT,
+                           title, ARRAYLENGTH(title));
+                alert_list_set_title(&alist, title);
+            }
+
+            alert_show_list(&alist);
+        }
+
+        alert_list_destroy(&alist);
+
+        if (n == 0) {
+            khui_alert * a;
+
+            /* no alerts were shown above.  This maybe because none of
+               the alerts were consolidatable or they were requested
+               to be shown in a balloon.  In this case, we just take
+               the first alert from the queue and show it manually. */
+
+            a = alert_queue_get_alert();
+            if (a) {
+                alert_show(a);
+                khui_alert_release(a);
+            }
+        }
+
+        check_for_queued_alerts();
+    }
+}
+
+
+static void
+check_for_queued_alerts(void) {
+    if (!is_alert_queue_empty()) {
+        khui_alert * a;
+
+        a = alert_queue_peek();
+
+        khui_alert_lock(a);
+
+        if (a->title) {
+            HICON hi;
+            int res;
+
+            if (a->severity == KHERR_ERROR)
+                res = OIC_ERROR;
+            else if (a->severity == KHERR_WARNING)
+                res = OIC_WARNING;
+            else
+                res = OIC_INFORMATION;
+
+            hi = LoadImage(0, MAKEINTRESOURCE(res),
+                           IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
+                           LR_SHARED);
+
+            khm_statusbar_set_part(KHUI_SBPART_NOTICE,
+                                   hi,
+                                   a->title);
+        } else {
+            khm_statusbar_set_part(KHUI_SBPART_NOTICE,
+                                   NULL, NULL);
+#ifdef DEBUG
+            DebugBreak();
+#endif
+        }
+
+        khui_alert_unlock(a);
+        khui_alert_release(a);
+
+    } else {
+        khm_statusbar_set_part(KHUI_SBPART_NOTICE,
+                               NULL, NULL);
+    }
+}
+
+static khm_int32
+alert_enqueue(khui_alert * a) {
+    if (is_alert_queue_full())
+        return KHM_ERROR_NO_RESOURCES;
+
+    alert_queue_put_alert(a);
+    check_for_queued_alerts();
+
+    return KHM_ERROR_SUCCESS;
+}
+
+/* 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:
+        {
+            LPCREATESTRUCT lpcs;
+            alert_list * alist;
+            alerter_wnd_data * d;
+
+            lpcs = (LPCREATESTRUCT) lParam;
+            alist = (alert_list *) lpcs->lpCreateParams;
+
+            d = create_alerter_wnd_data(hwnd, alist);
+
+#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);
+
+            estimate_alerter_wnd_sizes(d);
+            setup_alerter_window_controls(d);
+
+            if (d->hw_close) {
+                SetFocus(d->hw_close);
+            }
+
+            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);
+
+            destroy_alerter_wnd_data(d);
+
+            return TRUE;
+        }
+        break;
+
+    case WM_COMMAND:
+        {
+            alerter_wnd_data * d;
+
+            d = (alerter_wnd_data *)(LONG_PTR) 
+                GetWindowLongPtr(hwnd, NTF_PARAM);
+
+            if(HIWORD(wParam) == BN_CLICKED) {
+                if (LOWORD(wParam) == IDC_NTF_CLOSE ||
+                    LOWORD(wParam) == KHUI_PACTION_NEXT) {
+
+                    khm_leave_modal();
+
+                    DestroyWindow(hwnd);
+
+                    return 0;
+                }
+            }
+        }
+        break;
+
+    case WM_CLOSE:
+        {
+            khm_leave_modal();
+
+            DestroyWindow(hwnd);
+
+            return 0;
+        }
+    }
+
+    /* Since this is a custom built dialog, we use DefDlgProc instead
+       of DefWindowProc. */
+    return DefDlgProc(hwnd, uMsg, wParam, lParam);
+}
+
+static LRESULT CALLBACK 
+alert_bin_wnd_proc(HWND hwnd,
+                   UINT uMsg,
+                   WPARAM wParam,
+                   LPARAM lParam)
+{
+    BOOL in_printclient = FALSE;
+
+    switch(uMsg) {
+    case WM_CREATE:
+        {
+            LPCREATESTRUCT lpcs;
+            alerter_wnd_data * d;
+
+            lpcs = (LPCREATESTRUCT) lParam;
+            d = (alerter_wnd_data *) lpcs->lpCreateParams;
+
+#pragma warning(push)
+#pragma warning(disable: 4244)
+            SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) d);
+#pragma warning(pop)
+        }
+        return 0;
+
+    case WM_ERASEBKGND:
+        /* we erase the background when we are drawing the alerts
+           anyway. */
+        return 0;
+
+    case WM_PRINTCLIENT:
+        in_printclient = TRUE;
+        /* fallthrough */
+    case WM_PAINT:
+        {
+            HDC hdc;
+            PAINTSTRUCT ps;
+            RECT r;
+            HFONT hf_old;
+            int y;
+            alerter_wnd_data * d;
+            alerter_alert_data * adata;
+            size_t len;
+            int idx;
+
+            d = (alerter_wnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, GWLP_USERDATA);
+#ifdef DEBUG
+            assert(d);
+#endif
+
+            if (in_printclient) {
+                hdc = (HDC) wParam;
+            } else {
+                hdc = BeginPaint(hwnd, &ps);
+            }
+
+#ifdef DEBUG
+            assert(hdc);
+            assert(d->hfont);
+#endif
+
+#ifdef ALERT_STATIC_BACKGROUND
+            if (in_printclient || ps.fErase) {
+                HBRUSH hb_background;
+
+                hb_background = GetSysColorBrush(COLOR_BTNFACE);
+
+                GetClientRect(hwnd, &r);
+                FillRect(hdc, &r, hb_background);
+            }
+#endif
+
+            SetBkMode(hdc, TRANSPARENT);
+
+            hf_old = SelectFont(hdc, d->hfont);
+
+            y = -d->scroll_top;
+            idx = 0;
+            /* go through the alerts and display them */
+            adata = QTOP(d);
+            while(adata) {
+                khui_alert * a;
+
+#ifndef ALERT_STATIC_BACKGROUND
+#define MIX_C(v1, v2, p) (((int)v1) * p + (((int) v2) * (256 - p)))
+#define ALPHA 50
+                if (in_printclient || ps.fErase) {
+                    TRIVERTEX v[2];
+                    GRADIENT_RECT gr;
+                    COLORREF clr;
+                    COLORREF clr2;
+
+                    CopyRect(&r, &adata->r_alert);
+                    OffsetRect(&r, 0, y);
+
+                    v[0].x = r.left;
+                    v[0].y = r.top;
+                    v[0].Alpha = 0;
+
+                    v[1].x = r.right;
+                    v[1].y = r.bottom;
+                    v[1].Alpha = 0;
+
+                    if (idx == d->c_alert) {
+                        clr = GetSysColor(COLOR_HOTLIGHT);
+
+                        clr2 = GetSysColor(COLOR_BTNHIGHLIGHT);
+                        v[0].Red =   MIX_C(GetRValue(clr), GetRValue(clr2), ALPHA);
+                        v[0].Green = MIX_C(GetGValue(clr), GetGValue(clr2), ALPHA);
+                        v[0].Blue =  MIX_C(GetBValue(clr), GetBValue(clr2), ALPHA);
+
+                        clr2 = GetSysColor(COLOR_BTNFACE);
+                        v[1].Red =   MIX_C(GetRValue(clr), GetRValue(clr2), ALPHA);
+                        v[1].Green = MIX_C(GetGValue(clr), GetGValue(clr2), ALPHA);
+                        v[1].Blue =  MIX_C(GetBValue(clr), GetBValue(clr2), ALPHA);
+                    } else {
+                        clr = GetSysColor(COLOR_BTNHIGHLIGHT);
+                        v[0].Red =   ((int)GetRValue(clr)) << 8;
+                        v[0].Green = ((int)GetGValue(clr)) << 8;
+                        v[0].Blue =  ((int)GetBValue(clr)) << 8;
+
+                        clr = GetSysColor(COLOR_BTNFACE);
+                        v[1].Red =   ((int)GetRValue(clr)) << 8;
+                        v[1].Green = ((int)GetGValue(clr)) << 8;
+                        v[1].Blue =  ((int)GetBValue(clr)) << 8;
+                    }
+
+                    gr.UpperLeft = 0;
+                    gr.LowerRight = 1;
+                    GradientFill(hdc, v, 2, &gr, 1, GRADIENT_FILL_RECT_V);
+                }
+#undef ALPHA
+#undef MIX_C
+#endif
+
+                a = adata->alert;
+#ifdef DEBUG
+                assert(a != NULL);
+#endif
+                khui_alert_lock(a);
+
+                if (!IsRectEmpty(&adata->r_title)) {
+
+                    CopyRect(&r, &adata->r_title);
+                    OffsetRect(&r, 0, y);
+
+                    StringCchLength(a->title, KHUI_MAXCCH_TITLE, &len);
+
+                    DrawEdge(hdc, &r, EDGE_RAISED, BF_RECT | BF_MIDDLE);
+
+                    InflateRect(&r, -d->s_pad.cx, -d->s_pad.cy);
+
+                    DrawText(hdc, a->title, (int) len, &r,
+                             DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
+                }
+
+                {
+                    HICON hicon;
+                    int iid;
+
+                    CopyRect(&r, &adata->r_icon);
+                    OffsetRect(&r, 0, y);
+
+                    if(a->severity == KHERR_ERROR)
+                        iid = OIC_HAND;
+                    else if(a->severity == KHERR_WARNING)
+                        iid = OIC_BANG;
+                    else
+                        iid = OIC_NOTE;
+
+                    hicon = (HICON) LoadImage(NULL, 
+                                              MAKEINTRESOURCE(iid), 
+                                              IMAGE_ICON,
+                                              GetSystemMetrics(SM_CXICON),
+                                              GetSystemMetrics(SM_CYICON),
+                                              LR_SHARED);
+
+                    DrawIcon(hdc, r.left, r.top, hicon);
+                }
+
+                if (a->message) {
+
+                    CopyRect(&r, &adata->r_message);
+                    OffsetRect(&r, 0, y);
+
+                    StringCchLength(a->message, KHUI_MAXCCH_MESSAGE, &len);
+
+                    DrawText(hdc, a->message, (int) len, &r,
+                             DT_WORDBREAK);
+                }
+
+                if (a->suggestion) {
+                    HICON hicon;
+                    SIZE sz;
+
+                    CopyRect(&r, &adata->r_suggestion);
+                    OffsetRect(&r, 0, y);
+
+                    DrawEdge(hdc, &r, EDGE_SUNKEN, BF_RECT | BF_MIDDLE);
+
+                    InflateRect(&r, -d->s_pad.cx, -d->s_pad.cy);
+
+                    sz.cx = GetSystemMetrics(SM_CXSMICON);
+                    sz.cy = GetSystemMetrics(SM_CYSMICON);
+
+                    hicon = (HICON) LoadImage(NULL,
+                                              MAKEINTRESOURCE(OIC_NOTE),
+                                              IMAGE_ICON,
+                                              sz.cx,
+                                              sz.cy,
+                                              LR_SHARED);
+
+                    DrawIconEx(hdc, r.left, r.top, hicon, sz.cx, sz.cy, 0, NULL,
+                               DI_NORMAL);
+
+                    r.left += d->s_pad.cx + GetSystemMetrics(SM_CXSMICON);
+
+                    StringCchLength(a->suggestion, KHUI_MAXCCH_SUGGESTION, &len);
+
+                    DrawText(hdc, a->suggestion, (int) len, &r,
+                             DT_WORDBREAK);
+                }
+                khui_alert_unlock(a);
+
+                y += adata->r_alert.bottom;
+                idx++;
+
+                adata = QNEXT(adata);
+            }
+
+            SelectFont(hdc, hf_old);
+
+            if (!in_printclient) {
+                EndPaint(hwnd, &ps);
+            }
+        }
+        return 0;
+
+    case WM_VSCROLL:
+        {
+            alerter_wnd_data * d;
+            int new_pos = 0;
+            SCROLLINFO si;
+
+            d = (alerter_wnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, GWLP_USERDATA);
+#ifdef DEBUG
+            assert(d);
+#endif
+            if (d == NULL)
+                break;          /* we can't handle the message */
+
+            ZeroMemory(&si, sizeof(si));
+
+            switch(LOWORD(wParam)) {
+            case SB_BOTTOM:
+                new_pos = d->s_alerts.cy  - d->cy_max_wnd;
+                break;
+
+            case SB_LINEDOWN:
+                new_pos = d->scroll_top + SCROLL_LINE_SIZE(d);
+                break;
+
+            case SB_LINEUP:
+                new_pos = d->scroll_top - SCROLL_LINE_SIZE(d);
+                break;
+
+            case SB_PAGEDOWN:
+                new_pos = d->scroll_top + d->cy_max_wnd;
+                break;
+
+            case SB_PAGEUP:
+                new_pos = d->scroll_top - d->cy_max_wnd;
+                break;
+
+            case SB_THUMBPOSITION:
+            case SB_THUMBTRACK:
+                si.fMask = SIF_TRACKPOS;
+                GetScrollInfo(hwnd, SB_VERT, &si);
+                new_pos = si.nTrackPos;
+                break;
+
+            case SB_TOP:
+                new_pos = 0;
+                break;
+
+            case SB_ENDSCROLL:
+                si.fMask = SIF_POS;
+                si.nPos = d->scroll_top;
+                SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
+                return 0;
+
+            default:
+                return 0;
+            }
+
+            scroll_to_position(d, new_pos, FALSE);
+        }
+        return 0;
+
+    case WM_COMMAND:
+        {
+            alerter_wnd_data * d;
+
+            d = (alerter_wnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, GWLP_USERDATA);
+#ifdef DEBUG
+            assert(d);
+#endif
+            if (d == NULL)
+                break;
+
+            if (HIWORD(wParam) == BN_CLICKED) {
+                process_command_button(d, LOWORD(wParam));
+                return 0;
+            } else if (HIWORD(wParam) == BN_SETFOCUS) {
+                ensure_command_is_visible(d, LOWORD(wParam));
+                return 0;
+            }
+        }
+        break;
+
+    case WM_LBUTTONUP:
+        {
+            alerter_wnd_data * d;
+            int x,y;
+
+            d = (alerter_wnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, GWLP_USERDATA);
+#ifdef DEBUG
+            assert(d);
+#endif
+            if (d == NULL)
+                break;
+
+            x = GET_X_LPARAM(lParam);
+            y = GET_Y_LPARAM(lParam);
+
+            handle_mouse_select(d, x, y);
+        }
+        break;
+
+    case WM_SIZE:
+        {
+            InvalidateRect(hwnd, NULL, TRUE);
+        }
+        break;
+
+    case WM_DESTROY:
+        {
+            /* nothing needs to be done here */
+        }
+        return 0;
+    }
+
+    return DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+
+ATOM khm_register_alerter_wnd_class(void)
+{
+    WNDCLASSEX wcx;
+
+    ZeroMemory(&wcx, sizeof(wcx));
+
+    wcx.cbSize = sizeof(wcx);
+    wcx.style =
+        CS_OWNDC |
+#if(_WIN32_WINNT >= 0x0501)
+        ((IS_COMMCTL6())? CS_DROPSHADOW: 0) |
+#endif
+        0;
+    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_BTNFACE + 1);
+    wcx.lpszMenuName = NULL;
+    wcx.lpszClassName = KHUI_ALERTER_CLASS;
+    wcx.hIconSm = NULL;
+
+    atom_alerter = RegisterClassEx(&wcx);
+
+    return atom_alerter;
+}
+
+ATOM khm_register_alert_bin_wnd_class(void)
+{
+    WNDCLASSEX wcx;
+
+    ZeroMemory(&wcx, sizeof(wcx));
+
+    wcx.cbSize = sizeof(wcx);
+    wcx.style = CS_OWNDC;
+
+    wcx.lpfnWndProc = alert_bin_wnd_proc;
+    wcx.cbClsExtra = 0;
+    wcx.cbWndExtra = sizeof(LONG_PTR);
+    wcx.hInstance = khm_hInstance;
+    wcx.hIcon = NULL;
+    wcx.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
+    wcx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
+    wcx.lpszMenuName = NULL;
+    wcx.lpszClassName = KHUI_ALERTBIN_CLASS;
+    wcx.hIconSm = NULL;
+
+    atom_alert_bin = RegisterClassEx(&wcx);
+
+    return atom_alert_bin;
+}
+
+/**********************************************************************
+  Notification Icon
+***********************************************************************/
+
+#define KHUI_NOTIFY_ICON_ID 0
+
+void khm_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(iid_normal));
+    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);
+
+    DestroyIcon(ni.hIcon);
+
+    ni.cbSize = sizeof(ni);
+    ni.uVersion = NOTIFYICON_VERSION;
+    Shell_NotifyIcon(NIM_SETVERSION, &ni);
+}
+
+void 
+khm_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 = iid_normal;
+    }
+
+    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(ELLIPSIS));
+        StringCchCat(ni.szInfo, ARRAYLENGTH(ni.szInfo),
+                     ELLIPSIS);
+    }
+
+    if (FAILED(StringCbCopy(ni.szInfoTitle, sizeof(ni.szInfoTitle), 
+                            title))) {
+        StringCchCopyN(ni.szInfoTitle, ARRAYLENGTH(ni.szInfoTitle),
+                       title, 
+                       ARRAYLENGTH(ni.szInfoTitle) - ARRAYLENGTH(ELLIPSIS));
+        StringCchCat(ni.szInfoTitle, ARRAYLENGTH(ni.szInfoTitle),
+                     ELLIPSIS);
+    }
+
+    ni.uTimeout = timeout;
+
+    Shell_NotifyIcon(NIM_MODIFY, &ni);
+
+    DestroyIcon(ni.hIcon);
+}
+
+void khm_notify_icon_expstate(enum khm_notif_expstate expseverity) {
+    int new_iid;
+
+    if (expseverity == KHM_NOTIF_OK)
+        new_iid = IDI_APPICON_OK;
+    else if (expseverity == KHM_NOTIF_WARN)
+        new_iid = IDI_APPICON_WARN;
+    else if (expseverity == KHM_NOTIF_EXP)
+        new_iid = IDI_APPICON_EXP;
+    else
+        new_iid = IDI_NOTIFY_NONE;
+
+    if (iid_normal == new_iid)
+        return;
+
+    iid_normal = new_iid;
+
+    if (balloon_alert == NULL)
+        khm_notify_icon_change(KHERR_NONE);
+}
+
+void khm_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 = iid_normal;
+
+    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 khm_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);
+}
+
+static khm_int32
+get_default_notifier_action(void) {
+    khm_int32 def_cmd = KHUI_ACTION_OPEN_APP;
+    khm_handle csp_cw = NULL;
+    khm_size i;
+
+    if (KHM_FAILED(khc_open_space(NULL, L"CredWindow", KHM_PERM_READ,
+                                  &csp_cw)))
+        def_cmd;
+
+    khc_read_int32(csp_cw, L"NotificationAction", &def_cmd);
+
+    khc_close_space(csp_cw);
+
+    for (i=0; i < n_khm_notifier_actions; i++) {
+        if (khm_notifier_actions[i] == def_cmd)
+            break;
+    }
+
+    if (i < n_khm_notifier_actions)
+        return def_cmd;
+    else
+        return KHUI_ACTION_OPEN_APP;
+}
+
+void khm_notify_icon_activate(void) {
+    /* if there are any notifications waiting to be shown and there
+       are no alerts already being shown, we show them.  Otherwise we
+       execute the default action. */
+
+    khm_notify_icon_change(KHERR_NONE);
+
+    if (balloon_alert != NULL && khui_alert_windows == NULL) {
+
+        khui_alert * a;
+        khm_boolean alert_done = FALSE;
+
+        a = balloon_alert;
+        balloon_alert = NULL;
+
+        khui_alert_lock(a);
+
+        a->displayed = FALSE;
+
+        if ((a->flags & KHUI_ALERT_FLAG_DEFACTION) &&
+            (a->n_alert_commands > 0)) {
+
+            PostMessage(khm_hwnd_main, WM_COMMAND,
+                        MAKEWPARAM(a->alert_commands[0], 
+                                   0),
+                        0);
+            alert_done = TRUE;
+
+        } else if (a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW) {
+
+            alert_show_normal(a);
+            alert_done = TRUE;
+
+        }
+        khui_alert_unlock(a);
+        khui_alert_release(a);
+
+        if (alert_done)
+            return;
+    }
+
+    if (!is_alert_queue_empty() && !ALERT_DISPLAYED()) {
+
+        khm_show_main_window();
+        show_queued_alerts();
+
+        return;
+    }
+
+
+    /* if none of the above applied, then we perform the default
+       action for the notification icon. */
+    {
+        khm_int32 cmd = 0;
+
+        cmd = get_default_notifier_action();
+
+        if (cmd == KHUI_ACTION_OPEN_APP) {
+            if (khm_is_main_window_visible()) {
+                khm_hide_main_window();
+            } else {
+                khm_show_main_window();
+            }
+        } else {
+            khui_action_trigger(cmd, NULL);
+        }
+
+        check_for_queued_alerts();
+    }
+}
+
+/*********************************************************************
+  Initialization
+**********************************************************************/
+
+void khm_init_notifier(void)
+{
+    if(!khm_register_notifier_wnd_class())
+        return;
+
+    if(!khm_register_alerter_wnd_class())
+        return;
+
+    if(!khm_register_alert_bin_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;
+
+        khm_notify_icon_add();
+    } else {
+#ifdef DEBUG
+        assert(hwnd_notifier != NULL);
+#endif
+    }
+    khm_timer_init();
+
+    khm_addr_change_notifier_init();
+}
+
+void khm_exit_notifier(void)
+{
+    khm_addr_change_notifier_exit();
+
+    khm_timer_exit();
+
+    if(hwnd_notifier != NULL) {
+        khm_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;
+    }
+
+    if(atom_alert_bin != 0) {
+        UnregisterClass(MAKEINTATOM(atom_alert_bin), khm_hInstance);
+        atom_alert_bin = 0;
+    }
+
+    notifier_ready = FALSE;
+}
+
+/***** testing *****/
+
+void
+create_test_alerts(void) {
+
+    khui_alert * a;
+    int i;
+
+    for (i=0; i < 50; i++) {
+        wchar_t buf[128];
+
+        StringCbPrintf(buf, sizeof(buf), L"Foo bar baz.  This is alert number %d", i);
+        khui_alert_create_simple(L"Title", buf, KHERR_INFO, &a);
+        khui_alert_set_type(a, KHUI_ALERTTYPE_PLUGIN);
+        khui_alert_set_suggestion(a, L"This is a suggestion.  It is kinda long to see if the word wrapping actually works as we expect it to.  Just in case, here's a line feed.\n\nDoes this show up on a different line? Cool!");
+
+        khui_alert_add_command(a, KHUI_ACTION_NEW_CRED);
+        khui_alert_add_command(a, KHUI_ACTION_CLOSE_APP);
+        khui_alert_add_command(a, KHUI_ACTION_PROPERTIES);
+        khui_alert_add_command(a, KHUI_ACTION_OPEN_APP);
+        khui_alert_add_command(a, KHUI_ACTION_VIEW_REFRESH);
+
+        khui_alert_show(a);
+        khui_alert_release(a);
+    }
+}
index a42e63f1fe2e0e95415d118ca40538295aa871bd..b0cd5dc2c87e2fd8d97ac3b1a782ab1e1fd9f9d2 100644 (file)
@@ -1,63 +1,63 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_NOTIFIER_H\r
-#define __KHIMAIRA_NOTIFIER_H\r
-\r
-extern HWND hwnd_notifier;\r
-\r
-enum khm_notif_expstate {\r
-    KHM_NOTIF_EMPTY,\r
-    KHM_NOTIF_OK,\r
-    KHM_NOTIF_WARN,\r
-    KHM_NOTIF_EXP\r
-};\r
-\r
-extern khm_int32 khm_notifier_actions[];\r
-extern khm_size  n_khm_notifier_actions;\r
-\r
-void \r
-khm_init_notifier(void);\r
-\r
-void \r
-khm_exit_notifier(void);\r
-\r
-void \r
-khm_notify_icon_change(khm_int32 severity);\r
-\r
-void \r
-khm_notify_icon_balloon(khm_int32 severity,\r
-                         wchar_t * title,\r
-                         wchar_t * msg,\r
-                         khm_int32 timeout);\r
-\r
-void\r
-khm_notify_icon_expstate(enum khm_notif_expstate expseverity);\r
-\r
-void\r
-khm_notify_icon_activate(void);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
+
+extern HWND hwnd_notifier;
+
+enum khm_notif_expstate {
+    KHM_NOTIF_EMPTY,
+    KHM_NOTIF_OK,
+    KHM_NOTIF_WARN,
+    KHM_NOTIF_EXP
+};
+
+extern khm_int32 khm_notifier_actions[];
+extern khm_size  n_khm_notifier_actions;
+
+void 
+khm_init_notifier(void);
+
+void 
+khm_exit_notifier(void);
+
+void 
+khm_notify_icon_change(khm_int32 severity);
+
+void 
+khm_notify_icon_balloon(khm_int32 severity,
+                         wchar_t * title,
+                         wchar_t * msg,
+                         khm_int32 timeout);
+
+void
+khm_notify_icon_expstate(enum khm_notif_expstate expseverity);
+
+void
+khm_notify_icon_activate(void);
+
+#endif
index 4084ede413d560dcb9ea7de384fc80f3990d38d4..65cc06fe7677315001fb7ae9171f2f6073edee2e 100644 (file)
-#include<khmapp.h>\r
-\r
-static ATOM sAtom = 0;\r
-static HINSTANCE shInstance = 0;\r
-\r
-/* Callback for the MITPasswordControl\r
-This is a replacement for the normal edit control.  It does not show the \r
-annoying password char in the edit box so that the number of chars in the \r
-password are not known.\r
-*/\r
-\r
-#define PASSWORDCHAR L'#'\r
-#define DLGHT(ht) (HIWORD(GetDialogBaseUnits())*(ht)/8)\r
-#define DLGWD(wd) (LOWORD(GetDialogBaseUnits())*(wd)/4)\r
-\r
-static\r
-LRESULT\r
-CALLBACK\r
-MITPasswordEditProc(\r
-    HWND hWnd,\r
-    UINT message,\r
-    WPARAM wParam,\r
-    LPARAM lParam\r
-    )\r
-{\r
-    static SIZE pwdcharsz;\r
-    BOOL pass_the_buck = FALSE;\r
-  \r
-    if (message > WM_USER && message < 0x7FFF)\r
-        pass_the_buck = TRUE;\r
-  \r
-    switch(message)\r
-    {\r
-    case WM_GETTEXT:\r
-    case WM_GETTEXTLENGTH:\r
-    case WM_SETTEXT:\r
-        pass_the_buck = TRUE;\r
-        break;\r
-    case WM_PAINT:\r
-    {\r
-        HDC hdc;\r
-        PAINTSTRUCT ps;\r
-        RECT r;\r
-        \r
-        hdc = BeginPaint(hWnd, &ps);\r
-        GetClientRect(hWnd, &r);\r
-        Rectangle(hdc, 0, 0, r.right, r.bottom);\r
-        EndPaint(hWnd, &ps);\r
-    }\r
-    break;\r
-    case WM_SIZE:\r
-    {\r
-        MoveWindow(GetDlgItem(hWnd, 1), DLGWD(2), DLGHT(2),\r
-                  pwdcharsz.cx / 2, pwdcharsz.cy, TRUE);\r
-    }\r
-    break;\r
-    case WM_LBUTTONDOWN:\r
-    case WM_SETFOCUS:\r
-    {\r
-        SetFocus(GetDlgItem(hWnd, 1));\r
-    }\r
-    break;\r
-    case WM_CREATE:\r
-    {\r
-        HWND heditchild;\r
-        wchar_t pwdchar = PASSWORDCHAR;\r
-        HDC hdc;\r
-        /* Create a child window of this control for default processing. */\r
-        hdc = GetDC(hWnd);\r
-        GetTextExtentPoint32(hdc, &pwdchar, 1, &pwdcharsz);\r
-        ReleaseDC(hWnd, hdc);\r
-        \r
-        heditchild =\r
-            CreateWindow(L"edit", L"", WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL |\r
-                         ES_LEFT | ES_PASSWORD | WS_TABSTOP,\r
-                         0, 0, 0, 0,\r
-                         hWnd,\r
-                         (HMENU)1,\r
-                         ((LPCREATESTRUCT)lParam)->hInstance,\r
-                         NULL);\r
-        SendMessage(heditchild, EM_SETPASSWORDCHAR, PASSWORDCHAR, 0L);\r
-    }\r
-    break;\r
-    }\r
-  \r
-    if (pass_the_buck)\r
-        return SendMessage(GetDlgItem(hWnd, 1), message, wParam, lParam);\r
-    return DefWindowProc(hWnd, message, wParam, lParam);\r
-}\r
-\r
-khm_int32\r
-khm_register_passwnd_class(void)\r
-{\r
-    if (!sAtom) {\r
-        WNDCLASS wndclass;\r
-\r
-        memset(&wndclass, 0, sizeof(WNDCLASS));\r
-\r
-        shInstance = khm_hInstance;\r
-\r
-        wndclass.style = CS_HREDRAW | CS_VREDRAW;\r
-        wndclass.lpfnWndProc = (WNDPROC)MITPasswordEditProc;\r
-        wndclass.cbClsExtra = sizeof(HWND);\r
-        wndclass.cbWndExtra = 0;\r
-        wndclass.hInstance = shInstance;\r
-        wndclass.hbrBackground = (void *)(COLOR_WINDOW + 1);\r
-        wndclass.lpszClassName = MIT_PWD_DLL_CLASS;\r
-        wndclass.hCursor = LoadCursor((HINSTANCE)NULL, IDC_IBEAM);\r
-    \r
-        sAtom = RegisterClass(&wndclass);\r
-    }\r
-\r
-    return (sAtom)?KHM_ERROR_SUCCESS:KHM_ERROR_UNKNOWN;\r
-}\r
-\r
-khm_int32\r
-khm_unregister_passwnd_class(void)\r
-{\r
-    BOOL result = TRUE;\r
-\r
-    if ((khm_hInstance != shInstance) || !sAtom) {\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-    }\r
-\r
-    result = UnregisterClass(MIT_PWD_DLL_CLASS, khm_hInstance);\r
-    if (result) {\r
-      sAtom = 0;\r
-      shInstance = 0;\r
-      return KHM_ERROR_SUCCESS;\r
-    } else {\r
-      return KHM_ERROR_UNKNOWN;\r
-    }\r
-}\r
+#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;
+    }
+}
index b0adcf68934dbbb48d7f585f302fb339c07ea34d..c3ab8ef31461a8ac558211f89f529add1f9b528c 100644 (file)
@@ -1,39 +1,39 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_PASSWND_H\r
-#define __KHIMAIRA_PASSWND_H\r
-\r
-/* Declarations for the MIT password change control.  Functionally the\r
-   same as the regular Windows password edit control but doesn't\r
-   display the '*' password character. */\r
-\r
-#define MIT_PWD_DLL_CLASS L"MITPasswordWnd"\r
-\r
-khm_int32 khm_unregister_passwnd_class(void);\r
-khm_int32 khm_register_passwnd_class(void);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
index 4061cd78d25b7f84414c5d2ea87d9f742c088c62..4255b481be33fcf924728e1d4283a9ca5461c209 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khmapp.h>\r
-#include<assert.h>\r
-\r
-typedef struct tag_pw_data {\r
-    khm_handle record;\r
-    HWND    hwnd_lv;\r
-} pw_data;\r
-\r
-ATOM khui_propertywnd_cls;\r
-\r
-#define ID_LISTVIEW 1\r
-\r
-#define PW_WM_SET_RECORD WM_USER\r
-\r
-void pw_update_property_data(HWND hw, pw_data * d)\r
-{\r
-    HWND hwnd_lv;\r
-    khm_int32 * attrs = NULL;\r
-\r
-    hwnd_lv = d->hwnd_lv;\r
-\r
-    if(hwnd_lv == NULL)\r
-        return;\r
-\r
-    ListView_DeleteAllItems(hwnd_lv);\r
-\r
-    if(d->record != NULL) {\r
-        wchar_t * buffer;\r
-        khm_size attr_count;\r
-        khm_size i;\r
-        khm_size cb_buf;\r
-        khm_size t;\r
-        LVITEM lvi;\r
-        int idx;\r
-\r
-        if(KHM_FAILED(kcdb_attrib_get_count(\r
-            KCDB_ATTR_FLAG_VOLATILE |\r
-            KCDB_ATTR_FLAG_HIDDEN,\r
-            0,\r
-            &attr_count)))\r
-            return;\r
-\r
-        attrs = PMALLOC(sizeof(khm_int32) * attr_count);\r
-        assert(attrs != NULL);\r
-\r
-        kcdb_attrib_get_ids(\r
-            KCDB_ATTR_FLAG_VOLATILE |\r
-            KCDB_ATTR_FLAG_HIDDEN,\r
-            0,\r
-            attrs,\r
-            &attr_count);\r
-\r
-        cb_buf = sizeof(wchar_t) * 2048;\r
-        buffer = PMALLOC(cb_buf);\r
-        assert(buffer != NULL);\r
-\r
-        for(i=0; i<attr_count; i++) {\r
-            if(KHM_FAILED(kcdb_buf_get_attr(d->record, attrs[i], NULL, NULL, NULL)))\r
-                continue;\r
-\r
-            ZeroMemory(&lvi, sizeof(lvi));\r
-            lvi.mask = LVIF_TEXT | LVIF_PARAM;\r
-            lvi.iItem = (int) i;\r
-            lvi.iSubItem = 0;\r
-            lvi.pszText = buffer;\r
-            lvi.lParam = (LPARAM) attrs[i];\r
-\r
-            t = cb_buf;\r
-            kcdb_attrib_describe(attrs[i], buffer, &t, KCDB_TS_SHORT);\r
-\r
-            idx = ListView_InsertItem(hwnd_lv, &lvi);\r
-\r
-            ZeroMemory(&lvi, sizeof(lvi));\r
-            lvi.mask = LVIF_TEXT;\r
-            lvi.iItem = idx;\r
-            lvi.iSubItem = 1;\r
-            lvi.pszText = buffer;\r
-\r
-            t = cb_buf;\r
-            kcdb_buf_get_attr_string(d->record, attrs[i], buffer, &t, 0);\r
-\r
-            ListView_SetItem(hwnd_lv, &lvi);\r
-        }\r
-\r
-        PFREE(attrs);\r
-        PFREE(buffer);\r
-    }\r
-}\r
-\r
-LRESULT CALLBACK khui_property_wnd_proc(\r
-    HWND hwnd,\r
-    UINT msg,\r
-    WPARAM wParam,\r
-    LPARAM lParam)\r
-{\r
-    BOOL child_msg = FALSE;\r
-    pw_data * child;\r
-\r
-    switch(msg) {\r
-        case WM_CREATE: \r
-            {\r
-                CREATESTRUCT * cs;\r
-                LVCOLUMN lvc;\r
-                wchar_t sz_title[256];\r
-\r
-                cs = (CREATESTRUCT *) lParam;\r
-\r
-                child = PMALLOC(sizeof(*child));\r
-                ZeroMemory(child, sizeof(*child));\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable:4244)\r
-                SetWindowLongPtr(hwnd, 0, (LONG_PTR) child);\r
-#pragma warning(pop)\r
-\r
-                child->hwnd_lv = CreateWindow(\r
-                    WC_LISTVIEW, \r
-                    L"",\r
-                    WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |\r
-                    LVS_REPORT | LVS_SORTASCENDING,\r
-                    0, 0,\r
-                    cs->cx, cs->cy,\r
-                    hwnd, \r
-                    (HMENU) ID_LISTVIEW, \r
-                    khm_hInstance, \r
-                    NULL);\r
-\r
-                ListView_SetExtendedListViewStyle(child->hwnd_lv, \r
-                    LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);\r
-\r
-                ZeroMemory(&lvc, sizeof(lvc));\r
-                lvc.mask = LVCF_FMT | LVCF_ORDER | LVCF_TEXT | LVCF_WIDTH;\r
-                lvc.fmt = LVCFMT_LEFT;\r
-                lvc.cx = (cs->cx * 2)/ 5;\r
-                lvc.pszText = sz_title;\r
-                lvc.iSubItem = 0;\r
-                lvc.iOrder = 0;\r
-                LoadString(khm_hInstance, IDS_PROP_COL_PROPERTY, sz_title, ARRAYLENGTH(sz_title));\r
-\r
-                ListView_InsertColumn(child->hwnd_lv, 0, &lvc);\r
-\r
-                ZeroMemory(&lvc, sizeof(lvc));\r
-                lvc.mask = LVCF_FMT | LVCF_ORDER | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH;\r
-                lvc.fmt = LVCFMT_LEFT;\r
-                lvc.cx = (cs->cx * 3)/ 5;\r
-                lvc.pszText = sz_title;\r
-                lvc.iSubItem = 1;\r
-                lvc.iOrder = 1;\r
-                LoadString(khm_hInstance, IDS_PROP_COL_VALUE, sz_title, ARRAYLENGTH(sz_title));\r
-\r
-                ListView_InsertColumn(child->hwnd_lv, 1, &lvc);\r
-\r
-                if(cs->lpCreateParams != NULL) {\r
-                    child->record = cs->lpCreateParams;\r
-                    kcdb_buf_hold(child->record);\r
-                    pw_update_property_data(hwnd, child);\r
-                }\r
-            }\r
-            break;\r
-\r
-        case PW_WM_SET_RECORD:\r
-            {\r
-                child = (pw_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-                kcdb_buf_release(child->record);\r
-                child->record = (khm_handle) lParam;\r
-                kcdb_buf_hold(child->record);\r
-                pw_update_property_data(hwnd, child);\r
-            }\r
-            return 0;\r
-\r
-        case WM_DESTROY:\r
-            {\r
-                child = (pw_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-                kcdb_buf_release(child->record);\r
-                PFREE(child);\r
-            }\r
-            break;\r
-\r
-        case WM_PAINT:\r
-            break;\r
-\r
-        default:\r
-            child = (pw_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0);\r
-            child_msg = TRUE;\r
-    }\r
-\r
-    /*\r
-    if(child_msg && child && child->hwnd_lv)\r
-        return SendMessage(child->hwnd_lv, msg, wParam, lParam);\r
-    else\r
-    */\r
-        return DefWindowProc(hwnd, msg, wParam, lParam);\r
-}\r
-\r
-khm_int32 khm_register_propertywnd_class(void)\r
-{\r
-    WNDCLASSEX wcx;\r
-\r
-    wcx.cbSize = sizeof(wcx);\r
-    wcx.style = CS_DBLCLKS;\r
-    wcx.lpfnWndProc = khui_property_wnd_proc;\r
-    wcx.cbClsExtra = 0;\r
-    wcx.cbWndExtra = sizeof(LONG_PTR);\r
-    wcx.hInstance = khm_hInstance;\r
-    wcx.hIcon = NULL;\r
-    wcx.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);\r
-    wcx.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);\r
-    wcx.lpszMenuName = NULL;\r
-    wcx.lpszClassName = KHUI_PROPERTYWND_CLASS_NAME;\r
-    wcx.hIconSm = NULL;\r
-\r
-    khui_propertywnd_cls = RegisterClassEx(&wcx);\r
-\r
-    return (khui_propertywnd_cls == 0)?KHM_ERROR_UNKNOWN:KHM_ERROR_SUCCESS;\r
-}\r
-\r
-khm_int32 khm_unregister_propertywnd_class(void)\r
-{\r
-    UnregisterClass(MAKEINTATOM(khui_propertywnd_cls), khm_hInstance);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 = PMALLOC(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 = PMALLOC(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);
+        }
+
+        PFREE(attrs);
+        PFREE(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 = PMALLOC(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);
+                PFREE(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_BTNFACE + 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;
+}
index 89305dd7ba7f4ed263282194db07be2fef15705c..c4a738eafcac091755138f56b5c74e3350a713b2 100644 (file)
@@ -1,36 +1,36 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_PROPERTYWND_H\r
-#define __KHIMAIRA_PROPERTYWND_H\r
-\r
-#define KHUI_PROPERTYWND_CLASS_NAME L"NetIDMgrPropertyWnd"\r
-\r
-khm_int32 khm_register_propertywnd_class(void);\r
-\r
-khm_int32 khm_unregister_propertywnd_class(void);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
index e72e3e22b81c9c8831927707b4cc0903284cc6c3..20b126e2753cb0c30e209568761764c976706f3e 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khmapp.h>\r
-#include<assert.h>\r
-\r
-ATOM reqdaemon_atom = 0;\r
-HANDLE reqdaemon_thread = NULL;\r
-HWND reqdaemon_hwnd = NULL;\r
-\r
-LRESULT CALLBACK\r
-reqdaemonwnd_proc(HWND hwnd,\r
-                  UINT uMsg,\r
-                  WPARAM wParam,\r
-                  LPARAM lParam) {\r
-\r
-    switch(uMsg) {\r
-    case WM_CREATE:\r
-        break;\r
-\r
-    case WM_CLOSE:\r
-        DestroyWindow(hwnd);\r
-        break;\r
-\r
-    case WM_DESTROY:\r
-        reqdaemon_hwnd = NULL;\r
-        PostQuitMessage(0);\r
-        break;\r
-\r
-        /* Leash compatibility */\r
-    case ID_OBTAIN_TGT_WITH_LPARAM:\r
-        {\r
-            wchar_t widname[KCDB_IDENT_MAXCCH_NAME];\r
-            wchar_t wmapping[ARRAYLENGTH(KHUI_REQD_MAPPING_FORMAT) + 10];\r
-            khm_handle identity = NULL;\r
-            LPNETID_DLGINFO pdlginfo;\r
-            LRESULT lr = 1;\r
-            khm_int32 result;\r
-            HANDLE hmap = NULL;\r
-            HRESULT hr;\r
-\r
-            hr = StringCbPrintf(wmapping, sizeof(wmapping),\r
-                                KHUI_REQD_MAPPING_FORMAT, (DWORD) lParam);\r
-#ifdef DEBUG\r
-            assert(SUCCEEDED(hr));\r
-#endif\r
-            hmap = CreateFileMapping(INVALID_HANDLE_VALUE,\r
-                                     NULL,\r
-                                     PAGE_READWRITE,\r
-                                     0, 4096,\r
-                                     wmapping);\r
-\r
-            if (hmap == NULL) {\r
-                return -1;\r
-            } else if (hmap != NULL && GetLastError() != ERROR_ALREADY_EXISTS) {\r
-                CloseHandle(hmap);\r
-                return -1;\r
-            }\r
-\r
-            pdlginfo = MapViewOfFile(hmap,\r
-                                     FILE_MAP_WRITE,\r
-                                     0, 0,\r
-                                     sizeof(*pdlginfo));\r
-\r
-            if (pdlginfo == NULL) {\r
-                CloseHandle(hmap);\r
-                return 1;\r
-            }\r
-\r
-            if (pdlginfo->in.username[0] &&\r
-                pdlginfo->in.realm[0] &&\r
-                SUCCEEDED(StringCbPrintf(widname,\r
-                                         sizeof(widname),\r
-                                         L"%s@%s",\r
-                                         pdlginfo->in.username,\r
-                                         pdlginfo->in.realm))) {\r
-\r
-                kcdb_identity_create(widname,\r
-                                     KCDB_IDENT_FLAG_CREATE,\r
-                                     &identity);\r
-            }\r
-\r
-            widname[0] = 0;\r
-\r
-            do {\r
-                if (khm_cred_is_in_dialog()) {\r
-                    khm_cred_wait_for_dialog(INFINITE, NULL, NULL, 0);\r
-                }\r
-\r
-                if (identity)\r
-                    khui_context_set_ex(KHUI_SCOPE_IDENT,\r
-                                        identity,\r
-                                        KCDB_CREDTYPE_INVALID,\r
-                                        NULL,\r
-                                        NULL,\r
-                                        0,\r
-                                        NULL,\r
-                                        pdlginfo,\r
-                                        sizeof(*pdlginfo));\r
-                else\r
-                    khui_context_reset();\r
-\r
-                if (pdlginfo->dlgtype == NETID_DLGTYPE_TGT)\r
-                    SendMessage(khm_hwnd_main, WM_COMMAND,\r
-                                MAKEWPARAM(KHUI_ACTION_NEW_CRED, 0), 0);\r
-                else if (pdlginfo->dlgtype == NETID_DLGTYPE_CHPASSWD)\r
-                    SendMessage(khm_hwnd_main, WM_COMMAND,\r
-                                MAKEWPARAM(KHUI_ACTION_PASSWD_ID, 0), 0);\r
-                else\r
-                    break;\r
-\r
-                if (KHM_FAILED(khm_cred_wait_for_dialog(INFINITE, &result,\r
-                                                        widname,\r
-                                                        sizeof(widname))))\r
-                    continue;\r
-                else {\r
-                    lr = (result != KHUI_NC_RESULT_PROCESS);\r
-                    break;\r
-                }\r
-            } while(TRUE);\r
-\r
-#ifdef DEBUG\r
-            assert(lr || pdlginfo->dlgtype != NETID_DLGTYPE_TGT ||\r
-                   widname[0]);\r
-#endif\r
-\r
-            if (!lr && pdlginfo->dlgtype == NETID_DLGTYPE_TGT &&\r
-                widname[0]) {\r
-                khm_handle out_ident;\r
-                wchar_t * atsign;\r
-\r
-                atsign = wcsrchr(widname, L'@');\r
-\r
-                if (atsign == NULL)\r
-                    goto _exit;\r
-\r
-                if (KHM_SUCCEEDED(kcdb_identity_create(widname,\r
-                                                       0,\r
-                                                       &out_ident))) {\r
-                    khm_size cb;\r
-\r
-                    pdlginfo->out.ccache[0] = 0;\r
-\r
-                    cb = sizeof(pdlginfo->out.ccache);\r
-                    kcdb_identity_get_attrib(out_ident,\r
-                                             L"Krb5CCName",\r
-                                             NULL,\r
-                                             pdlginfo->out.ccache,\r
-                                             &cb);\r
-                    kcdb_identity_release(out_ident);\r
-                } else {\r
-#ifdef DEBUG\r
-                    assert(FALSE);\r
-#endif\r
-                }\r
-\r
-                *atsign++ = 0;\r
-\r
-                StringCbCopy(pdlginfo->out.username,\r
-                             sizeof(pdlginfo->out.username),\r
-                             widname);\r
-\r
-                StringCbCopy(pdlginfo->out.realm,\r
-                             sizeof(pdlginfo->out.realm),\r
-                             atsign);\r
-            }\r
-\r
-        _exit:\r
-\r
-            if (pdlginfo)\r
-                UnmapViewOfFile(pdlginfo);\r
-            if (hmap)\r
-                CloseHandle(hmap);\r
-            if (identity)\r
-                kcdb_identity_release(identity);\r
-\r
-            return lr;\r
-        }\r
-\r
-#ifdef DEPRECATED_REMOTE_CALL\r
-        /* deprecated */\r
-    case ID_OBTAIN_TGT_WITH_LPARAM:\r
-        {\r
-            char * param = (char *) GlobalLock((HGLOBAL) lParam);\r
-            char * username = NULL;\r
-            char * realm = NULL;\r
-            char * title = NULL;\r
-            char * ccache = NULL;\r
-            wchar_t widname[KCDB_IDENT_MAXCCH_NAME];\r
-            wchar_t wtitle[KHUI_MAXCCH_TITLE];\r
-            size_t cch;\r
-            khm_int32 rv = KHM_ERROR_SUCCESS;\r
-            khm_handle identity = NULL;\r
-            NETID_DLGINFO dlginfo;\r
-\r
-            if (param) {\r
-                if (*param)\r
-                    title = param;\r
-\r
-                if (FAILED(StringCchLengthA(param, KHUI_MAXCCH_TITLE, &cch))) {\r
-#ifdef DEBUG\r
-                    assert(FALSE);\r
-#endif\r
-                    rv = KHM_ERROR_INVALID_PARAM;\r
-                    goto _exit_tgt_with_lparam;\r
-                }\r
-\r
-                param += cch + 1;\r
-\r
-                if (*param)\r
-                    username = param;\r
-\r
-                if (FAILED(StringCchLengthA(param, KCDB_IDENT_MAXCCH_NAME, &cch))) {\r
-#ifdef DEBUG\r
-                    assert(FALSE);\r
-#endif\r
-                    rv = KHM_ERROR_INVALID_PARAM;\r
-                    goto _exit_tgt_with_lparam;\r
-                }\r
-\r
-                param += cch + 1;\r
-\r
-                if (*param)\r
-                    realm = param;\r
-\r
-                if (FAILED(StringCchLengthA(param, KCDB_IDENT_MAXCCH_NAME, &cch))) {\r
-#ifdef DEBUG\r
-                    assert(FALSE);\r
-#endif\r
-                    rv = KHM_ERROR_INVALID_PARAM;\r
-                    goto _exit_tgt_with_lparam;\r
-                }\r
-\r
-                param += cch + 1;\r
-\r
-                if (*param)\r
-                    ccache = param;\r
-            }\r
-\r
-            if (username && realm) {\r
-\r
-                if (FAILED(StringCbPrintf(widname, sizeof(widname),\r
-                                          L"%hs@%hs", username, realm))) {\r
-                    rv = KHM_ERROR_INVALID_PARAM;\r
-                    goto _exit_tgt_with_lparam;\r
-                }\r
-\r
-                rv = kcdb_identity_create(widname,\r
-                                          KCDB_IDENT_FLAG_CREATE,\r
-                                          &identity);\r
-                if (KHM_FAILED(rv)) {\r
-                    goto _exit_tgt_with_lparam;\r
-                }\r
-            }\r
-\r
-            ZeroMemory(&dlginfo, sizeof(dlginfo));\r
-\r
-            dlginfo.size = NETID_DLGINFO_V1_SZ;\r
-            dlginfo.dlgtype = NETID_DLGTYPE_TGT;\r
-            \r
-            if (title)\r
-                StringCbCopy(dlginfo.in.title, sizeof(dlginfo.in.title),\r
-                             wtitle);\r
-            if (username)\r
-                AnsiStrToUnicode(dlginfo.in.username, sizeof(dlginfo.in.username),\r
-                                 username);\r
-            if (realm)\r
-                AnsiStrToUnicode(dlginfo.in.realm, sizeof(dlginfo.in.realm),\r
-                                 realm);\r
-\r
-            if (ccache)\r
-                AnsiStrToUnicode(dlginfo.in.ccache, sizeof(dlginfo.in.ccache),\r
-                                 ccache);\r
-\r
-            dlginfo.in.use_defaults = TRUE;\r
-\r
-            do {\r
-                if (khm_cred_is_in_dialog()) {\r
-                    khm_cred_wait_for_dialog(INFINITE);\r
-                }\r
-\r
-                khui_context_set_ex(KHUI_SCOPE_IDENT,\r
-                                    identity,\r
-                                    KCDB_CREDTYPE_INVALID,\r
-                                    NULL,\r
-                                    NULL,\r
-                                    0,\r
-                                    NULL,\r
-                                    &dlginfo,\r
-                                    sizeof(dlginfo));\r
-\r
-                if (title) {\r
-                    AnsiStrToUnicode(wtitle, sizeof(wtitle),\r
-                                     title);\r
-\r
-                    khm_cred_obtain_new_creds(wtitle);\r
-                } else {\r
-                    khm_cred_obtain_new_creds(NULL);\r
-                }\r
-\r
-                if (KHM_FAILED(khm_cred_wait_for_dialog(INFINITE)))\r
-                    continue;\r
-                else\r
-                    break;\r
-            } while(TRUE);\r
-\r
-        _exit_tgt_with_lparam:\r
-            if (identity)\r
-                kcdb_identity_release(identity);\r
-\r
-            GlobalUnlock((HGLOBAL) lParam);\r
-        }\r
-        return 0;\r
-#endif\r
-\r
-    }\r
-\r
-    return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
-}\r
-\r
-DWORD WINAPI\r
-khm_reqdaemon_thread_proc(LPVOID vparam) {\r
-    BOOL rv;\r
-    MSG msg;\r
-#ifdef DEBUG\r
-    DWORD dw;\r
-#endif\r
-\r
-    PDESCTHREAD(L"Remote Request Daemon", L"App");\r
-\r
-    khm_register_reqdaemonwnd_class();\r
-\r
-#ifdef DEBUG\r
-    assert(reqdaemon_atom != 0);\r
-#endif\r
-\r
-    reqdaemon_hwnd = CreateWindowEx(0,\r
-                                    MAKEINTATOM(reqdaemon_atom),\r
-                                    KHUI_REQDAEMONWND_NAME,\r
-                                    0,\r
-                                    0,0,0,0,\r
-                                    HWND_MESSAGE,\r
-                                    NULL,\r
-                                    khm_hInstance,\r
-                                    NULL);\r
-\r
-#ifdef DEBUG\r
-    dw = GetLastError();\r
-    assert(reqdaemon_hwnd != NULL);\r
-#endif\r
-\r
-    while(rv = GetMessage(&msg, NULL, 0, 0)) {\r
-        if (rv == -1) {\r
-#ifdef DEBUG\r
-            assert(FALSE);\r
-#endif\r
-            break;\r
-        } else {\r
-            TranslateMessage(&msg);\r
-            DispatchMessage(&msg);\r
-        }\r
-    }\r
-\r
-    reqdaemon_thread = NULL;\r
-\r
-    khm_unregister_reqdaemonwnd_class();\r
-\r
-    return 0;\r
-}\r
-\r
-void\r
-khm_register_reqdaemonwnd_class(void) {\r
-    WNDCLASSEX wcx;\r
-\r
-    ZeroMemory(&wcx, sizeof(wcx));\r
-\r
-    wcx.cbSize = sizeof(wcx);\r
-    wcx.style = 0;\r
-    wcx.lpfnWndProc = reqdaemonwnd_proc;\r
-    wcx.cbClsExtra = 0;\r
-    wcx.cbWndExtra = 0;\r
-    wcx.hInstance = khm_hInstance;\r
-    wcx.hIcon = NULL;\r
-    wcx.hCursor = NULL;\r
-    wcx.hbrBackground = NULL;\r
-    wcx.lpszMenuName = NULL;\r
-    wcx.lpszClassName = KHUI_REQDAEMONWND_CLASS;\r
-    wcx.hIconSm = NULL;\r
-\r
-    reqdaemon_atom = RegisterClassEx(&wcx);\r
-\r
-#ifdef DEBUG\r
-    assert(reqdaemon_atom != 0);\r
-#endif    \r
-}\r
-\r
-void\r
-khm_unregister_reqdaemonwnd_class(void) {\r
-    if (reqdaemon_atom != 0) {\r
-        UnregisterClass(MAKEINTATOM(reqdaemon_atom), khm_hInstance);\r
-        reqdaemon_atom = 0;\r
-    }\r
-}\r
-\r
-void\r
-khm_init_request_daemon(void) {\r
-#ifdef DEBUG\r
-    assert(reqdaemon_thread == NULL);\r
-#endif\r
-\r
-    reqdaemon_thread = CreateThread(NULL,\r
-                                    0,\r
-                                    khm_reqdaemon_thread_proc,\r
-                                    NULL,\r
-                                    0,\r
-                                    NULL);\r
-\r
-#ifdef DEBUG\r
-    assert(reqdaemon_thread != NULL);\r
-#endif    \r
-}\r
-\r
-void\r
-khm_exit_request_daemon(void) {\r
-    if (reqdaemon_hwnd == NULL)\r
-        return;\r
-\r
-    SendMessage(reqdaemon_hwnd, WM_CLOSE, 0, 0);\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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);
+            }
+
+            widname[0] = 0;
+
+            do {
+                if (khm_cred_is_in_dialog()) {
+                    khm_cred_wait_for_dialog(INFINITE, NULL, NULL, 0);
+                }
+
+                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,
+                                                        widname,
+                                                        sizeof(widname))))
+                    continue;
+                else {
+                    lr = (result != KHUI_NC_RESULT_PROCESS);
+                    break;
+                }
+            } while(TRUE);
+
+#ifdef DEBUG
+            assert(lr || pdlginfo->dlgtype != NETID_DLGTYPE_TGT ||
+                   widname[0]);
+#endif
+
+            if (!lr && pdlginfo->dlgtype == NETID_DLGTYPE_TGT &&
+                widname[0]) {
+                khm_handle out_ident;
+                wchar_t * atsign;
+
+                atsign = wcsrchr(widname, L'@');
+
+                if (atsign == NULL)
+                    goto _exit;
+
+                if (KHM_SUCCEEDED(kcdb_identity_create(widname,
+                                                       0,
+                                                       &out_ident))) {
+                    khm_size cb;
+
+                    pdlginfo->out.ccache[0] = 0;
+
+                    cb = sizeof(pdlginfo->out.ccache);
+                    kcdb_identity_get_attrib(out_ident,
+                                             L"Krb5CCName",
+                                             NULL,
+                                             pdlginfo->out.ccache,
+                                             &cb);
+                    kcdb_identity_release(out_ident);
+                } else {
+#ifdef DEBUG
+                    assert(FALSE);
+#endif
+                }
+
+                *atsign++ = 0;
+
+                StringCbCopy(pdlginfo->out.username,
+                             sizeof(pdlginfo->out.username),
+                             widname);
+
+                StringCbCopy(pdlginfo->out.realm,
+                             sizeof(pdlginfo->out.realm),
+                             atsign);
+            }
+
+        _exit:
+
+            if (pdlginfo)
+                UnmapViewOfFile(pdlginfo);
+            if (hmap)
+                CloseHandle(hmap);
+            if (identity)
+                kcdb_identity_release(identity);
+
+            return lr;
+        }
+
+#ifdef DEPRECATED_REMOTE_CALL
+        /* 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_PARAM;
+                    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_PARAM;
+                    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_PARAM;
+                    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_PARAM;
+                    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;
+#ifdef DEBUG
+    DWORD dw;
+#endif
+
+    PDESCTHREAD(L"Remote Request Daemon", L"App");
+
+    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);
+}
index b55e93c992b3d6d3e6e6e6fb3d27ed15a19e6493..99a50f5e0a0e1a970da7add4631055bab7efc53f 100644 (file)
@@ -1,42 +1,42 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_REQDAEMON_H\r
-#define __KHIMAIRA_REQDAEMON_H\r
-\r
-void\r
-khm_register_reqdaemonwnd_class(void);\r
-\r
-void\r
-khm_unregister_reqdaemonwnd_class(void);\r
-\r
-void\r
-khm_init_request_daemon(void);\r
-\r
-void\r
-khm_exit_request_daemon(void);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
index 7be9a84bef985363adadee4a44a57f2511ad6ab1..1a3c35b23937ab679e1f905929e5af97714d8160 100644 (file)
-//{{NO_DEPENDENCIES}}\r
-// Microsoft Visual C++ generated include file.\r
-// Used by C:\work\pismere\athena\auth\krb5\src\windows\identity\ui\lang\en_us\khapp.rc\r
-//\r
-#define IDI_MAIN_APP                    104\r
-#define IDD_PP_CRED                     106\r
-#define IDD_PP_IDENT                    107\r
-#define IDB_TK_REFRESH                  108\r
-#define IDS_MAIN_WINDOW_TITLE           108\r
-#define IDS_MENU_FILE                   109\r
-#define IDB_ID                          110\r
-#define IDS_MENU_CRED                   110\r
-#define IDB_ID_DELETE                   111\r
-#define IDS_MENU_VIEW                   111\r
-#define IDB_ID_NEW                      112\r
-#define IDS_MENU_OPTIONS                112\r
-#define IDB_ID_REFRESH                  113\r
-#define IDS_MENU_HELP                   113\r
-#define IDB_TK                          114\r
-#define IDS_ACTION_PROPERTIES           114\r
-#define IDB_TK_DELETE                   115\r
-#define IDS_ACTION_EXIT                 115\r
-#define IDB_TK_NEW                      116\r
-#define IDS_CFG_ROOT_NAME               116\r
-#define IDS_ACTION_SET_DEF_ID           117\r
-#define IDS_ACTION_SET_SRCH_ID          118\r
-#define IDB_VW_REFRESH_SM               118\r
-#define IDR_MENU_BAR                    119\r
-#define IDS_CFG_ROOT_TITLE              119\r
-#define IDS_CFG_GENERAL_SHORT           120\r
-#define IDB_TB_BLANK                    121\r
-#define IDS_ACTION_NEW_CRED             121\r
-#define IDS_ACTION_PASSWD_ID            122\r
-#define IDS_ACTION_CHOOSE_COLS          123\r
-#define IDB_TB_BLANK_SM                 123\r
-#define IDS_ACTION_DEBUG_WINDOW         124\r
-#define IDB_VW_REFRESH                  124\r
-#define IDS_ACTION_VIEW_REFRESH         125\r
-#define IDB_ID_DELETE_DIS               125\r
-#define IDS_MENU_LAYOUT                 126\r
-#define IDB_ID_DELETE_DIS_SM            126\r
-#define IDS_MENU_TOOLBARS               127\r
-#define IDB_ID_DELETE_SM                127\r
-#define IDS_ACTION_LAYOUT_ID            128\r
-#define IDB_ID_DIS                      128\r
-#define IDS_ACTION_LAYOUT_TYPE          129\r
-#define IDB_ID_DIS_SM                   129\r
-#define IDS_ACTION_LAYOUT_LOC           130\r
-#define IDB_ID_NEW_DIS                  130\r
-#define IDS_ACTION_TB_STANDARD          131\r
-#define IDB_ID_NEW_DIS_SM               131\r
-#define IDS_ACTION_OPT_KHIM             132\r
-#define IDB_ID_NEW_SM                   132\r
-#define IDB_ID_REFRESH_DIS              133\r
-#define IDS_ACTION_OPT_IDENTS           133\r
-#define IDS_ACTION_OPT_NOTIF            134\r
-#define IDB_ID_REFRESH_SM               134\r
-#define IDS_ACTION_HELP_CTX             135\r
-#define IDB_ID_REFRESH_DIS_SM           135\r
-#define IDS_ACTION_HELP_CONTENTS        136\r
-#define IDB_TK_DELETE_DIS               136\r
-#define IDS_ACTION_HELP_INDEX           137\r
-#define IDB_TK_DELETE_DIS_SM            137\r
-#define IDS_ACTION_HELP_ABOUT           138\r
-#define IDB_TK_DELETE_SM                138\r
-#define IDB_TK_DIS_SM                   139\r
-#define IDS_CFG_GENERAL_LONG            139\r
-#define IDB_TK_NEW_DIS                  140\r
-#define IDS_SAMPLE_STRING               140\r
-#define IDB_TK_NEW_DIS_SM               141\r
-#define IDS_NO_CREDS                    141\r
-#define IDB_TK_NEW_SM                   142\r
-#define IDS_WT_INIT_CREDS               142\r
-#define IDB_TK_REFRESH_DIS              143\r
-#define IDS_WT_NEW_CREDS                143\r
-#define IDB_TK_REFRESH_DIS_SM           144\r
-#define IDS_NC_IDENTITY                 144\r
-#define IDB_TK_REFRESH_SM               145\r
-#define IDS_NC_IDENTS                   145\r
-#define IDB_TK_SM                       146\r
-#define IDS_NC_CREDTEXT_ID_NONE         146\r
-#define IDB_HELP_SM                     147\r
-#define IDS_NC_CREDTEXT_ID_ONE          147\r
-#define IDB_HELP                        148\r
-#define IDS_NC_CREDTEXT_ID_MANY         148\r
-#define IDB_LOGO_SHADE                  149\r
-#define IDS_NC_CREDTEXT_ID_INVALID      149\r
-#define IDS_WTPOST_INIT_CREDS           150\r
-#define IDS_WTPOST_NEW_CREDS            151\r
-#define IDB_WDG_EXPAND                  152\r
-#define IDS_ACTION_RENEW_CRED           152\r
-#define IDB_WDG_COLLAPSE                153\r
-#define IDS_ACTION_DESTROY_CRED         153\r
-#define IDB_ID_SM                       154\r
-#define IDS_DEFAULT_FONT                154\r
-#define IDB_WDG_EXPAND_HI               155\r
-#define IDS_NC_CREDTEXT_TABS            155\r
-#define IDB_WDG_COLLAPSE_HI             156\r
-#define IDS_NOTIFY_PREFIX               156\r
-#define IDB_WDG_CREDTYPE                157\r
-#define IDS_NOTIFY_READY                157\r
-#define IDB_WDG_FLAG                    158\r
-#define IDS_NOTIFY_ATTENTION            158\r
-#define IDB_FLAG_WARN                   159\r
-#define IDS_ALERT_DEFAULT               159\r
-#define IDB_FLAG_EXPIRED                160\r
-#define IDS_PACTION_OK                  160\r
-#define IDB_FLAG_CRITICAL               161\r
-#define IDS_PACTION_CANCEL              161\r
-#define IDS_PACTION_CLOSE               162\r
-#define IDD_NC_NEWCRED                  162\r
-#define IDD_NC_BBAR                     163\r
-#define IDS_ALERT_NOSEL_TITLE           163\r
-#define IDS_ALERT_NOSEL                 164\r
-#define IDI_ENABLED                     165\r
-#define IDS_NC_CREDTEXT_ID_VALID        165\r
-#define IDI_DISABLED                    166\r
-#define IDS_NC_CREDTEXT_ID_UNCHECKED    166\r
-#define IDS_PROP_COL_PROPERTY           167\r
-#define IDS_PROP_COL_VALUE              168\r
-#define IDI_NOTIFY_NONE                 169\r
-#define IDS_NC_NEW_IDENT                169\r
-#define IDI_NOTIFY_INFO                 170\r
-#define IDS_NC_CREDTEXT_ID_CHECKING     170\r
-#define IDI_NOTIFY_WARN                 171\r
-#define IDS_ACTION_OPEN_APP             171\r
-#define IDI_NOTIFY_ERROR                172\r
-#define IDS_CTX_NEW_IDENT               172\r
-#define IDS_CTX_NEW_CREDS               173\r
-#define IDD_CFG_MAIN                    173\r
-#define IDS_CTX_RENEW_CREDS             174\r
-#define IDD_CFG_GENERIC                 174\r
-#define IDS_CTX_PROC_NEW_IDENT          175\r
-#define IDB_LOGO_OPAQUE                 175\r
-#define IDS_CTX_PROC_NEW_CREDS          176\r
-#define IDD_CFG_GENERAL                 176\r
-#define IDS_CTX_PROC_RENEW_CREDS        177\r
-#define IDD_CFG_IDENTITIES              177\r
-#define IDS_ACTION_CLOSE_APP            178\r
-#define IDD_CFG_NOTIF                   178\r
-#define IDS_NC_FAILED_TITLE             179\r
-#define IDD_CFG_PLUGINS                 179\r
-#define IDS_CFG_IDENTITIES_SHORT        180\r
-#define IDD_CFG_IDENTITY                180\r
-#define IDS_CFG_IDENTITIES_LONG         181\r
-#define IDI_CFG_DEFAULT                 181\r
-#define IDS_CFG_NOTIF_SHORT             182\r
-#define IDI_CFG_MODIFIED                182\r
-#define IDS_CFG_NOTIF_LONG              183\r
-#define IDI_CFG_APPLIED                 183\r
-#define IDS_CFG_PLUGINS_SHORT           184\r
-#define IDD_CFG_IDS_TAB                 184\r
-#define IDS_CFG_PLUGINS_LONG            185\r
-#define IDD_CFG_ID_TAB                  185\r
-#define IDS_CFG_IDENTITY_SHORT          186\r
-#define IDI_CFG_DELETED                 186\r
-#define IDS_CFG_IDENTITY_LONG           187\r
-#define IDI_ID                          187\r
-#define IDS_CTX_DESTROY_CREDS           188\r
-#define IDB_IMPORT_SM_DIS               188\r
-#define IDS_WARN_EXPIRE                 189\r
-#define IDB_IMPORT                      189\r
-#define IDS_WARN_TITLE                  190\r
-#define IDB_IMPORT_DIS                  190\r
-#define IDS_ALERT_MOREINFO              191\r
-#define IDB_IMPORT_SM                   191\r
-#define IDS_WARN_EXPIRED                192\r
-#define IDB_CHPW_SM                     192\r
-#define IDS_WARN_EXPIRE_ID              193\r
-#define IDB_CHPW                        193\r
-#define IDS_WARN_EXPIRED_ID             194\r
-#define IDB_CHPW_DIS                    194\r
-#define IDS_WARN_WM_TITLE               195\r
-#define IDB_CHPW_DIS_SM                 195\r
-#define IDS_WARN_WM_MSG                 196\r
-#define IDD_ABOUT                       196\r
-#define IDS_CFG_ID_TAB_SHORT            197\r
-#define IDB_TB_SPACE                    197\r
-#define IDS_CFG_ID_TAB_LONG             198\r
-#define IDB_WDG_STUCK_HI                198\r
-#define IDS_CFG_IDS_TAB_SHORT           199\r
-#define IDB_WDG_STICK                   199\r
-#define IDS_CFG_IDS_TAB_LONG            200\r
-#define IDB_WDG_STICK_HI                200\r
-#define IDS_CFG_IDS_IDENTITY            201\r
-#define IDB_WDG_STUCK                   201\r
-#define IDS_ACTION_IMPORT               202\r
-#define IDS_CTX_IMPORT                  203\r
-#define IDB_FLAG_RENEW                  203\r
-#define IDS_CFG_PI_COL_PLUGINS          204\r
-#define IDI_APPICON_WARN                204\r
-#define IDS_PISTATE_FAILUNK             205\r
-#define IDI_APPICON_EXP                 205\r
-#define IDS_PISTATE_FAILMAX             206\r
-#define IDI_APPICON_OK                  206\r
-#define IDS_PISTATE_FAILREG             207\r
-#define IDI_CFG_PLUGIN                  207\r
-#define IDS_PISTATE_FAILDIS             208\r
-#define IDI_CFG_PLUGIN_ERR              208\r
-#define IDS_PISTATE_FAILLOD             209\r
-#define IDI_CFG_PLUGIN_DIS              209\r
-#define IDS_PISTATE_PLACEHOLD           210\r
-#define IDD_CFG_APPEAR                  210\r
-#define IDS_PISTATE_REG                 211\r
-#define IDD_CFG_ADDIDENT                211\r
-#define IDS_PISTATE_HOLD                212\r
-#define IDS_PISTATE_INIT                213\r
-#define IDS_PISTATE_RUN                 214\r
-#define IDS_PISTATE_EXIT                215\r
-#define IDS_CTX_PASSWORD                216\r
-#define IDS_WT_PASSWORD                 217\r
-#define IDS_WTPOST_PASSWORD             218\r
-#define IDS_CTX_PROC_PASSWORD           219\r
-#define IDS_NC_PWD_FAILED_TITLE         220\r
-#define IDS_CMDLINE_HELP                221\r
-#define IDS_PACTION_NEXT                222\r
-#define IDS_ERR_TITLE_NO_IDENTPRO       223\r
-#define IDS_ERR_MSG_NO_IDENTPRO         224\r
-#define IDS_ERR_SUGG_NO_IDENTPRO        225\r
-#define IDS_NC_REN_FAILED_TITLE         226\r
-#define IDS_CW_DEFAULT                  227\r
-#define IDS_ACTION_OPT_PLUGINS          228\r
-#define IDS_NC_SETDEF                   229\r
-#define IDS_NC_ID_DEF                   230\r
-#define IDS_NC_ID_WDEF                  231\r
-#define IDS_NC_ID_NDEF                  232\r
-#define IDS_PACTION_YES                 233\r
-#define IDS_PACTION_NO                  234\r
-#define IDS_PACTION_YESALL              235\r
-#define IDS_PACTION_NOALL               236\r
-#define IDS_PACTION_KEEP                237\r
-#define IDS_PACTION_REMOVE              238\r
-#define IDS_PACTION_DISCARD             239\r
-#define IDS_CFG_IT_MOD                  240\r
-#define IDS_CFG_IT_APP                  241\r
-#define IDS_CFG_IT_NONE                 242\r
-#define IDS_CFG_NODESC                  243\r
-#define IDS_CFG_P_DELCNFT               244\r
-#define IDS_CFG_P_DELCNFM               245\r
-#define IDS_CFG_P_DELCNFS               246\r
-#define IDS_CFG_P_DELNDEP               247\r
-#define IDS_CFG_P_ENBCNFT               248\r
-#define IDS_CFG_P_ENBCNFM               249\r
-#define IDS_PISTATE_FAILINIT            250\r
-#define IDS_CFG_P_UNRCNFT               251\r
-#define IDS_CFG_P_UNRCNFM               252\r
-#define IDS_CFG_P_UNRCNFS               253\r
-#define IDS_ACTION_LAYOUT_CUST          254\r
-#define IDS_APR_HEADER_TEXT             255\r
-#define IDS_APR_HEADER_TEXT_BOLD        256\r
-#define IDS_APR_TEXT                    257\r
-#define IDS_APR_TEXT_BOLD               258\r
-#define IDS_APR_SAMPLE_TEXT_NORMAL      259\r
-#define IDS_CFG_APPEAR_SHORT            260\r
-#define IDS_CFG_APPEAR_LONG             261\r
-#define IDS_ACTION_OPT_APPEAR           262\r
-#define IDS_APR_SAMPLE_TEXT_SEL         263\r
-#define IDS_CFG_IDNAME_INV              264\r
-#define IDS_CFG_IDNAME_PRB              265\r
-#define IDS_CFG_IDNAME_EXT              266\r
-#define IDS_CFG_IDNAME_CCR              267\r
-#define IDS_CFG_IDNAME_CCC              268\r
-#define IDS_CFG_LOGF_CS                 269\r
-#define IDS_CFG_LOGF_CSR                270\r
-#define IDS_ACTIONT_PROPERTIES          271\r
-#define IDS_ACTIONT_EXIT                272\r
-#define IDS_ACTIONT_SET_DEF_ID          273\r
-#define IDS_ACTIONT_PASSWD_ID           274\r
-#define IDS_ACTIONT_NEW_CRED            275\r
-#define IDS_ACTIONT_RENEW_CRED          276\r
-#define IDS_ACTIONT_DESTROY_CRED        277\r
-#define IDS_ACTIONT_VIEW_REFRESH        278\r
-#define IDS_ACTIONT_OPT_IDENTS          279\r
-#define IDS_ACTIONT_OPT_KHIM            280\r
-#define IDS_ACTIONT_OPT_NOTIF           281\r
-#define IDS_ACTIONT_OPT_PLUGINS         282\r
-#define IDS_ACTIONT_OPT_APPEAR          283\r
-#define IDS_ACTIONT_HELP_CTX            284\r
-#define IDS_ACTIONT_IMPORT              285\r
-#define IDS_NC_FAILED_TITLE_I           286\r
-#define IDS_NC_PWD_FAILED_TITLE_I       287\r
-#define IDS_NC_REN_FAILED_TITLE_I       288\r
-#define IDS_CFG_IDNAME_NON              289\r
-#define IDS_MENU_DESTROY_CRED           290\r
-#define IDS_MENU_RENEW_CRED             291\r
-#define IDS_ACTION_DESTROY_ALL          292\r
-#define IDS_ACTION_RENEW_ALL            293\r
-#define IDS_IDACTION_RENEW              294\r
-#define IDS_IDACTION_DESTROY            295\r
-#define IDS_CTX_DESTROY_ID              296\r
-#define IDS_NCN_IDENT_INVALID           297\r
-#define IDS_NCN_IDENT_CHECKING          298\r
-#define IDS_NCN_IDENT_UNKNOWN           299\r
-#define IDS_REMOTE_FAIL                 300\r
-#define IDS_REMOTE_FAIL_TITLE           301\r
-#define IDS_IDACTION_NEW                302\r
-#define IDS_IDACTIONT_NEW               303\r
-#define IDS_IDACTIONT_RENEW             304\r
-#define IDS_IDACTIONT_DESTROY           305\r
-#define IDS_ALERTTYPE_PLUGIN            306\r
-#define IDS_ALERTTYPE_EXPIRE            307\r
-#define IDS_ALERTTYPE_RENEWFAIL         308\r
-#define IDS_ALERTTYPE_ACQUIREFAIL       309\r
-#define IDS_ALERTTYPE_CHPW              310\r
-#define IDS_ACTION_LAYOUT_MINI          311\r
-#define IDS_IDEXPDISP_NOCRED            312\r
-#define IDS_IDEXPDISP_1CRED             313\r
-#define IDS_IDEXPDISP_NCRED             314\r
-#define IDS_CW_DEFAULTTF                315\r
-#define IDS_CW_TYPEF                    316\r
-#define IDS_CW_EXPIREF                  317\r
-#define IDS_CW_EXPIRED                  318\r
-#define IDC_NC_CREDTEXT_LABEL           1009\r
-#define IDC_NC_CREDTEXT                 1012\r
-#define IDC_NC_HELP                     1017\r
-#define IDC_NC_ADVANCED                 1019\r
-#define IDC_PP_IDNAME                   1026\r
-#define IDC_PP_IDDEF                    1027\r
-#define IDC_PP_IDSEARCH                 1028\r
-#define IDC_PP_PROPLIST                 1035\r
-#define IDC_PP_CPROPLIST                1036\r
-#define IDC_NC_TPL_ROW                  1039\r
-#define IDC_NC_TPL_PANEL                1040\r
-#define IDC_NC_TPL_LABEL                1041\r
-#define IDC_NC_TPL_INPUT                1042\r
-#define IDC_NC_TPL_LABEL_LG             1043\r
-#define IDC_NC_TPL_INPUT_LG             1044\r
-#define IDC_NC_TPL_ROW_LG               1045\r
-#define IDC_CFG_NODELIST                1045\r
-#define IDAPPLY                         1048\r
-#define IDC_CFG_TITLE                   1050\r
-#define IDC_CFG_PANE                    1051\r
-#define IDC_NOTIF_MONITOR               1053\r
-#define IDC_PP_DUMMY                    1054\r
-#define IDC_NOTIF_RENEW                 1055\r
-#define IDC_NOTIF_RENEW_THR             1056\r
-#define IDC_NOTIF_WARN1                 1057\r
-#define IDC_NOTIF_WARN1_THR             1058\r
-#define IDC_NOTIF_WARN2                 1059\r
-#define IDC_NOTIF_WARN2_THR             1060\r
-#define IDC_CFG_KEEPRUNNING             1061\r
-#define IDC_CFG_STARTUP_GROUP           1062\r
-#define IDC_CFG_AUTOSTART               1063\r
-#define IDC_CFG_AUTOIMPORT              1064\r
-#define IDC_CFG_AUTOINIT                1065\r
-#define IDC_CFG_OTHER                   1066\r
-#define IDC_CFG_MONITOR                 1069\r
-#define IDC_CFG_STICKY                  1070\r
-#define IDC_CFG_RENEW                   1075\r
-#define IDC_CFG_REMOVE                  1076\r
-#define IDC_CFG_TAB                     1077\r
-#define IDC_CFG_TARGET                  1078\r
-#define IDC_CFG_PLUGINS                 1079\r
-#define IDC_CFG_DESC                    1084\r
-#define IDC_CFG_LBL_STATE               1085\r
-#define IDC_CFG_STATE                   1086\r
-#define IDC_CFG_LBL_DEPS                1087\r
-#define IDC_CFG_DEPS                    1088\r
-#define IDC_CFG_DISABLE                 1089\r
-#define IDC_CFG_ENABLE                  1090\r
-#define IDC_CFG_LBL_MOD                 1092\r
-#define IDC_CFG_MODULE                  1093\r
-#define IDC_CFG_LBL_VEN                 1094\r
-#define IDC_CFG_VENDOR                  1095\r
-#define IDC_CFG_REGISTER                1097\r
-#define IDC_CFG_NETDETECT               1098\r
-#define IDC_PP_STICKY                   1099\r
-#define IDC_PRODUCT                     1100\r
-#define IDC_COPYRIGHT                   1101\r
-#define IDC_BUILDINFO                   1102\r
-#define IDC_MODULES                     1103\r
-#define IDC_PP_CONFIG                   1104\r
-#define IDC_CFG_UNREGISTER              1107\r
-#define IDC_CFG_VERSION                 1108\r
-#define IDC_CFG_ICON                    1109\r
-#define IDC_CFG_LOGTOFILE               1110\r
-#define IDC_CFG_LOGPATH                 1111\r
-#define IDC_NOTIF_HALFLIFE              1112\r
-#define IDC_CFG_DESTROYALL              1113\r
-#define IDC_CFG_SAMPLE_NORMAL           1123\r
-#define IDC_CFG_REVERT                  1127\r
-#define IDC_CFG_FONTS                   1128\r
-#define IDC_CFG_SAMPLE_BOLD             1129\r
-#define IDC_CFG_SIZE                    1130\r
-#define IDC_CFG_BOLD                    1131\r
-#define IDC_CFG_ITALICS                 1132\r
-#define IDC_CFG_ADDIDENT                1133\r
-#define IDC_SM_CTL                      1134\r
-#define IDC_CFG_SHOWLOG                 1135\r
-#define IDC_MED_CTL                     1135\r
-#define IDC_LG_CTL                      1136\r
-#define IDC_SM_LBL                      1137\r
-#define IDC_MED_LBL                     1138\r
-#define IDC_LG_LBL                      1139\r
-#define IDC_CFG_NOTACTION               1141\r
-#define IDC_CFG_NOTACT_STATIC           1142\r
-#define IDC_NC_BASIC                    1143\r
-#define IDA_ACTIVATE_MENU               40003\r
-#define IDA_UP                          40004\r
-#define IDA_DOWN                        40005\r
-#define IDA_LEFT                        40006\r
-#define IDA_RIGHT                       40007\r
-#define IDA_ESC                         40008\r
-#define IDA_ENTER                       40009\r
-\r
-// Next default values for new objects\r
-// \r
-#ifdef APSTUDIO_INVOKED\r
-#ifndef APSTUDIO_READONLY_SYMBOLS\r
-#define _APS_NEXT_RESOURCE_VALUE        212\r
-#define _APS_NEXT_COMMAND_VALUE         40010\r
-#define _APS_NEXT_CONTROL_VALUE         1144\r
-#define _APS_NEXT_SYMED_VALUE           101\r
-#endif\r
-#endif\r
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by C:\work\pismere\athena\auth\krb5\src\windows\identity\ui\lang\en_us\khapp.rc
+//
+#define IDI_MAIN_APP                    104
+#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_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_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 IDR_MENU_BAR                    119
+#define IDS_CFG_ROOT_TITLE              119
+#define IDS_CFG_GENERAL_SHORT           120
+#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 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_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_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 IDS_WTPOST_INIT_CREDS           150
+#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 IDS_PACTION_CLOSE               162
+#define IDD_NC_NEWCRED                  162
+#define IDD_NC_BBAR                     163
+#define IDS_ALERT_NOSEL_TITLE           163
+#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_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 IDB_WDG_STUCK_HI                198
+#define IDS_CFG_IDS_TAB_SHORT           199
+#define IDB_WDG_STICK                   199
+#define IDS_CFG_IDS_TAB_LONG            200
+#define IDB_WDG_STICK_HI                200
+#define IDS_CFG_IDS_IDENTITY            201
+#define IDB_WDG_STUCK                   201
+#define IDS_ACTION_IMPORT               202
+#define IDS_CTX_IMPORT                  203
+#define IDB_FLAG_RENEW                  203
+#define IDS_CFG_PI_COL_PLUGINS          204
+#define IDI_APPICON_WARN                204
+#define IDS_PISTATE_FAILUNK             205
+#define IDI_APPICON_EXP                 205
+#define IDS_PISTATE_FAILMAX             206
+#define IDI_APPICON_OK                  206
+#define IDS_PISTATE_FAILREG             207
+#define IDI_CFG_PLUGIN                  207
+#define IDS_PISTATE_FAILDIS             208
+#define IDI_CFG_PLUGIN_ERR              208
+#define IDS_PISTATE_FAILLOD             209
+#define IDI_CFG_PLUGIN_DIS              209
+#define IDS_PISTATE_PLACEHOLD           210
+#define IDD_CFG_APPEAR                  210
+#define IDS_PISTATE_REG                 211
+#define IDD_CFG_ADDIDENT                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 IDS_PACTION_NEXT                222
+#define IDS_ERR_TITLE_NO_IDENTPRO       223
+#define IDS_ERR_MSG_NO_IDENTPRO         224
+#define IDS_ERR_SUGG_NO_IDENTPRO        225
+#define IDS_NC_REN_FAILED_TITLE         226
+#define IDS_CW_DEFAULT                  227
+#define IDS_ACTION_OPT_PLUGINS          228
+#define IDS_NC_SETDEF                   229
+#define IDS_NC_ID_DEF                   230
+#define IDS_NC_ID_WDEF                  231
+#define IDS_NC_ID_NDEF                  232
+#define IDS_PACTION_YES                 233
+#define IDS_PACTION_NO                  234
+#define IDS_PACTION_YESALL              235
+#define IDS_PACTION_NOALL               236
+#define IDS_PACTION_KEEP                237
+#define IDS_PACTION_REMOVE              238
+#define IDS_PACTION_DISCARD             239
+#define IDS_CFG_IT_MOD                  240
+#define IDS_CFG_IT_APP                  241
+#define IDS_CFG_IT_NONE                 242
+#define IDS_CFG_NODESC                  243
+#define IDS_CFG_P_DELCNFT               244
+#define IDS_CFG_P_DELCNFM               245
+#define IDS_CFG_P_DELCNFS               246
+#define IDS_CFG_P_DELNDEP               247
+#define IDS_CFG_P_ENBCNFT               248
+#define IDS_CFG_P_ENBCNFM               249
+#define IDS_PISTATE_FAILINIT            250
+#define IDS_CFG_P_UNRCNFT               251
+#define IDS_CFG_P_UNRCNFM               252
+#define IDS_CFG_P_UNRCNFS               253
+#define IDS_ACTION_LAYOUT_CUST          254
+#define IDS_APR_HEADER_TEXT             255
+#define IDS_APR_HEADER_TEXT_BOLD        256
+#define IDS_APR_TEXT                    257
+#define IDS_APR_TEXT_BOLD               258
+#define IDS_APR_SAMPLE_TEXT_NORMAL      259
+#define IDS_CFG_APPEAR_SHORT            260
+#define IDS_CFG_APPEAR_LONG             261
+#define IDS_ACTION_OPT_APPEAR           262
+#define IDS_APR_SAMPLE_TEXT_SEL         263
+#define IDS_CFG_IDNAME_INV              264
+#define IDS_CFG_IDNAME_PRB              265
+#define IDS_CFG_IDNAME_EXT              266
+#define IDS_CFG_IDNAME_CCR              267
+#define IDS_CFG_IDNAME_CCC              268
+#define IDS_CFG_LOGF_CS                 269
+#define IDS_CFG_LOGF_CSR                270
+#define IDS_ACTIONT_PROPERTIES          271
+#define IDS_ACTIONT_EXIT                272
+#define IDS_ACTIONT_SET_DEF_ID          273
+#define IDS_ACTIONT_PASSWD_ID           274
+#define IDS_ACTIONT_NEW_CRED            275
+#define IDS_ACTIONT_RENEW_CRED          276
+#define IDS_ACTIONT_DESTROY_CRED        277
+#define IDS_ACTIONT_VIEW_REFRESH        278
+#define IDS_ACTIONT_OPT_IDENTS          279
+#define IDS_ACTIONT_OPT_KHIM            280
+#define IDS_ACTIONT_OPT_NOTIF           281
+#define IDS_ACTIONT_OPT_PLUGINS         282
+#define IDS_ACTIONT_OPT_APPEAR          283
+#define IDS_ACTIONT_HELP_CTX            284
+#define IDS_ACTIONT_IMPORT              285
+#define IDS_NC_FAILED_TITLE_I           286
+#define IDS_NC_PWD_FAILED_TITLE_I       287
+#define IDS_NC_REN_FAILED_TITLE_I       288
+#define IDS_CFG_IDNAME_NON              289
+#define IDS_MENU_DESTROY_CRED           290
+#define IDS_MENU_RENEW_CRED             291
+#define IDS_ACTION_DESTROY_ALL          292
+#define IDS_ACTION_RENEW_ALL            293
+#define IDS_IDACTION_RENEW              294
+#define IDS_IDACTION_DESTROY            295
+#define IDS_CTX_DESTROY_ID              296
+#define IDS_NCN_IDENT_INVALID           297
+#define IDS_NCN_IDENT_CHECKING          298
+#define IDS_NCN_IDENT_UNKNOWN           299
+#define IDS_REMOTE_FAIL                 300
+#define IDS_REMOTE_FAIL_TITLE           301
+#define IDS_IDACTION_NEW                302
+#define IDS_IDACTIONT_NEW               303
+#define IDS_IDACTIONT_RENEW             304
+#define IDS_IDACTIONT_DESTROY           305
+#define IDS_ALERTTYPE_PLUGIN            306
+#define IDS_ALERTTYPE_EXPIRE            307
+#define IDS_ALERTTYPE_RENEWFAIL         308
+#define IDS_ALERTTYPE_ACQUIREFAIL       309
+#define IDS_ALERTTYPE_CHPW              310
+#define IDS_ACTION_LAYOUT_MINI          311
+#define IDS_IDEXPDISP_NOCRED            312
+#define IDS_IDEXPDISP_1CRED             313
+#define IDS_IDEXPDISP_NCRED             314
+#define IDS_CW_DEFAULTTF                315
+#define IDS_CW_TYPEF                    316
+#define IDS_CW_EXPIREF                  317
+#define IDS_CW_EXPIRED                  318
+#define IDC_NC_CREDTEXT_LABEL           1009
+#define IDC_NC_CREDTEXT                 1012
+#define IDC_NC_HELP                     1017
+#define IDC_NC_ADVANCED                 1019
+#define IDC_PP_IDNAME                   1026
+#define IDC_PP_IDDEF                    1027
+#define IDC_PP_IDSEARCH                 1028
+#define IDC_PP_PROPLIST                 1035
+#define IDC_PP_CPROPLIST                1036
+#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_ROW_LG               1045
+#define IDC_CFG_NODELIST                1045
+#define IDAPPLY                         1048
+#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_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_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_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_MODULES                     1103
+#define IDC_PP_CONFIG                   1104
+#define IDC_CFG_UNREGISTER              1107
+#define IDC_CFG_VERSION                 1108
+#define IDC_CFG_ICON                    1109
+#define IDC_CFG_LOGTOFILE               1110
+#define IDC_CFG_LOGPATH                 1111
+#define IDC_NOTIF_HALFLIFE              1112
+#define IDC_CFG_DESTROYALL              1113
+#define IDC_CFG_SAMPLE_NORMAL           1123
+#define IDC_CFG_REVERT                  1127
+#define IDC_CFG_FONTS                   1128
+#define IDC_CFG_SAMPLE_BOLD             1129
+#define IDC_CFG_SIZE                    1130
+#define IDC_CFG_BOLD                    1131
+#define IDC_CFG_ITALICS                 1132
+#define IDC_CFG_ADDIDENT                1133
+#define IDC_SM_CTL                      1134
+#define IDC_CFG_SHOWLOG                 1135
+#define IDC_MED_CTL                     1135
+#define IDC_LG_CTL                      1136
+#define IDC_SM_LBL                      1137
+#define IDC_MED_LBL                     1138
+#define IDC_LG_LBL                      1139
+#define IDC_CFG_NOTACTION               1141
+#define IDC_CFG_NOTACT_STATIC           1142
+#define IDC_NC_BASIC                    1143
+#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        212
+#define _APS_NEXT_COMMAND_VALUE         40010
+#define _APS_NEXT_CONTROL_VALUE         1144
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
index e5f7e200902e14689d8ffde318fd8b51bb875f59..b785dfdd03c0b218625713d1db81656aea7259c0 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khmapp.h>\r
-#ifdef DEBUG\r
-#include<assert.h>\r
-#endif\r
-\r
-khm_statusbar_part khm_statusbar_parts[] = {\r
-    {KHUI_SBPART_INFO, 0, KHUI_SB_WTYPE_FILLER, NULL},\r
-    {KHUI_SBPART_NOTICE, 40, KHUI_SB_WTYPE_RELATIVE, NULL},\r
-#if 0\r
-    /* Not implemented. This was originally intended to provide\r
-       location information. */\r
-    {KHUI_SBPART_LOC, 40, KHUI_SB_WTYPE_ABSOLUTE, NULL}\r
-#endif\r
-};\r
-\r
-int khm_n_statusbar_parts = sizeof(khm_statusbar_parts) / sizeof(khm_statusbar_part);\r
-\r
-HWND khm_hwnd_statusbar = NULL;\r
-\r
-LRESULT \r
-khm_statusbar_notify(LPNMHDR nmhdr) {\r
-    LPNMMOUSE pnmm;\r
-\r
-    switch(nmhdr->code) {\r
-    case NM_CLICK:\r
-    case NM_DBLCLK:\r
-        pnmm = (LPNMMOUSE) nmhdr;\r
-\r
-        if (pnmm->dwItemSpec >= (DWORD) khm_n_statusbar_parts)\r
-            return TRUE;\r
-\r
-        if (khm_statusbar_parts[pnmm->dwItemSpec].id == KHUI_SBPART_NOTICE) {\r
-            /* means, show next notification */\r
-            kmq_post_message(KMSG_ALERT, KMSG_ALERT_SHOW_QUEUED, 0, 0);\r
-        }\r
-\r
-        return TRUE;\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-void \r
-khui_statusbar_set_parts(HWND parent) {\r
-    int i;\r
-    int fillerwidth;\r
-    int staticwidth;\r
-    int lastx;\r
-    int width;\r
-    RECT r;\r
-    INT * parts;\r
-\r
-    GetClientRect(parent, &r);\r
-    width = r.right - r.left;\r
-\r
-    /* calculate fillerwidth and staticwidth */\r
-    staticwidth = 0;\r
-    for(i=0;i<khm_n_statusbar_parts;i++) {\r
-        if(khm_statusbar_parts[i].wtype == KHUI_SB_WTYPE_ABSOLUTE) {\r
-            staticwidth += khm_statusbar_parts[i].width;\r
-        } else if(khm_statusbar_parts[i].wtype == KHUI_SB_WTYPE_RELATIVE) {\r
-            staticwidth += (khm_statusbar_parts[i].width * width) / 100;\r
-        }\r
-    }\r
-\r
-    fillerwidth = width - staticwidth;\r
-\r
-    parts = PMALLOC(sizeof(INT) * khm_n_statusbar_parts);\r
-\r
-    lastx = 0;\r
-    for(i=0;i<khm_n_statusbar_parts;i++) {\r
-        int w = 0;\r
-        switch(khm_statusbar_parts[i].wtype) {\r
-        case KHUI_SB_WTYPE_ABSOLUTE:\r
-            w = khm_statusbar_parts[i].width;\r
-            break;\r
-\r
-        case KHUI_SB_WTYPE_RELATIVE:\r
-            w = (khm_statusbar_parts[i].width * width) / 100;\r
-            break;\r
-\r
-        case KHUI_SB_WTYPE_FILLER:\r
-            w = fillerwidth;\r
-            break;\r
-\r
-        default:\r
-            w = 0;\r
-#ifdef DEBUG\r
-            assert(FALSE);\r
-#endif\r
-        }\r
-        lastx += w;\r
-\r
-        if(i==khm_n_statusbar_parts - 1)\r
-            parts[i] = -1;\r
-        else\r
-            parts[i] = lastx;\r
-    }\r
-\r
-    SendMessage(\r
-        khm_hwnd_statusbar,\r
-        SB_SETPARTS,\r
-        khm_n_statusbar_parts,\r
-        (LPARAM) parts);\r
-\r
-    PFREE(parts);\r
-}\r
-\r
-void khm_create_statusbar(HWND parent) {\r
-    HWND hwsb;\r
-\r
-    hwsb = CreateWindowEx(\r
-        0,\r
-        STATUSCLASSNAME,\r
-        NULL,\r
-        SBARS_SIZEGRIP | WS_CHILD | WS_VISIBLE,\r
-        0,0,0,0,\r
-        parent,\r
-        NULL,\r
-        khm_hInstance,\r
-        NULL);\r
-\r
-    if(!hwsb)\r
-        return;\r
-\r
-    khm_hwnd_statusbar = hwsb;\r
-\r
-    khui_statusbar_set_parts(parent);\r
-\r
-    kmq_post_message(KMSG_ALERT, KMSG_ALERT_CHECK_QUEUE, 0, 0);\r
-}\r
-\r
-void khm_update_statusbar(HWND parent) {\r
-    MoveWindow(khm_hwnd_statusbar, 0, 0, 0, 0, TRUE);\r
-    khui_statusbar_set_parts(parent);\r
-}\r
-\r
-int sb_find_index(int id) {\r
-    int i;\r
-\r
-    for(i=0;i<khm_n_statusbar_parts;i++) {\r
-        if(khm_statusbar_parts[i].id == id)\r
-            return i;\r
-    }\r
-\r
-    return -1;\r
-}\r
-\r
-void khm_statusbar_set_part(int id, HICON icon, wchar_t * text) {\r
-    int idx;\r
-\r
-    if (!khm_hwnd_statusbar)\r
-        return;\r
-\r
-    idx = sb_find_index(id);\r
-    if(idx < 0)\r
-        return;\r
-\r
-    if (khm_statusbar_parts[idx].hIcon != NULL) {\r
-        DestroyIcon(khm_statusbar_parts[idx].hIcon);\r
-        khm_statusbar_parts[idx].hIcon = NULL;\r
-    }\r
-\r
-    if (icon) {\r
-        khm_statusbar_parts[idx].hIcon = CopyImage(icon, IMAGE_ICON,\r
-                                                   GetSystemMetrics(SM_CXSMICON),\r
-                                                   GetSystemMetrics(SM_CYSMICON),\r
-                                                   LR_COPYFROMRESOURCE);\r
-    }\r
-\r
-    SendMessage(khm_hwnd_statusbar,\r
-                SB_SETICON,\r
-                idx,\r
-                (LPARAM) (khm_statusbar_parts[idx].hIcon ? khm_statusbar_parts[idx].hIcon:icon));\r
-\r
-    SendMessage(khm_hwnd_statusbar,\r
-                SB_SETTEXT,\r
-                idx,\r
-                (LPARAM) text);\r
-}\r
-\r
-\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+#ifdef DEBUG
+#include<assert.h>
+#endif
+
+khm_statusbar_part khm_statusbar_parts[] = {
+    {KHUI_SBPART_INFO, 0, KHUI_SB_WTYPE_FILLER, NULL},
+    {KHUI_SBPART_NOTICE, 40, KHUI_SB_WTYPE_RELATIVE, NULL},
+#if 0
+    /* Not implemented. This was originally intended to provide
+       location information. */
+    {KHUI_SBPART_LOC, 40, KHUI_SB_WTYPE_ABSOLUTE, NULL}
+#endif
+};
+
+int khm_n_statusbar_parts = sizeof(khm_statusbar_parts) / sizeof(khm_statusbar_part);
+
+HWND khm_hwnd_statusbar = NULL;
+
+LRESULT 
+khm_statusbar_notify(LPNMHDR nmhdr) {
+    LPNMMOUSE pnmm;
+
+    switch(nmhdr->code) {
+    case NM_CLICK:
+    case NM_DBLCLK:
+        pnmm = (LPNMMOUSE) nmhdr;
+
+        if (pnmm->dwItemSpec >= (DWORD) khm_n_statusbar_parts)
+            return TRUE;
+
+        if (khm_statusbar_parts[pnmm->dwItemSpec].id == KHUI_SBPART_NOTICE) {
+            /* means, show next notification */
+            kmq_post_message(KMSG_ALERT, KMSG_ALERT_SHOW_QUEUED, 0, 0);
+        }
+
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+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<khm_n_statusbar_parts;i++) {
+        if(khm_statusbar_parts[i].wtype == KHUI_SB_WTYPE_ABSOLUTE) {
+            staticwidth += khm_statusbar_parts[i].width;
+        } else if(khm_statusbar_parts[i].wtype == KHUI_SB_WTYPE_RELATIVE) {
+            staticwidth += (khm_statusbar_parts[i].width * width) / 100;
+        }
+    }
+
+    fillerwidth = width - staticwidth;
+
+    parts = PMALLOC(sizeof(INT) * khm_n_statusbar_parts);
+
+    lastx = 0;
+    for(i=0;i<khm_n_statusbar_parts;i++) {
+        int w = 0;
+        switch(khm_statusbar_parts[i].wtype) {
+        case KHUI_SB_WTYPE_ABSOLUTE:
+            w = khm_statusbar_parts[i].width;
+            break;
+
+        case KHUI_SB_WTYPE_RELATIVE:
+            w = (khm_statusbar_parts[i].width * width) / 100;
+            break;
+
+        case KHUI_SB_WTYPE_FILLER:
+            w = fillerwidth;
+            break;
+
+        default:
+            w = 0;
+#ifdef DEBUG
+            assert(FALSE);
+#endif
+        }
+        lastx += w;
+
+        if(i==khm_n_statusbar_parts - 1)
+            parts[i] = -1;
+        else
+            parts[i] = lastx;
+    }
+
+    SendMessage(
+        khm_hwnd_statusbar,
+        SB_SETPARTS,
+        khm_n_statusbar_parts,
+        (LPARAM) parts);
+
+    PFREE(parts);
+}
+
+void khm_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;
+
+    khm_hwnd_statusbar = hwsb;
+
+    khui_statusbar_set_parts(parent);
+
+    kmq_post_message(KMSG_ALERT, KMSG_ALERT_CHECK_QUEUE, 0, 0);
+}
+
+void khm_update_statusbar(HWND parent) {
+    MoveWindow(khm_hwnd_statusbar, 0, 0, 0, 0, TRUE);
+    khui_statusbar_set_parts(parent);
+}
+
+int sb_find_index(int id) {
+    int i;
+
+    for(i=0;i<khm_n_statusbar_parts;i++) {
+        if(khm_statusbar_parts[i].id == id)
+            return i;
+    }
+
+    return -1;
+}
+
+void khm_statusbar_set_part(int id, HICON icon, wchar_t * text) {
+    int idx;
+
+    if (!khm_hwnd_statusbar)
+        return;
+
+    idx = sb_find_index(id);
+    if(idx < 0)
+        return;
+
+    if (khm_statusbar_parts[idx].hIcon != NULL) {
+        DestroyIcon(khm_statusbar_parts[idx].hIcon);
+        khm_statusbar_parts[idx].hIcon = NULL;
+    }
+
+    if (icon) {
+        khm_statusbar_parts[idx].hIcon = CopyImage(icon, IMAGE_ICON,
+                                                   GetSystemMetrics(SM_CXSMICON),
+                                                   GetSystemMetrics(SM_CYSMICON),
+                                                   LR_COPYFROMRESOURCE);
+    }
+
+    SendMessage(khm_hwnd_statusbar,
+                SB_SETICON,
+                idx,
+                (LPARAM) (khm_statusbar_parts[idx].hIcon ? khm_statusbar_parts[idx].hIcon:icon));
+
+    SendMessage(khm_hwnd_statusbar,
+                SB_SETTEXT,
+                idx,
+                (LPARAM) text);
+}
+
+
index 6a2b3ddfb60113391aa2c3cfc1735fc01e9e0590..7b79f69b015b238d82a143d289be83ed6a8ffc54 100644 (file)
@@ -1,55 +1,55 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_STATUSBAR_H\r
-#define __KHIMAIRA_STATUSBAR_H\r
-\r
-typedef struct khm_statusbar_part_t {\r
-    int id;\r
-    int width;\r
-    int wtype; /* one of KHUI_SB_WTYPE_* */\r
-    HICON hIcon;\r
-} khm_statusbar_part;\r
-\r
-#define KHUI_SB_WTYPE_RELATIVE    1\r
-#define KHUI_SB_WTYPE_ABSOLUTE    2\r
-#define KHUI_SB_WTYPE_FILLER      4\r
-\r
-/* statusbar parts */\r
-#define KHUI_SBPART_INFO    1\r
-#define KHUI_SBPART_NOTICE  2\r
-#define KHUI_SBPART_LOC     3\r
-\r
-extern HWND khm_hwnd_statusbar;\r
-extern khm_statusbar_part khm_statusbar_parts[];\r
-extern int khm_n_statusbar_parts;\r
-\r
-void khm_create_statusbar(HWND p);\r
-void khm_update_statusbar(HWND parent);\r
-void khm_statusbar_set_part(int id, HICON icon, wchar_t * text);\r
-LRESULT khm_statusbar_notify(LPNMHDR nmhdr);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 khm_statusbar_part_t {
+    int id;
+    int width;
+    int wtype; /* one of KHUI_SB_WTYPE_* */
+    HICON hIcon;
+} khm_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 khm_hwnd_statusbar;
+extern khm_statusbar_part khm_statusbar_parts[];
+extern int khm_n_statusbar_parts;
+
+void khm_create_statusbar(HWND p);
+void khm_update_statusbar(HWND parent);
+void khm_statusbar_set_part(int id, HICON icon, wchar_t * text);
+LRESULT khm_statusbar_notify(LPNMHDR nmhdr);
+
+#endif
index 7024481c4a710155b1859aad9d97ed13c864959a..9a270c3eed57c289e2460917384dd5faffc83beb 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khmapp.h>\r
-#include<assert.h>\r
-\r
-/* The minimum half time interval is 60 seconds*/\r
-#define TT_MIN_HALFLIFE_INTERVAL 60\r
-\r
-/* as above, in FILETIME units of 100ns */\r
-#define FT_MIN_HALFLIFE_INTERVAL (TT_MIN_HALFLIFE_INTERVAL * 10000000i64)\r
-\r
-/* in seconds */\r
-#if 0\r
-khm_int32 khui_timeout_warn = KHUI_DEF_TIMEOUT_WARN;\r
-khm_int32 khui_timeout_crit = KHUI_DEF_TIMEOUT_CRIT;\r
-khm_int32 khui_timeout_renew = KHUI_DEF_TIMEOUT_RENEW;\r
-\r
-khm_boolean khui_do_renew = TRUE;\r
-khm_boolean khui_do_warn = TRUE;\r
-khm_boolean khui_do_crit = TRUE;\r
-#endif\r
-\r
-khui_timer_event * khui_timers = NULL;\r
-khm_size khui_n_timers = 0;\r
-khm_size khui_nc_timers = 0;\r
-\r
-CRITICAL_SECTION cs_timers;\r
-\r
-/*********************************************************************\r
-  Timers\r
- *********************************************************************/\r
-\r
-\r
-#define KHUI_TIMER_ALLOC_INCR 16\r
-\r
-void \r
-khm_timer_init(void) {\r
-#ifdef DEBUG\r
-    assert(khui_timers == NULL);\r
-#endif\r
-\r
-    khui_nc_timers = KHUI_TIMER_ALLOC_INCR;\r
-    khui_n_timers = 0;\r
-    khui_timers = PMALLOC(sizeof(*khui_timers) * khui_nc_timers);\r
-\r
-#ifdef DEBUG\r
-    assert(khui_timers != NULL);\r
-#endif\r
-\r
-    InitializeCriticalSection(&cs_timers);\r
-}\r
-\r
-void\r
-khm_timer_exit(void) {\r
-    EnterCriticalSection(&cs_timers);\r
-\r
-    if (khui_timers)\r
-        PFREE(khui_timers);\r
-    khui_timers = NULL;\r
-    khui_n_timers = 0;\r
-    khui_nc_timers = 0;\r
-\r
-    LeaveCriticalSection(&cs_timers);\r
-    DeleteCriticalSection(&cs_timers);\r
-}\r
-\r
-/* called with cs_timers held */\r
-static void\r
-tmr_fire_timer(void) {\r
-    int i;\r
-    khm_int64 curtime;\r
-    khm_int64 err;\r
-    khm_int64 next_event;\r
-    int     tmr_count[KHUI_N_TTYPES];\r
-    khm_int64 tmr_offset[KHUI_N_TTYPES];\r
-    int t;\r
-    khm_handle eff_ident = NULL;\r
-    khui_timer_type eff_type = 0; /* meaningless */\r
-    int fire_count = 0;\r
-    FILETIME ft;\r
-\r
-    _begin_task(0);\r
-    _report_cs0(KHERR_DEBUG_1, L"Checking for expired timers");\r
-    _describe();\r
-\r
-    TimetToFileTimeInterval(KHUI_TIMEEQ_ERROR_SMALL, &ft);\r
-    err = FtToInt(&ft);\r
-    GetSystemTimeAsFileTime(&ft);\r
-    curtime = FtToInt(&ft);\r
-\r
-    next_event = 0;\r
-\r
-    ZeroMemory(tmr_count, sizeof(tmr_count));\r
-    ZeroMemory(tmr_offset, sizeof(tmr_offset));\r
-\r
-    for (i=0; i < (int) khui_n_timers; i++) {\r
-        if (!(khui_timers[i].flags & \r
-              (KHUI_TE_FLAG_STALE | KHUI_TE_FLAG_EXPIRED)) &&\r
-            khui_timers[i].type != KHUI_TTYPE_ID_MARK &&\r
-            khui_timers[i].expire < curtime + err) {\r
-\r
-            _report_cs3(KHERR_DEBUG_1, L"Expiring timer index=%1!d!, type=%2!d!, key=%3!p!",\r
-                        _int32(i), _int32(khui_timers[i].type),\r
-                        _cptr(khui_timers[i].key));\r
-\r
-            t = khui_timers[i].type;\r
-\r
-            switch(t) {\r
-            case KHUI_TTYPE_ID_RENEW:\r
-                _report_cs1(KHERR_DEBUG_1, L"Renewing identity %1!p!",\r
-                            _cptr(khui_timers[i].key));\r
-                khm_cred_renew_identity(khui_timers[i].key);\r
-                khui_timers[i].flags |= KHUI_TE_FLAG_EXPIRED;\r
-                break;\r
-\r
-            case KHUI_TTYPE_CRED_RENEW:\r
-                /* the equivalence threshold for setting the timer is\r
-                   a lot larger than what we are testing for here\r
-                   (KHUI_TIMEEQ_ERROR vs KHUI_TIMEEQ_ERROR_SMALL) so\r
-                   we assume that it is safe to trigger a renew_cred\r
-                   call here without checking if there's an imminent\r
-                   renew_identity call. */\r
-                _report_cs1(KHERR_DEBUG_1, L"Renewing credential %1!p!",\r
-                            _cptr(khui_timers[i].key));\r
-                khm_cred_renew_cred(khui_timers[i].key);\r
-                khui_timers[i].flags |= KHUI_TE_FLAG_EXPIRED;\r
-                break;\r
-\r
-            default:\r
-                if (t < KHUI_N_TTYPES) {\r
-                    tmr_count[t]++;\r
-                    if (tmr_offset[t] == 0 ||\r
-                        tmr_offset[t] > khui_timers[i].offset)\r
-                        tmr_offset[t] = khui_timers[i].offset;\r
-                    if (next_event == 0 ||\r
-                        next_event > \r
-                        khui_timers[i].expire + khui_timers[i].offset)\r
-                        next_event = khui_timers[i].expire +\r
-                            khui_timers[i].offset;\r
-\r
-                    if (eff_ident == NULL &&\r
-                        (t == KHUI_TTYPE_ID_EXP ||\r
-                         t == KHUI_TTYPE_ID_CRIT ||\r
-                         t == KHUI_TTYPE_ID_WARN)) {\r
-                        /* we don't need a hold since we will be done\r
-                           with the handle before the marker is\r
-                           expired (the marker is the timer with the\r
-                           KHUI_TTYPE_ID_MARK which contains a held\r
-                           handle and is not really a timer.) */\r
-                        eff_ident = khui_timers[i].key;\r
-                        eff_type = t;\r
-                    }\r
-\r
-                    fire_count++;\r
-\r
-                    khui_timers[i].flags |= KHUI_TE_FLAG_EXPIRED;\r
-                }\r
-                else {\r
-#ifdef DEBUG\r
-                    assert(FALSE);\r
-#endif\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-    /* See if we have anything to do */\r
-    if (next_event == 0)\r
-        return;\r
-    else {\r
-        wchar_t fmt[128];\r
-        wchar_t wtime[128];\r
-        wchar_t wmsg[256];\r
-        wchar_t wtitle[64];\r
-        khm_int64 second;\r
-        khui_alert * alert = NULL;\r
-\r
-        khm_size cb;\r
-\r
-        next_event -= curtime;\r
-\r
-        /* Due to measurement errors we may be slightly off on our\r
-           next_event calculation which shows up as '4 mins 59\r
-           seconds' instead of '5 mins' and so on when converting to a\r
-           string.  So we add half a second to make the message\r
-           neater. */\r
-        TimetToFileTimeInterval(1, &ft);\r
-        second = FtToInt(&ft);\r
-        next_event += second / 2;\r
-\r
-        cb = sizeof(wtime);\r
-        ft = IntToFt(next_event);\r
-        FtIntervalToString(&ft,\r
-                           wtime,\r
-                           &cb);\r
-\r
-        if (fire_count == 1 &&\r
-            eff_ident != NULL &&\r
-            (eff_type == KHUI_TTYPE_ID_EXP ||\r
-             eff_type == KHUI_TTYPE_ID_CRIT ||\r
-             eff_type == KHUI_TTYPE_ID_WARN)) {\r
-\r
-            wchar_t idname[KCDB_IDENT_MAXCCH_NAME];\r
-\r
-            cb = sizeof(idname);\r
-            kcdb_identity_get_name(eff_ident, idname, &cb);\r
-\r
-            if (next_event < second) {\r
-                LoadString(khm_hInstance, IDS_WARN_EXPIRED_ID,\r
-                           fmt, ARRAYLENGTH(fmt));\r
-\r
-                StringCbPrintf(wmsg, sizeof(wmsg), fmt, idname);\r
-            } else {\r
-                LoadString(khm_hInstance, IDS_WARN_EXPIRE_ID,\r
-                           fmt, ARRAYLENGTH(fmt));\r
-\r
-                StringCbPrintf(wmsg, sizeof(wmsg), fmt, idname, wtime);\r
-            }\r
-        } else {\r
-            if (next_event < second) {\r
-                LoadString(khm_hInstance, IDS_WARN_EXPIRED,\r
-                           wmsg, ARRAYLENGTH(wmsg));\r
-            } else {\r
-                LoadString(khm_hInstance, IDS_WARN_EXPIRE, \r
-                           fmt, ARRAYLENGTH(fmt));\r
-\r
-                StringCbPrintf(wmsg, sizeof(wmsg), fmt, wtime);\r
-            }\r
-        }\r
-\r
-        LoadString(khm_hInstance, IDS_WARN_TITLE,\r
-                   wtitle, ARRAYLENGTH(wtitle));\r
-\r
-        khui_alert_create_simple(wtitle, wmsg, KHERR_WARNING, &alert);\r
-        khui_alert_set_flags(alert,\r
-                             KHUI_ALERT_FLAG_REQUEST_BALLOON | KHUI_ALERT_FLAG_DISPATCH_CMD,\r
-                             KHUI_ALERT_FLAG_REQUEST_BALLOON | KHUI_ALERT_FLAG_DISPATCH_CMD);\r
-\r
-        if (eff_ident != NULL) {\r
-            khm_int32 cmd;\r
-\r
-            cmd = khm_get_identity_new_creds_action(eff_ident);\r
-\r
-            if (cmd) {\r
-                khui_alert_add_command(alert, cmd);\r
-                khui_alert_add_command(alert, KHUI_PACTION_CLOSE);\r
-            }\r
-        }\r
-\r
-        khui_alert_show(alert);\r
-        khui_alert_release(alert);\r
-    }\r
-\r
-    _end_task();\r
-\r
-}\r
-\r
-void \r
-khm_timer_fire(HWND hwnd) {\r
-    EnterCriticalSection(&cs_timers);\r
-    tmr_fire_timer();\r
-    LeaveCriticalSection(&cs_timers);\r
-\r
-    khm_timer_refresh(hwnd);\r
-}\r
-\r
-static int\r
-tmr_update(khm_handle key, khui_timer_type type, __int64 expire,\r
-           __int64 offset, void * data, khm_boolean reinstate) {\r
-    int i;\r
-    wchar_t name[KCDB_MAXCCH_NAME];\r
-    wchar_t tstamp[128];\r
-    wchar_t *type_str = NULL;\r
-    SYSTEMTIME st;\r
-    FILETIME ft;\r
-    FILETIME ftl;\r
-    khm_size cb;\r
-\r
-    switch(type) {\r
-    case KHUI_TTYPE_ID_MARK:\r
-        type_str = L"marker";\r
-        break;\r
-\r
-    case KHUI_TTYPE_CRED_WARN:\r
-    case KHUI_TTYPE_ID_WARN:\r
-        type_str = L"warning";\r
-        break;\r
-\r
-    case KHUI_TTYPE_CRED_CRIT:\r
-    case KHUI_TTYPE_ID_CRIT:\r
-        type_str = L"critical";\r
-        break;\r
-\r
-    case KHUI_TTYPE_CRED_EXP:\r
-    case KHUI_TTYPE_ID_EXP:\r
-        type_str = L"expiry";\r
-        break;\r
-\r
-    case KHUI_TTYPE_CRED_RENEW:\r
-    case KHUI_TTYPE_ID_RENEW:\r
-        type_str = L"renew";\r
-        break;\r
-    }\r
-\r
-    ft = IntToFt(expire);\r
-    FileTimeToLocalFileTime(&ft, &ftl);\r
-    FileTimeToSystemTime(&ftl, &st);\r
-    StringCbPrintf(tstamp, sizeof(tstamp),\r
-                   L"%d-%d-%d %d:%d:%d",\r
-                   st.wYear, st.wMonth, st.wDay,\r
-                   st.wHour, st.wMinute, st.wSecond);\r
-\r
-    cb = sizeof(name); name[0] = L'\0';\r
-    if (type_str == NULL) {\r
-\r
-        _report_cs2(KHERR_DEBUG_1,\r
-                    L"Updating uknown timer of type %1!d! exp(%2!s!)",\r
-                    _int32(type),\r
-                    _cstr(tstamp));\r
-        _resolve();\r
-\r
-    } else if (type == KHUI_TTYPE_ID_MARK ||\r
-               type == KHUI_TTYPE_ID_WARN ||\r
-               type == KHUI_TTYPE_ID_CRIT ||\r
-               type == KHUI_TTYPE_ID_EXP ||\r
-               type == KHUI_TTYPE_ID_RENEW) {\r
-\r
-        kcdb_identity_get_name(key, name, &cb);\r
-        _report_cs3(KHERR_DEBUG_1,\r
-                    L"Updating identity %1!s! timer for %2!s! exp(%3!s!)",\r
-                    _cstr(type_str),\r
-                    _cstr(name),\r
-                    _cstr(tstamp));\r
-        _resolve();\r
-\r
-    } else if (type == KHUI_TTYPE_CRED_RENEW ||\r
-               type == KHUI_TTYPE_CRED_WARN ||\r
-               type == KHUI_TTYPE_CRED_CRIT ||\r
-               type == KHUI_TTYPE_CRED_EXP) {\r
-\r
-        kcdb_cred_get_name(key, name, &cb);\r
-        _report_cs3(KHERR_DEBUG_1,\r
-                    L"Updating credential %1!s! timer for %2!s! exp(%3!s!)",\r
-                    _cstr(type_str),\r
-                    _cstr(name),\r
-                    _cstr(tstamp));\r
-        _resolve();\r
-\r
-    }\r
-\r
-    for (i=0; i < (int) khui_n_timers; i++) {\r
-        if (khui_timers[i].key == key &&\r
-            khui_timers[i].type == type)\r
-            break;\r
-    }\r
-\r
-    if (i >= (int) khui_n_timers) {\r
-        i = (int) khui_n_timers;\r
-\r
-        if (i >= (int) khui_nc_timers) {\r
-            khui_timer_event * nt;\r
-#ifdef DEBUG\r
-            assert(khui_timers);\r
-#endif\r
-            khui_nc_timers = UBOUNDSS(i+1, KHUI_TIMER_ALLOC_INCR,\r
-                                      KHUI_TIMER_ALLOC_INCR);\r
-            nt = PMALLOC(sizeof(*nt) * khui_nc_timers);\r
-#ifdef DEBUG\r
-            assert(nt);\r
-#endif\r
-            memcpy(nt, khui_timers, sizeof(*nt) * khui_n_timers);\r
-\r
-            PFREE(khui_timers);\r
-            khui_timers = nt;\r
-        }\r
-\r
-        khui_timers[i].key = key;\r
-        khui_timers[i].type = type;\r
-        khui_timers[i].flags = 0;\r
-        khui_n_timers++;\r
-    }\r
-\r
-    khui_timers[i].expire = expire;\r
-    khui_timers[i].offset = offset;\r
-    khui_timers[i].data = data;\r
-\r
-    khui_timers[i].flags &= ~KHUI_TE_FLAG_STALE;\r
-    if (reinstate)\r
-        khui_timers[i].flags &= ~KHUI_TE_FLAG_EXPIRED;\r
-\r
-    return i;\r
-}\r
-\r
-/* called with cs_timers held */\r
-static int\r
-tmr_find(khm_handle key, khui_timer_type type,\r
-         khm_int32 and_flags, khm_int32 eq_flags) {\r
-    int i;\r
-\r
-    eq_flags &= and_flags;\r
-\r
-    for (i=0; i < (int) khui_n_timers; i++) {\r
-        if (khui_timers[i].key == key &&\r
-            khui_timers[i].type == type &&\r
-            (khui_timers[i].flags & and_flags) == eq_flags)\r
-            break;\r
-    }\r
-\r
-    if (i < (int) khui_n_timers)\r
-        return i;\r
-    else\r
-        return -1;\r
-}\r
-\r
-/* called with cs_timers held. */\r
-static FILETIME\r
-tmr_next_halflife_timeout(int idx, FILETIME * issue, FILETIME * expire) {\r
-    FILETIME lifetime;\r
-    FILETIME current;\r
-    FILETIME ret;\r
-\r
-    khm_int64 ilife;\r
-    khm_int64 icurrent;\r
-    khm_int64 iexpire;\r
-\r
-    khm_int64 iret;\r
-\r
-    GetSystemTimeAsFileTime(&current);\r
-\r
-    /* wha?? */\r
-    if (CompareFileTime(issue, expire) >= 0)\r
-        return current;\r
-\r
-    lifetime = FtSub(expire, issue);\r
-    icurrent = FtToInt(&current);\r
-    iexpire = FtToInt(expire);\r
-\r
-    ilife = FtToInt(&lifetime);\r
-\r
-    while(ilife / 2 > FT_MIN_HALFLIFE_INTERVAL) {\r
-        ilife /= 2;\r
-\r
-        /* is this the next renewal time? */\r
-        if (iexpire - ilife > icurrent) {\r
-            if (idx >= 0 &&\r
-                khui_timers[idx].expire == iexpire - ilife &&\r
-                (khui_timers[idx].flags & KHUI_TE_FLAG_EXPIRED)) {\r
-\r
-                /* if this renewal time has already been triggered\r
-                   (note that when the timer fires, it also fires all\r
-                   events that are within a few seconds of the current\r
-                   time) then we need to set the alarm for the next\r
-                   slot down the line. */\r
-\r
-                continue;\r
-\r
-            } else {\r
-                break;\r
-            }\r
-        }\r
-    }\r
-\r
-    iret = iexpire - ilife;\r
-\r
-    ret = IntToFt(iret);\r
-\r
-    /* if the previous renew timer had fired, we need to mark it as\r
-       not expired.  However, we leave it to the caller to update the\r
-       actual timer and mark it as not stale. */\r
-    if (idx >= 0 &&\r
-        khui_timers[idx].expire < iret) {\r
-\r
-        khui_timers[idx].flags &= ~KHUI_TE_FLAG_EXPIRED;\r
-        khui_timers[idx].expire = iret;\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-/* called with cs_timers held.  Called once for each credential in the\r
-   root credentials set. */\r
-static khm_int32 KHMAPI\r
-tmr_cred_apply_proc(khm_handle cred, void * rock) {\r
-    khm_handle ident = NULL;\r
-    int mark_idx;\r
-    int idx;\r
-    FILETIME ft_expiry;\r
-    FILETIME ft_current;\r
-    FILETIME ft_creinst;\r
-    FILETIME ft_cred_expiry;\r
-    FILETIME ft_cred_issue;\r
-    FILETIME ft_issue;\r
-    FILETIME ft;\r
-    FILETIME fte;\r
-    FILETIME ft_reinst;\r
-    khm_size cb;\r
-    wchar_t wname[KCDB_MAXCCH_NAME];\r
-\r
-    cb = sizeof(wname);\r
-    wname[0] = L'\0';\r
-    kcdb_cred_get_name(cred, wname, &cb);\r
-\r
-    _report_cs1(KHERR_DEBUG_1, L"Looking at cred [%1!s!]",\r
-                _cstr(wname));\r
-    _resolve();\r
-\r
-    kcdb_cred_get_identity(cred, &ident);\r
-#ifdef DEBUG\r
-    assert(ident);\r
-#endif\r
-\r
-    /* now get the expiry for the identity*/\r
-    cb = sizeof(ft_expiry);\r
-    if (KHM_FAILED(kcdb_identity_get_attr(ident, KCDB_ATTR_EXPIRE,\r
-                                          NULL,\r
-                                          &ft_expiry, &cb))) {\r
-\r
-        /* failing which, we get the expiry for this credential */\r
-        cb = sizeof(ft_expiry);\r
-        if (KHM_FAILED(kcdb_cred_get_attr(cred, KCDB_ATTR_EXPIRE,\r
-                                          NULL,\r
-                                          &ft_expiry, &cb))) {\r
-            /* we don't have an expiry time to work with */\r
-            _report_cs1(KHERR_DEBUG_1, L"Skipping cred [%1!s!].  No expiry time",\r
-                        _cstr(wname));\r
-            _resolve();\r
-\r
-            kcdb_identity_release(ident);\r
-            return KHM_ERROR_SUCCESS;\r
-        } else {\r
-            /* and the time of issue */\r
-            cb = sizeof(ft_issue);\r
-            if (KHM_FAILED(kcdb_cred_get_attr(cred, KCDB_ATTR_ISSUE,\r
-                                              NULL, &ft_issue, &cb)))\r
-                ZeroMemory(&ft_issue, sizeof(ft_issue));\r
-        }\r
-\r
-    } else {\r
-        /* also try to get the time of issue. */\r
-        cb = sizeof(ft_issue);\r
-        if (KHM_FAILED(kcdb_identity_get_attr(ident, KCDB_ATTR_ISSUE,\r
-                                              NULL, &ft_issue, &cb)))\r
-            /* if we fail, we just zero out the time of issue and\r
-               failover to using the threshold value to set the expiry\r
-               timer instead of the half life algorithm. */\r
-            ZeroMemory(&ft_issue, sizeof(ft_issue));\r
-    }\r
-\r
-    /* and the current time */\r
-    GetSystemTimeAsFileTime(&ft_current);\r
-\r
-    TimetToFileTimeInterval(KHUI_TIMEEQ_ERROR, &ft_reinst);\r
-\r
-    ft_creinst = FtAdd(&ft_current, &ft_reinst);\r
-\r
-    mark_idx = tmr_find(ident, KHUI_TTYPE_ID_MARK, 0, 0);\r
-\r
-    if (mark_idx < 0) {\r
-        mark_idx = tmr_update(ident, KHUI_TTYPE_ID_MARK, 0, 0, 0, FALSE);\r
-        kcdb_identity_hold(ident);\r
-#ifdef DEBUG\r
-        assert(mark_idx >= 0);\r
-#endif\r
-        khui_timers[mark_idx].flags |= KHUI_TE_FLAG_STALE;\r
-    }\r
-\r
-    if (khui_timers[mark_idx].flags & KHUI_TE_FLAG_STALE) {\r
-        /* first time we are touching this */\r
-        khm_handle csp_cw = NULL;\r
-        khm_handle csp_id = NULL;\r
-        khm_int32 rv;\r
-        khm_int32 t;\r
-        khm_boolean do_warn = TRUE;\r
-        khm_boolean do_crit = TRUE;\r
-        khm_boolean do_renew = TRUE;\r
-        khm_boolean do_halflife = TRUE;\r
-        khm_boolean renew_done = FALSE;\r
-        khm_boolean monitor = TRUE;\r
-        khm_int32 to_warn = KHUI_DEF_TIMEOUT_WARN;\r
-        khm_int32 to_crit = KHUI_DEF_TIMEOUT_CRIT;\r
-        khm_int32 to_renew = KHUI_DEF_TIMEOUT_RENEW;\r
-\r
-        if (CompareFileTime(&ft_expiry, &ft_current) < 0)\r
-            /* already expired */\r
-            goto _done_with_ident;\r
-\r
-        rv = khc_open_space(NULL, L"CredWindow", KHM_PERM_READ, \r
-                            &csp_cw);\r
-\r
-        assert(KHM_SUCCEEDED(rv));\r
-\r
-        rv = kcdb_identity_get_config(ident, KHM_PERM_READ, &csp_id);\r
-        if (KHM_SUCCEEDED(rv)) {\r
-            khc_shadow_space(csp_id, csp_cw);\r
-            khc_close_space(csp_cw);\r
-        } else {\r
-            csp_id = csp_cw;\r
-        }\r
-        csp_cw = NULL;\r
-\r
-        rv = khc_read_int32(csp_id, L"Monitor", &t);\r
-        if (KHM_SUCCEEDED(rv))\r
-            monitor = t;\r
-\r
-        rv = khc_read_int32(csp_id, L"AllowWarn", &t);\r
-        if (KHM_SUCCEEDED(rv))\r
-            do_warn = t;\r
-\r
-        rv = khc_read_int32(csp_id, L"AllowCritical", &t);\r
-        if (KHM_SUCCEEDED(rv)) \r
-            do_crit = t;\r
-\r
-        rv = khc_read_int32(csp_id, L"AllowAutoRenew", &t);\r
-        if (KHM_SUCCEEDED(rv))\r
-            do_renew = t;\r
-\r
-        rv = khc_read_int32(csp_id, L"RenewAtHalfLife", &t);\r
-        if (KHM_SUCCEEDED(rv))\r
-            do_halflife = t;\r
-\r
-        rv = khc_read_int32(csp_id, L"WarnThreshold", &t);\r
-        if (KHM_SUCCEEDED(rv))\r
-            to_warn = t;\r
-\r
-        rv = khc_read_int32(csp_id, L"CriticalThreshold", &t);\r
-        if (KHM_SUCCEEDED(rv))\r
-            to_crit = t;\r
-\r
-        rv = khc_read_int32(csp_id, L"AutoRenewThreshold", &t);\r
-        if (KHM_SUCCEEDED(rv))\r
-            to_renew = t;\r
-\r
-        khc_close_space(csp_id);\r
-\r
-        if (monitor && do_renew) {\r
-            int prev;\r
-\r
-            TimetToFileTimeInterval(to_renew, &ft);\r
-\r
-            prev =\r
-                tmr_find(ident, KHUI_TTYPE_ID_RENEW, 0, 0);\r
-\r
-            if (do_halflife && (ft_issue.dwLowDateTime != 0 ||\r
-                                ft_issue.dwHighDateTime != 0))\r
-                fte = tmr_next_halflife_timeout(prev, &ft_issue, &ft_expiry);\r
-            else\r
-                fte = FtSub(&ft_expiry, &ft);\r
-\r
-            /* we set off a renew notification immediately if the\r
-               renew threshold has passed but a renew was never sent.\r
-               This maybe because that NetIDMgr was started at the\r
-               last minute, or because for some reason the renew timer\r
-               could not be triggered earlier. */\r
-\r
-            if (CompareFileTime(&fte, &ft_current) > 0 ||\r
-                prev == -1 ||\r
-                !(khui_timers[prev].flags & KHUI_TE_FLAG_EXPIRED)) {\r
-\r
-                if (CompareFileTime(&fte, &ft_current) < 0)\r
-                    fte = ft_current;\r
-\r
-                tmr_update(ident, KHUI_TTYPE_ID_RENEW, \r
-                           FtToInt(&fte), FtToInt(&ft), 0,\r
-                           CompareFileTime(&fte,&ft_creinst) > 0);\r
-                renew_done = TRUE;\r
-\r
-            } else {\r
-\r
-                /* special case.  If the renew timer was in the past\r
-                   and it was expired, then we retain the record as\r
-                   long as the credentials are around.  If the renewal\r
-                   failed we don't want to automatically retry\r
-                   everytime we check the timers. */\r
-\r
-                tmr_update(ident, KHUI_TTYPE_ID_RENEW,\r
-                           FtToInt(&fte), FtToInt(&ft), 0, FALSE);\r
-\r
-            }\r
-        }\r
-\r
-        if (monitor && do_warn && !renew_done) {\r
-\r
-            TimetToFileTimeInterval(to_warn, &ft);\r
-            fte = FtSub(&ft_expiry, &ft);\r
-\r
-            if (CompareFileTime(&fte, &ft_current) > 0)\r
-                tmr_update(ident, KHUI_TTYPE_ID_WARN,\r
-                           FtToInt(&fte), FtToInt(&ft), 0,\r
-                           CompareFileTime(&fte, &ft_creinst) > 0);\r
-        }\r
-\r
-        if (monitor && do_crit && !renew_done) {\r
-            TimetToFileTimeInterval(to_crit, &ft);\r
-            fte = FtSub(&ft_expiry, &ft);\r
-\r
-            if (CompareFileTime(&fte, &ft_current) > 0)\r
-                tmr_update(ident, KHUI_TTYPE_ID_CRIT,\r
-                           FtToInt(&fte), FtToInt(&ft), 0,\r
-                           CompareFileTime(&fte, &ft_creinst) > 0);\r
-        }\r
-\r
-        if (monitor && !renew_done) {\r
-            if (CompareFileTime(&ft_expiry, &ft_current) > 0)\r
-                tmr_update(ident, KHUI_TTYPE_ID_EXP, \r
-                           FtToInt(&ft_expiry), 0, 0,\r
-                           CompareFileTime(&fte, &ft_creinst) > 0);\r
-        }\r
-\r
-    _done_with_ident:\r
-        khui_timers[mark_idx].flags &= ~KHUI_TE_FLAG_STALE;\r
-    }\r
-\r
-    cb = sizeof(ft_cred_expiry);\r
-    if (KHM_FAILED(kcdb_cred_get_attr(cred, KCDB_ATTR_EXPIRE,\r
-                                      NULL,\r
-                                      &ft_cred_expiry,\r
-                                      &cb))) {\r
-        _report_cs1(KHERR_DEBUG_1, L"Skipping cred [%1!s!]. Can't lookup cred expiry",\r
-                    _cstr(wname));\r
-        _resolve();\r
-        goto _cleanup;\r
-    }\r
-\r
-    cb = sizeof(ft_cred_issue);\r
-    if (KHM_FAILED(kcdb_cred_get_attr(cred, KCDB_ATTR_ISSUE,\r
-                                      NULL,\r
-                                      &ft_cred_issue,\r
-                                      &cb))) {\r
-\r
-        ZeroMemory(&ft_cred_issue, sizeof(ft_cred_issue));\r
-\r
-    }\r
-\r
-    TimetToFileTimeInterval(KHUI_TIMEEQ_ERROR, &ft);\r
-\r
-    {\r
-        /* if the credential has a longer lifetime than the identity,\r
-           or it expires within KHUI_TIMEEQ_ERROR seconds of the\r
-           identity, then we don't need to set any alerts for this\r
-           credential. */\r
-\r
-        FILETIME ft_delta;\r
-\r
-        ft_delta = FtSub(&ft_expiry, &ft_cred_expiry);\r
-\r
-        if (CompareFileTime(&ft_cred_expiry, &ft_expiry) >= 0 ||\r
-            CompareFileTime(&ft_delta, &ft) < 0) {\r
-\r
-            _report_cs1(KHERR_DEBUG_1,\r
-                        L"Skipping credential [%1!s!].  The expiry time is too close to the identity expiry.",\r
-                        _cstr(wname));\r
-            _resolve();\r
-            goto _cleanup;\r
-        }\r
-    }\r
-\r
-    if ((idx = tmr_find(ident, KHUI_TTYPE_ID_WARN, 0, 0)) >= 0 &&\r
-        !(khui_timers[idx].flags & KHUI_TE_FLAG_STALE)) {\r
-\r
-        fte = IntToFt(FtToInt(&ft_cred_expiry) - khui_timers[idx].offset);\r
-        if (CompareFileTime(&fte, &ft_current) > 0) {\r
-           tmr_update(cred, KHUI_TTYPE_CRED_WARN,\r
-                      FtToInt(&fte), \r
-                      khui_timers[idx].offset, 0,\r
-                      CompareFileTime(&fte, &ft_creinst) > 0);\r
-           kcdb_cred_hold(cred);\r
-       }\r
-    }\r
-\r
-    if ((idx = tmr_find(ident, KHUI_TTYPE_ID_CRIT, 0, 0)) >= 0 &&\r
-        !(khui_timers[idx].flags & KHUI_TE_FLAG_STALE)) {\r
-\r
-        fte = IntToFt(FtToInt(&ft_cred_expiry) - khui_timers[idx].offset);\r
-        if (CompareFileTime(&fte, &ft_current) > 0) {\r
-            tmr_update(cred, KHUI_TTYPE_CRED_CRIT,\r
-                       FtToInt(&fte),\r
-                       khui_timers[idx].offset, 0,\r
-                       CompareFileTime(&fte, &ft_creinst) > 0);\r
-            kcdb_cred_hold(cred);\r
-        }\r
-    }\r
-\r
-    if ((idx = tmr_find(ident, KHUI_TTYPE_ID_RENEW, 0, 0)) >= 0 &&\r
-        !(khui_timers[idx].flags & KHUI_TE_FLAG_STALE)) {\r
-\r
-        int cidx = tmr_find(cred, KHUI_TTYPE_CRED_RENEW, 0, 0);\r
-\r
-        if (ft_cred_issue.dwLowDateTime == 0 &&\r
-            ft_cred_issue.dwHighDateTime == 0) {\r
-            fte = IntToFt(FtToInt(&ft_cred_expiry) - khui_timers[idx].offset);\r
-            /* a special case, for a credential whose remaining\r
-               lifetime is less than the offset, we try half life on\r
-               the current time and the expiry. */\r
-            if (CompareFileTime(&fte, &ft_current) <= 0 &&\r
-                CompareFileTime(&ft_current, &ft_expiry) < 0) {\r
-                fte = tmr_next_halflife_timeout(cidx, &ft_current, &ft_cred_expiry);\r
-#if 0\r
-                /* now, if we already have a renew timer for this\r
-                   credential that hasn't expired yet and that is set\r
-                   for earlier than fte, we let it be. */\r
-                if (cidx >= 0 &&\r
-                    khui_timers[cidx].expire < FtToInt(&fte) &&\r
-                    khui_timers[cidx].expire > FtToInt(&ft_current) &&\r
-                    !(khui_timers[cidx].flags & KHUI_TE_FLAG_EXPIRED)) {\r
-\r
-                    fte = IntToFt(khui_timers[cidx].expire);\r
-\r
-                }\r
-#endif\r
-            }\r
-        } else {\r
-            fte = tmr_next_halflife_timeout(cidx, &ft_cred_issue, &ft_cred_expiry);\r
-        }\r
-\r
-        if (CompareFileTime(&fte, &ft_current) > 0) {\r
-            tmr_update(cred, KHUI_TTYPE_CRED_RENEW,\r
-                       FtToInt(&fte),\r
-                       khui_timers[idx].offset, 0,\r
-                       CompareFileTime(&fte, &ft_creinst) > 0);\r
-            kcdb_cred_hold(cred);\r
-        }\r
-    }\r
-\r
-    if ((idx = tmr_find(ident, KHUI_TTYPE_ID_EXP, 0, 0)) >= 0 &&\r
-        !(khui_timers[idx].flags & KHUI_TE_FLAG_STALE)) {\r
-\r
-        if (CompareFileTime(&ft_cred_expiry, &ft_current) > 0) {\r
-            tmr_update(cred, KHUI_TTYPE_CRED_EXP,\r
-                       FtToInt(&ft_cred_expiry),\r
-                       0, 0,\r
-                       CompareFileTime(&ft_cred_expiry, &ft_creinst) > 0);\r
-        }\r
-    }\r
-\r
- _cleanup:\r
-\r
-    if (ident)\r
-        kcdb_identity_release(ident);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* called with cs_timers held */\r
-static void\r
-tmr_purge(void) {\r
-    int i, j;\r
-\r
-    for (i=0,j=0; i < (int) khui_n_timers; i++) {\r
-        if (khui_timers[i].flags & KHUI_TE_FLAG_STALE) {\r
-            if (khui_timers[i].type == KHUI_TTYPE_ID_MARK) {\r
-                kcdb_identity_release(khui_timers[i].key);\r
-#ifdef DEBUG\r
-                {\r
-                    int idx;\r
-\r
-                    idx = tmr_find(khui_timers[i].key, \r
-                                   KHUI_TTYPE_ID_CRIT, 0, 0);\r
-                    assert(idx < 0 || \r
-                           (khui_timers[idx].flags & \r
-                            KHUI_TE_FLAG_STALE));\r
-\r
-                    idx = tmr_find(khui_timers[i].key, \r
-                                   KHUI_TTYPE_ID_RENEW, 0, 0);\r
-                    assert(idx < 0 || \r
-                           (khui_timers[idx].flags & \r
-                            KHUI_TE_FLAG_STALE));\r
-\r
-                    idx = tmr_find(khui_timers[i].key, \r
-                                   KHUI_TTYPE_ID_WARN, 0, 0);\r
-                    assert(idx < 0 || \r
-                           (khui_timers[idx].flags & \r
-                            KHUI_TE_FLAG_STALE));\r
-\r
-                    idx = tmr_find(khui_timers[i].key, \r
-                                   KHUI_TTYPE_ID_EXP, 0, 0);\r
-                    assert(idx < 0 || \r
-                           (khui_timers[idx].flags & \r
-                            KHUI_TE_FLAG_STALE));\r
-                }\r
-#endif\r
-            } else if (khui_timers[i].type == KHUI_TTYPE_CRED_WARN ||\r
-                       khui_timers[i].type == KHUI_TTYPE_CRED_CRIT ||\r
-                       khui_timers[i].type == KHUI_TTYPE_CRED_RENEW ||\r
-                       khui_timers[i].type == KHUI_TTYPE_CRED_EXP) {\r
-                kcdb_cred_release(khui_timers[i].key);\r
-            }\r
-        } else {\r
-            if (i != j)\r
-                khui_timers[j] = khui_timers[i];\r
-            j++;\r
-        }\r
-    }\r
-\r
-    khui_n_timers = j;\r
-}\r
-\r
-/* go through all the credentials and set timers as appropriate.  hwnd\r
-   is the window that will receive the timer events.*/\r
-void \r
-khm_timer_refresh(HWND hwnd) {\r
-    int i;\r
-    khm_int64 next_event = 0;\r
-    khm_int64 curtime;\r
-    khm_int64 diff;\r
-\r
-    _begin_task(0);\r
-    _report_cs0(KHERR_DEBUG_1, L"Refreshing timers");\r
-    _describe();\r
-\r
-    EnterCriticalSection(&cs_timers);\r
-\r
-    KillTimer(hwnd, KHUI_TRIGGER_TIMER_ID);\r
-\r
-    /* When refreshing timers, we go through all of them and mark them\r
-       as stale.  Then we go through the credentials in the root\r
-       credential set and add or refresh the timers associated with\r
-       each identity and credential.  Once this is done, we remove the\r
-       timers that are still stale, since they are no longer in\r
-       use. */\r
-\r
-    for (i=0; i < (int) khui_n_timers; i++) {\r
-#ifdef NOT_IMPLEMENTED_YET\r
-        if (khui_timers[i].type == KHUI_TTYPE_BMSG ||\r
-            khui_timers[i].type == KHUI_TTYPE_SMSG) {\r
-            khui_timers[i].flags &= ~KHUI_TE_FLAG_STALE;\r
-        } else {\r
-#endif\r
-\r
-            khui_timers[i].flags |= KHUI_TE_FLAG_STALE;\r
-\r
-#ifdef NOT_IMPLEMENTED_YET\r
-       }\r
-#endif\r
-    }\r
-\r
-    _report_cs1(KHERR_DEBUG_1, L"Starting with %1!d! timers",\r
-                _int32(khui_n_timers));\r
-\r
-    kcdb_credset_apply(NULL,\r
-                       tmr_cred_apply_proc,\r
-                       NULL);\r
-\r
-    tmr_purge();\r
-\r
-    _report_cs1(KHERR_DEBUG_1, L"Leaving with %1!d! timers",\r
-                _int32(khui_n_timers));\r
-\r
- _check_next_event:\r
-\r
-    /* Before we return, we should check if any timers are set to\r
-       expire right now.  If there are, we should fire the timer\r
-       before returning. */\r
-\r
-    next_event = 0;\r
-    for (i=0; i < (int) khui_n_timers; i++) {\r
-        if (!(khui_timers[i].flags & KHUI_TE_FLAG_EXPIRED) &&\r
-            khui_timers[i].type != KHUI_TTYPE_ID_MARK &&\r
-            (next_event == 0 ||\r
-             next_event > khui_timers[i].expire)) {\r
-\r
-            next_event = khui_timers[i].expire;\r
-\r
-        }\r
-    }\r
-\r
-    if (next_event != 0) {\r
-        FILETIME ft;\r
-\r
-        GetSystemTimeAsFileTime(&ft);\r
-        curtime = FtToInt(&ft);\r
-\r
-        TimetToFileTimeInterval(KHUI_TIMEEQ_ERROR_SMALL, &ft);\r
-        diff = FtToInt(&ft);\r
-\r
-        if (curtime + diff > next_event) {\r
-            tmr_fire_timer();\r
-            goto _check_next_event;\r
-        } else {\r
-            diff = next_event - curtime;\r
-            ft = IntToFt(diff);\r
-            SetTimer(hwnd,\r
-                     KHUI_TRIGGER_TIMER_ID,\r
-                     FtIntervalToMilliseconds(&ft),\r
-                     NULL);\r
-        }\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_timers);\r
-\r
-    _end_task();\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+
+/* The minimum half time interval is 60 seconds*/
+#define TT_MIN_HALFLIFE_INTERVAL 60
+
+/* as above, in FILETIME units of 100ns */
+#define FT_MIN_HALFLIFE_INTERVAL (TT_MIN_HALFLIFE_INTERVAL * 10000000i64)
+
+/* 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 = PMALLOC(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)
+        PFREE(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;
+    khm_int64 curtime;
+    khm_int64 err;
+    khm_int64 next_event;
+    int     tmr_count[KHUI_N_TTYPES];
+    khm_int64 tmr_offset[KHUI_N_TTYPES];
+    int t;
+    khm_handle eff_ident = NULL;
+    khui_timer_type eff_type = 0; /* meaningless */
+    int fire_count = 0;
+    FILETIME ft;
+
+    _begin_task(0);
+    _report_cs0(KHERR_DEBUG_1, L"Checking for expired timers");
+    _describe();
+
+    TimetToFileTimeInterval(KHUI_TIMEEQ_ERROR_SMALL, &ft);
+    err = FtToInt(&ft);
+    GetSystemTimeAsFileTime(&ft);
+    curtime = FtToInt(&ft);
+
+    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) {
+
+            _report_cs3(KHERR_DEBUG_1, L"Expiring timer index=%1!d!, type=%2!d!, key=%3!p!",
+                        _int32(i), _int32(khui_timers[i].type),
+                        _cptr(khui_timers[i].key));
+
+            t = khui_timers[i].type;
+
+            switch(t) {
+            case KHUI_TTYPE_ID_RENEW:
+                _report_cs1(KHERR_DEBUG_1, L"Renewing identity %1!p!",
+                            _cptr(khui_timers[i].key));
+                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. */
+                _report_cs1(KHERR_DEBUG_1, L"Renewing credential %1!p!",
+                            _cptr(khui_timers[i].key));
+                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;
+                }
+                else {
+#ifdef DEBUG
+                    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];
+        khm_int64 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, &ft);
+        second = FtToInt(&ft);
+        next_event += second / 2;
+
+        cb = sizeof(wtime);
+        ft = IntToFt(next_event);
+        FtIntervalToString(&ft,
+                           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 < 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 < 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_DISPATCH_CMD,
+                             KHUI_ALERT_FLAG_REQUEST_BALLOON | KHUI_ALERT_FLAG_DISPATCH_CMD);
+
+        if (eff_ident != NULL) {
+            khm_int32 cmd;
+
+            cmd = khm_get_identity_new_creds_action(eff_ident);
+
+            if (cmd) {
+                khui_alert_add_command(alert, cmd);
+                khui_alert_add_command(alert, KHUI_PACTION_CLOSE);
+            }
+        }
+
+        khui_alert_show(alert);
+        khui_alert_release(alert);
+    }
+
+    _end_task();
+
+}
+
+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, khm_boolean reinstate) {
+    int i;
+    wchar_t name[KCDB_MAXCCH_NAME];
+    wchar_t tstamp[128];
+    wchar_t *type_str = NULL;
+    SYSTEMTIME st;
+    FILETIME ft;
+    FILETIME ftl;
+    khm_size cb;
+
+    switch(type) {
+    case KHUI_TTYPE_ID_MARK:
+        type_str = L"marker";
+        break;
+
+    case KHUI_TTYPE_CRED_WARN:
+    case KHUI_TTYPE_ID_WARN:
+        type_str = L"warning";
+        break;
+
+    case KHUI_TTYPE_CRED_CRIT:
+    case KHUI_TTYPE_ID_CRIT:
+        type_str = L"critical";
+        break;
+
+    case KHUI_TTYPE_CRED_EXP:
+    case KHUI_TTYPE_ID_EXP:
+        type_str = L"expiry";
+        break;
+
+    case KHUI_TTYPE_CRED_RENEW:
+    case KHUI_TTYPE_ID_RENEW:
+        type_str = L"renew";
+        break;
+    }
+
+    ft = IntToFt(expire);
+    FileTimeToLocalFileTime(&ft, &ftl);
+    FileTimeToSystemTime(&ftl, &st);
+    StringCbPrintf(tstamp, sizeof(tstamp),
+                   L"%d-%d-%d %d:%d:%d",
+                   st.wYear, st.wMonth, st.wDay,
+                   st.wHour, st.wMinute, st.wSecond);
+
+    cb = sizeof(name); name[0] = L'\0';
+    if (type_str == NULL) {
+
+        _report_cs2(KHERR_DEBUG_1,
+                    L"Updating uknown timer of type %1!d! exp(%2!s!)",
+                    _int32(type),
+                    _cstr(tstamp));
+        _resolve();
+
+    } else if (type == KHUI_TTYPE_ID_MARK ||
+               type == KHUI_TTYPE_ID_WARN ||
+               type == KHUI_TTYPE_ID_CRIT ||
+               type == KHUI_TTYPE_ID_EXP ||
+               type == KHUI_TTYPE_ID_RENEW) {
+
+        kcdb_identity_get_name(key, name, &cb);
+        _report_cs3(KHERR_DEBUG_1,
+                    L"Updating identity %1!s! timer for %2!s! exp(%3!s!)",
+                    _cstr(type_str),
+                    _cstr(name),
+                    _cstr(tstamp));
+        _resolve();
+
+    } else if (type == KHUI_TTYPE_CRED_RENEW ||
+               type == KHUI_TTYPE_CRED_WARN ||
+               type == KHUI_TTYPE_CRED_CRIT ||
+               type == KHUI_TTYPE_CRED_EXP) {
+
+        kcdb_cred_get_name(key, name, &cb);
+        _report_cs3(KHERR_DEBUG_1,
+                    L"Updating credential %1!s! timer for %2!s! exp(%3!s!)",
+                    _cstr(type_str),
+                    _cstr(name),
+                    _cstr(tstamp));
+        _resolve();
+
+    }
+
+    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 = PMALLOC(sizeof(*nt) * khui_nc_timers);
+#ifdef DEBUG
+            assert(nt);
+#endif
+            memcpy(nt, khui_timers, sizeof(*nt) * khui_n_timers);
+
+            PFREE(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;
+    if (reinstate)
+        khui_timers[i].flags &= ~KHUI_TE_FLAG_EXPIRED;
+
+    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 FILETIME
+tmr_next_halflife_timeout(int idx, FILETIME * issue, FILETIME * expire) {
+    FILETIME lifetime;
+    FILETIME current;
+    FILETIME ret;
+
+    khm_int64 ilife;
+    khm_int64 icurrent;
+    khm_int64 iexpire;
+
+    khm_int64 iret;
+
+    GetSystemTimeAsFileTime(&current);
+
+    /* wha?? */
+    if (CompareFileTime(issue, expire) >= 0)
+        return current;
+
+    lifetime = FtSub(expire, issue);
+    icurrent = FtToInt(&current);
+    iexpire = FtToInt(expire);
+
+    ilife = FtToInt(&lifetime);
+
+    while(ilife / 2 > FT_MIN_HALFLIFE_INTERVAL) {
+        ilife /= 2;
+
+        /* is this the next renewal time? */
+        if (iexpire - ilife > icurrent) {
+            if (idx >= 0 &&
+                khui_timers[idx].expire == iexpire - ilife &&
+                (khui_timers[idx].flags & KHUI_TE_FLAG_EXPIRED)) {
+
+                /* if this renewal time has already been triggered
+                   (note that when the timer fires, it also fires all
+                   events that are within a few seconds of the current
+                   time) then we need to set the alarm for the next
+                   slot down the line. */
+
+                continue;
+
+            } else {
+                break;
+            }
+        }
+    }
+
+    iret = iexpire - ilife;
+
+    ret = IntToFt(iret);
+
+    /* if the previous renew timer had fired, we need to mark it as
+       not expired.  However, we leave it to the caller to update the
+       actual timer and mark it as not stale. */
+    if (idx >= 0 &&
+        khui_timers[idx].expire < iret) {
+
+        khui_timers[idx].flags &= ~KHUI_TE_FLAG_EXPIRED;
+        khui_timers[idx].expire = iret;
+    }
+
+    return ret;
+}
+
+/* called with cs_timers held.  Called once for each credential in the
+   root credentials set. */
+static khm_int32 KHMAPI
+tmr_cred_apply_proc(khm_handle cred, void * rock) {
+    khm_handle ident = NULL;
+    int mark_idx;
+    int idx;
+    FILETIME ft_expiry;
+    FILETIME ft_current;
+    FILETIME ft_creinst;
+    FILETIME ft_cred_expiry;
+    FILETIME ft_cred_issue;
+    FILETIME ft_issue;
+    FILETIME ft;
+    FILETIME fte;
+    FILETIME ft_reinst;
+    khm_size cb;
+    wchar_t wname[KCDB_MAXCCH_NAME];
+
+    cb = sizeof(wname);
+    wname[0] = L'\0';
+    kcdb_cred_get_name(cred, wname, &cb);
+
+    _report_cs1(KHERR_DEBUG_1, L"Looking at cred [%1!s!]",
+                _cstr(wname));
+    _resolve();
+
+    kcdb_cred_get_identity(cred, &ident);
+#ifdef DEBUG
+    assert(ident);
+#endif
+
+    /* now get the expiry for the identity*/
+    cb = sizeof(ft_expiry);
+    if (KHM_FAILED(kcdb_identity_get_attr(ident, KCDB_ATTR_EXPIRE,
+                                          NULL,
+                                          &ft_expiry, &cb))) {
+
+        /* failing which, we get the expiry for this credential */
+        cb = sizeof(ft_expiry);
+        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 */
+            _report_cs1(KHERR_DEBUG_1, L"Skipping cred [%1!s!].  No expiry time",
+                        _cstr(wname));
+            _resolve();
+
+            kcdb_identity_release(ident);
+            return KHM_ERROR_SUCCESS;
+        } else {
+            /* and the time of issue */
+            cb = sizeof(ft_issue);
+            if (KHM_FAILED(kcdb_cred_get_attr(cred, KCDB_ATTR_ISSUE,
+                                              NULL, &ft_issue, &cb)))
+                ZeroMemory(&ft_issue, sizeof(ft_issue));
+        }
+
+    } else {
+        /* also try to get the time of issue. */
+        cb = sizeof(ft_issue);
+        if (KHM_FAILED(kcdb_identity_get_attr(ident, KCDB_ATTR_ISSUE,
+                                              NULL, &ft_issue, &cb)))
+            /* if we fail, we just zero out the time of issue and
+               failover to using the threshold value to set the expiry
+               timer instead of the half life algorithm. */
+            ZeroMemory(&ft_issue, sizeof(ft_issue));
+    }
+
+    /* and the current time */
+    GetSystemTimeAsFileTime(&ft_current);
+
+    TimetToFileTimeInterval(KHUI_TIMEEQ_ERROR, &ft_reinst);
+
+    ft_creinst = FtAdd(&ft_current, &ft_reinst);
+
+    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, FALSE);
+        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 do_halflife = 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 (CompareFileTime(&ft_expiry, &ft_current) < 0)
+            /* 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"RenewAtHalfLife", &t);
+        if (KHM_SUCCEEDED(rv))
+            do_halflife = 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) {
+            int prev;
+
+            TimetToFileTimeInterval(to_renew, &ft);
+
+            prev =
+                tmr_find(ident, KHUI_TTYPE_ID_RENEW, 0, 0);
+
+            if (do_halflife && (ft_issue.dwLowDateTime != 0 ||
+                                ft_issue.dwHighDateTime != 0))
+                fte = tmr_next_halflife_timeout(prev, &ft_issue, &ft_expiry);
+            else
+                fte = FtSub(&ft_expiry, &ft);
+
+            /* we set off a renew notification immediately if the
+               renew threshold has passed but a renew was never sent.
+               This maybe because that NetIDMgr was started at the
+               last minute, or because for some reason the renew timer
+               could not be triggered earlier. */
+
+            if (CompareFileTime(&fte, &ft_current) > 0 ||
+                prev == -1 ||
+                !(khui_timers[prev].flags & KHUI_TE_FLAG_EXPIRED)) {
+
+                if (CompareFileTime(&fte, &ft_current) < 0)
+                    fte = ft_current;
+
+                tmr_update(ident, KHUI_TTYPE_ID_RENEW, 
+                           FtToInt(&fte), FtToInt(&ft), 0,
+                           CompareFileTime(&fte,&ft_creinst) > 0);
+                renew_done = TRUE;
+
+            } else {
+
+                /* special case.  If the renew timer was in the past
+                   and it was expired, then we retain the record as
+                   long as the credentials are around.  If the renewal
+                   failed we don't want to automatically retry
+                   everytime we check the timers. */
+
+                tmr_update(ident, KHUI_TTYPE_ID_RENEW,
+                           FtToInt(&fte), FtToInt(&ft), 0, FALSE);
+
+            }
+        }
+
+        if (monitor && do_warn && !renew_done) {
+
+            TimetToFileTimeInterval(to_warn, &ft);
+            fte = FtSub(&ft_expiry, &ft);
+
+            if (CompareFileTime(&fte, &ft_current) > 0)
+                tmr_update(ident, KHUI_TTYPE_ID_WARN,
+                           FtToInt(&fte), FtToInt(&ft), 0,
+                           CompareFileTime(&fte, &ft_creinst) > 0);
+        }
+
+        if (monitor && do_crit && !renew_done) {
+            TimetToFileTimeInterval(to_crit, &ft);
+            fte = FtSub(&ft_expiry, &ft);
+
+            if (CompareFileTime(&fte, &ft_current) > 0)
+                tmr_update(ident, KHUI_TTYPE_ID_CRIT,
+                           FtToInt(&fte), FtToInt(&ft), 0,
+                           CompareFileTime(&fte, &ft_creinst) > 0);
+        }
+
+        if (monitor && !renew_done) {
+            if (CompareFileTime(&ft_expiry, &ft_current) > 0)
+                tmr_update(ident, KHUI_TTYPE_ID_EXP, 
+                           FtToInt(&ft_expiry), 0, 0,
+                           CompareFileTime(&fte, &ft_creinst) > 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))) {
+        _report_cs1(KHERR_DEBUG_1, L"Skipping cred [%1!s!]. Can't lookup cred expiry",
+                    _cstr(wname));
+        _resolve();
+        goto _cleanup;
+    }
+
+    cb = sizeof(ft_cred_issue);
+    if (KHM_FAILED(kcdb_cred_get_attr(cred, KCDB_ATTR_ISSUE,
+                                      NULL,
+                                      &ft_cred_issue,
+                                      &cb))) {
+
+        ZeroMemory(&ft_cred_issue, sizeof(ft_cred_issue));
+
+    }
+
+    TimetToFileTimeInterval(KHUI_TIMEEQ_ERROR, &ft);
+
+    {
+        /* if the credential has a longer lifetime than the identity,
+           or it expires within KHUI_TIMEEQ_ERROR seconds of the
+           identity, then we don't need to set any alerts for this
+           credential. */
+
+        FILETIME ft_delta;
+
+        ft_delta = FtSub(&ft_expiry, &ft_cred_expiry);
+
+        if (CompareFileTime(&ft_cred_expiry, &ft_expiry) >= 0 ||
+            CompareFileTime(&ft_delta, &ft) < 0) {
+
+            _report_cs1(KHERR_DEBUG_1,
+                        L"Skipping credential [%1!s!].  The expiry time is too close to the identity expiry.",
+                        _cstr(wname));
+            _resolve();
+            goto _cleanup;
+        }
+    }
+
+    if ((idx = tmr_find(ident, KHUI_TTYPE_ID_WARN, 0, 0)) >= 0 &&
+        !(khui_timers[idx].flags & KHUI_TE_FLAG_STALE)) {
+
+        fte = IntToFt(FtToInt(&ft_cred_expiry) - khui_timers[idx].offset);
+        if (CompareFileTime(&fte, &ft_current) > 0) {
+           tmr_update(cred, KHUI_TTYPE_CRED_WARN,
+                      FtToInt(&fte), 
+                      khui_timers[idx].offset, 0,
+                      CompareFileTime(&fte, &ft_creinst) > 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 = IntToFt(FtToInt(&ft_cred_expiry) - khui_timers[idx].offset);
+        if (CompareFileTime(&fte, &ft_current) > 0) {
+            tmr_update(cred, KHUI_TTYPE_CRED_CRIT,
+                       FtToInt(&fte),
+                       khui_timers[idx].offset, 0,
+                       CompareFileTime(&fte, &ft_creinst) > 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)) {
+
+        int cidx = tmr_find(cred, KHUI_TTYPE_CRED_RENEW, 0, 0);
+
+        if (ft_cred_issue.dwLowDateTime == 0 &&
+            ft_cred_issue.dwHighDateTime == 0) {
+            fte = IntToFt(FtToInt(&ft_cred_expiry) - khui_timers[idx].offset);
+            /* a special case, for a credential whose remaining
+               lifetime is less than the offset, we try half life on
+               the current time and the expiry. */
+            if (CompareFileTime(&fte, &ft_current) <= 0 &&
+                CompareFileTime(&ft_current, &ft_expiry) < 0) {
+                fte = tmr_next_halflife_timeout(cidx, &ft_current, &ft_cred_expiry);
+#if 0
+                /* now, if we already have a renew timer for this
+                   credential that hasn't expired yet and that is set
+                   for earlier than fte, we let it be. */
+                if (cidx >= 0 &&
+                    khui_timers[cidx].expire < FtToInt(&fte) &&
+                    khui_timers[cidx].expire > FtToInt(&ft_current) &&
+                    !(khui_timers[cidx].flags & KHUI_TE_FLAG_EXPIRED)) {
+
+                    fte = IntToFt(khui_timers[cidx].expire);
+
+                }
+#endif
+            }
+        } else {
+            fte = tmr_next_halflife_timeout(cidx, &ft_cred_issue, &ft_cred_expiry);
+        }
+
+        if (CompareFileTime(&fte, &ft_current) > 0) {
+            tmr_update(cred, KHUI_TTYPE_CRED_RENEW,
+                       FtToInt(&fte),
+                       khui_timers[idx].offset, 0,
+                       CompareFileTime(&fte, &ft_creinst) > 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 (CompareFileTime(&ft_cred_expiry, &ft_current) > 0) {
+            tmr_update(cred, KHUI_TTYPE_CRED_EXP,
+                       FtToInt(&ft_cred_expiry),
+                       0, 0,
+                       CompareFileTime(&ft_cred_expiry, &ft_creinst) > 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;
+}
+
+/* go through all the credentials and set timers as appropriate.  hwnd
+   is the window that will receive the timer events.*/
+void 
+khm_timer_refresh(HWND hwnd) {
+    int i;
+    khm_int64 next_event = 0;
+    khm_int64 curtime;
+    khm_int64 diff;
+
+    _begin_task(0);
+    _report_cs0(KHERR_DEBUG_1, L"Refreshing timers");
+    _describe();
+
+    EnterCriticalSection(&cs_timers);
+
+    KillTimer(hwnd, KHUI_TRIGGER_TIMER_ID);
+
+    /* When refreshing timers, we go through all of them and mark them
+       as stale.  Then we go through the credentials in the root
+       credential set and add or refresh the timers associated with
+       each identity and credential.  Once this is done, we remove the
+       timers that are still stale, since they are no longer in
+       use. */
+
+    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;
+
+#ifdef NOT_IMPLEMENTED_YET
+       }
+#endif
+    }
+
+    _report_cs1(KHERR_DEBUG_1, L"Starting with %1!d! timers",
+                _int32(khui_n_timers));
+
+    kcdb_credset_apply(NULL,
+                       tmr_cred_apply_proc,
+                       NULL);
+
+    tmr_purge();
+
+    _report_cs1(KHERR_DEBUG_1, L"Leaving with %1!d! timers",
+                _int32(khui_n_timers));
+
+ _check_next_event:
+
+    /* Before we return, we should check if any timers are set to
+       expire right now.  If there are, we should fire the timer
+       before returning. */
+
+    next_event = 0;
+    for (i=0; i < (int) khui_n_timers; i++) {
+        if (!(khui_timers[i].flags & KHUI_TE_FLAG_EXPIRED) &&
+            khui_timers[i].type != KHUI_TTYPE_ID_MARK &&
+            (next_event == 0 ||
+             next_event > khui_timers[i].expire)) {
+
+            next_event = khui_timers[i].expire;
+
+        }
+    }
+
+    if (next_event != 0) {
+        FILETIME ft;
+
+        GetSystemTimeAsFileTime(&ft);
+        curtime = FtToInt(&ft);
+
+        TimetToFileTimeInterval(KHUI_TIMEEQ_ERROR_SMALL, &ft);
+        diff = FtToInt(&ft);
+
+        if (curtime + diff > next_event) {
+            tmr_fire_timer();
+            goto _check_next_event;
+        } else {
+            diff = next_event - curtime;
+            ft = IntToFt(diff);
+            SetTimer(hwnd,
+                     KHUI_TRIGGER_TIMER_ID,
+                     FtIntervalToMilliseconds(&ft),
+                     NULL);
+        }
+    }
+
+    LeaveCriticalSection(&cs_timers);
+
+    _end_task();
+}
index 130ae999ac00722d2f5a9d56fdf1749ff37fa11c..af4ece7234f80740da928a47797cc4d85914dcc7 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_TIMER_H\r
-#define __KHIMAIRA_TIMER_H\r
-\r
-/* note that the ordering of the first few enum constants are\r
-   significant.  The values of the constants up to KHUI_N_TTYPES are\r
-   used as indices.  */\r
-typedef enum tag_khui_timer_type {\r
-    KHUI_TTYPE_ID_EXP = 0,      /* Identity expiration */\r
-    KHUI_TTYPE_ID_CRIT,         /* Identity critical */\r
-    KHUI_TTYPE_ID_WARN,         /* Identity warning */\r
-    KHUI_TTYPE_CRED_EXP,        /* Credential expiration */\r
-    KHUI_TTYPE_CRED_CRIT,       /* Credential critical */ \r
-    KHUI_TTYPE_CRED_WARN,       /* Credential warning */\r
-\r
-    KHUI_N_TTYPES,              /* Count of the timers that we\r
-                                   aggregate for notifications */\r
-\r
-    KHUI_TTYPE_ID_MARK,         /* Identity marker */\r
-\r
-    KHUI_TTYPE_ID_RENEW,        /* Identity auto renewal */\r
-    KHUI_TTYPE_CRED_RENEW,      /* Credential renewal */\r
-\r
-#if 0\r
-    KHUI_TTYPE_BMSG,            /* Custom. Sends broadcast message\r
-                                   when triggered.*/\r
-    KHUI_TTYPE_SMSG,            /* Custom. Sends subscription message\r
-                                   when triggered. */\r
-#endif\r
-} khui_timer_type;\r
-\r
-typedef struct tag_khui_timer_event {\r
-    khm_handle       key;\r
-    khui_timer_type  type;\r
-\r
-    khm_int64 expire;    /* time at which the timer expires */\r
-    khm_int64 offset;    /* time offset at which the event that the\r
-                            timer warns of happens */\r
-    void *           data;\r
-    khm_int32        flags;\r
-} khui_timer_event;\r
-\r
-#define KHUI_TRIGGER_TIMER_ID 48\r
-#define KHUI_REFRESH_TIMER_ID 49\r
-\r
-#define KHUI_REFRESH_TIMEOUT 5000\r
-\r
-#define KHUI_TE_FLAG_EXPIRED 0x00000001\r
-#define KHUI_TE_FLAG_STALE   0x00000002\r
-\r
-#define KHUI_DEF_TIMEOUT_WARN 900\r
-#define KHUI_DEF_TIMEOUT_CRIT 300\r
-#define KHUI_DEF_TIMEOUT_RENEW 60\r
-\r
-/* the max absolute difference between two timers (in seconds) that\r
-   can exist where we consider both timers to be in the same\r
-   timeslot. */\r
-#define KHUI_TIMEEQ_ERROR 20\r
-\r
-/* the small error. */\r
-#define KHUI_TIMEEQ_ERROR_SMALL 1\r
-\r
-void\r
-khm_timer_refresh(HWND hwnd);\r
-\r
-void\r
-khm_timer_fire(HWND hwnd);\r
-\r
-void\r
-khm_timer_init(void);\r
-\r
-void\r
-khm_timer_exit(void);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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;
+
+    khm_int64 expire;    /* time at which the timer expires */
+    khm_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
index a464009fc63eb14857920a3d629c119db72e04ad..c543e69236fef2c9c0a944f0ebfc57b0dbd55075 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khmapp.h>\r
-#include<assert.h>\r
-\r
-HWND khui_hwnd_standard_toolbar;\r
-int khui_tb_blank;\r
-\r
-khui_ilist * ilist_toolbar;\r
-\r
-void khui_init_toolbar(void) {\r
-    ilist_toolbar = khui_create_ilist(KHUI_TOOLBAR_IMAGE_WIDTH, KHUI_TOOLBAR_IMAGE_HEIGHT, KHUI_TOOLBAR_MAX_BTNS, 5, 0);\r
-}\r
-\r
-void khui_exit_toolbar(void) {\r
-    khui_delete_ilist(ilist_toolbar);\r
-}\r
-\r
-LRESULT khm_toolbar_notify(LPNMHDR notice) {\r
-    switch(notice->code) {\r
-    case TBN_GETINFOTIP:\r
-        {\r
-            LPNMTBGETINFOTIP git = (LPNMTBGETINFOTIP) notice;\r
-            int cmd;\r
-            khui_action * a;\r
-\r
-            cmd = git->iItem;\r
-            a = khui_find_action(cmd);\r
-\r
-            if (a) {\r
-                if (a->caption) {\r
-                    StringCchCopy(git->pszText, git->cchTextMax, a->caption);\r
-                } else if (a->tooltip) {\r
-                    StringCchCopy(git->pszText, git->cchTextMax, a->tooltip);\r
-                } else if (a->is_caption) {\r
-                    wchar_t buf[INFOTIPSIZE];\r
-\r
-                    buf[0] = L'\0';\r
-                    LoadString(khm_hInstance, a->is_caption,\r
-                               buf, ARRAYLENGTH(buf));\r
-\r
-                    StringCchCopy(git->pszText, git->cchTextMax, buf);\r
-                } else {\r
-                    StringCchCopy(git->pszText, git->cchTextMax, L"");\r
-                }\r
-            } else {\r
-                StringCchCopy(git->pszText,\r
-                              git->cchTextMax,\r
-                              L"");\r
-            }\r
-        }\r
-        break;\r
-\r
-    case TBN_HOTITEMCHANGE:\r
-        {\r
-            LPNMTBHOTITEM hi = (LPNMTBHOTITEM) notice;\r
-\r
-            if (hi->dwFlags & HICF_LEAVING) {\r
-                khm_statusbar_set_part(KHUI_SBPART_INFO, NULL, L"");\r
-            } else {\r
-                khui_action * a;\r
-                int cmd;\r
-                wchar_t buf[256];\r
-\r
-                cmd = hi->idNew;\r
-                a = khui_find_action(cmd);\r
-\r
-                buf[0] = L'\0';\r
-\r
-                if (a) {\r
-                    if (a->tooltip)\r
-                        StringCbCopy(buf, sizeof(buf), a->tooltip);\r
-                    else if (a->is_tooltip) {\r
-                        LoadString(khm_hInstance, a->is_tooltip,\r
-                                   buf, ARRAYLENGTH(buf));\r
-                    }\r
-                }\r
-\r
-                khm_statusbar_set_part(KHUI_SBPART_INFO, NULL, buf);\r
-            }\r
-        }\r
-        break;\r
-\r
-    case TBN_DROPDOWN:\r
-        {\r
-            LPNMTOOLBAR nmtb = (LPNMTOOLBAR) notice;\r
-            RECT r;\r
-\r
-            GetWindowRect(khui_hwnd_standard_toolbar, &r);\r
-            if (nmtb->iItem == KHUI_ACTION_DESTROY_CRED) {\r
-                khm_menu_show_panel(KHUI_MENU_DESTROY_CRED,\r
-                                    r.left + nmtb->rcButton.left,\r
-                                    r.top + nmtb->rcButton.bottom);\r
-            } else if (nmtb->iItem == KHUI_ACTION_RENEW_CRED) {\r
-                khm_menu_show_panel(KHUI_MENU_RENEW_CRED,\r
-                                    r.left + nmtb->rcButton.left,\r
-                                    r.top + nmtb->rcButton.bottom);\r
-            } else {\r
-                return TBDDRET_NODEFAULT;\r
-            }\r
-\r
-            return TBDDRET_DEFAULT;\r
-        }\r
-        break;\r
-\r
-    case NM_CUSTOMDRAW:\r
-        {\r
-            LPNMTBCUSTOMDRAW nmcd = (LPNMTBCUSTOMDRAW) notice;\r
-            if(nmcd->nmcd.dwDrawStage == CDDS_PREPAINT) {\r
-                return CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTERASE;\r
-            } else if(nmcd->nmcd.dwDrawStage == CDDS_ITEMPREPAINT) {\r
-                return CDRF_NOTIFYPOSTPAINT;\r
-            } else if(nmcd->nmcd.dwDrawStage == CDDS_ITEMPOSTPAINT) {\r
-                /* draw the actual icon */\r
-                int iidx;\r
-                int ibmp;\r
-                HBITMAP hbmp;\r
-                RECT r;\r
-\r
-                khui_action * act = \r
-                    khui_find_action((int) nmcd->nmcd.dwItemSpec);\r
-\r
-                if(!act || !act->ib_normal)\r
-                    return CDRF_DODEFAULT;\r
-\r
-                if((act->state & KHUI_ACTIONSTATE_DISABLED) && \r
-                   act->ib_disabled) {\r
-                    ibmp = act->ib_disabled;\r
-                } else if(act->ib_hot && \r
-                          ((nmcd->nmcd.uItemState & CDIS_HOT) || \r
-                           (nmcd->nmcd.uItemState & CDIS_SELECTED))){\r
-                    ibmp = act->ib_hot;\r
-                } else {\r
-                    ibmp = act->ib_normal;\r
-                }\r
-\r
-                iidx = khui_ilist_lookup_id(ilist_toolbar, ibmp);\r
-                if(iidx < 0) {\r
-                    hbmp = LoadImage(khm_hInstance, \r
-                                     MAKEINTRESOURCE(ibmp), \r
-                                     IMAGE_BITMAP, \r
-                                     KHUI_TOOLBAR_IMAGE_WIDTH, \r
-                                     KHUI_TOOLBAR_IMAGE_HEIGHT, 0);\r
-                    iidx = \r
-                        khui_ilist_add_masked_id(ilist_toolbar, \r
-                                                 hbmp, \r
-                                                 KHUI_TOOLBAR_BGCOLOR, \r
-                                                 ibmp);\r
-                    DeleteObject(hbmp);\r
-                }\r
-\r
-                if(iidx < 0)\r
-                    return CDRF_DODEFAULT;\r
-\r
-                CopyRect(&r, &(nmcd->nmcd.rc));\r
-                r.left += ((r.bottom - r.top) -\r
-                          KHUI_TOOLBAR_IMAGE_HEIGHT) / 2;\r
-                r.top += ((r.bottom - r.top) -\r
-                          KHUI_TOOLBAR_IMAGE_HEIGHT) / 2;\r
-#if 0\r
-                r.left += ((r.right - r.left) - \r
-                           KHUI_TOOLBAR_IMAGE_WIDTH) / 2;\r
-#endif\r
-                khui_ilist_draw(ilist_toolbar, \r
-                                iidx, \r
-                                nmcd->nmcd.hdc, \r
-                                r.left,\r
-                                r.top, \r
-                                0);\r
-\r
-                return CDRF_DODEFAULT;\r
-            }\r
-        }\r
-        break;\r
-    }\r
-    return 0;\r
-}\r
-\r
-void khui_add_action_to_toolbar(HWND tb, khui_action *a, int opt, HIMAGELIST hiList) {\r
-    wchar_t buf[MAX_RES_STRING] = L"";\r
-    int idx_caption = 0;\r
-    TBBUTTON bn;\r
-    LRESULT lr;\r
-\r
-    ZeroMemory(&bn,sizeof(bn));\r
-\r
-    if(opt & KHUI_TOOLBAR_ADD_SEP) {\r
-        bn.fsStyle = BTNS_SEP;\r
-        bn.iBitmap = 3;\r
-\r
-        lr = SendMessage(tb,\r
-                         TB_ADDBUTTONS,\r
-                         1,\r
-                         (LPARAM) &bn);\r
-#ifdef DEBUG\r
-        assert(lr);\r
-#endif\r
-        return;\r
-    }\r
-\r
-    bn.fsStyle = BTNS_BUTTON;\r
-\r
-    if(opt & KHUI_TOOLBAR_VARSIZE) {\r
-        bn.fsStyle |= BTNS_AUTOSIZE;\r
-    }\r
-\r
-    if(opt & KHUI_TOOLBAR_ADD_TEXT) {\r
-        int sid = 0;\r
-        if((opt & KHUI_TOOLBAR_ADD_LONGTEXT) == \r
-           KHUI_TOOLBAR_ADD_LONGTEXT) {\r
-            sid = a->is_tooltip;\r
-        }\r
-        if(!sid)\r
-            sid = a->is_caption;\r
-        if(sid) {\r
-            LoadString(khm_hInstance, \r
-                       sid, \r
-                       buf, ARRAYLENGTH(buf));\r
-            buf[wcslen(buf) + 1] = L'\0';\r
-            idx_caption = (int) SendMessage(tb,\r
-                                            TB_ADDSTRING,\r
-                                            (WPARAM) NULL,\r
-                                            (LPARAM) buf);\r
-#if (_WIN32_IE >= 0x0501)\r
-            bn.fsStyle |= BTNS_SHOWTEXT;\r
-#endif\r
-            bn.iString = idx_caption;\r
-        }\r
-    }\r
-\r
-    if(opt & KHUI_TOOLBAR_ADD_DROPDOWN) {\r
-        bn.fsStyle |= BTNS_DROPDOWN;\r
-    }\r
-\r
-    if((opt & KHUI_TOOLBAR_ADD_BITMAP) && a->ib_normal) {\r
-        bn.fsStyle |= TBSTYLE_CUSTOMERASE;\r
-        bn.iBitmap = khui_tb_blank;\r
-    } else {\r
-#if (_WIN32_IE >= 0x0501)\r
-        bn.iBitmap = I_IMAGENONE;\r
-#endif\r
-    }\r
-\r
-    bn.idCommand = a->cmd;\r
-\r
-    if(a->state & KHUI_ACTIONSTATE_DISABLED) {\r
-        bn.fsState = 0;\r
-    } else {\r
-        bn.fsState = TBSTATE_ENABLED;\r
-    }\r
-\r
-    if(a->state & KHUI_ACTIONSTATE_CHECKED) {\r
-        bn.fsState |= TBSTATE_CHECKED;\r
-    }\r
-\r
-    bn.dwData = 0;\r
-\r
-    lr = SendMessage(\r
-                     tb,\r
-                     TB_ADDBUTTONS,\r
-                     1,\r
-                     (LPARAM) &bn);\r
-\r
-#ifdef DEBUG\r
-    assert(lr);\r
-#endif\r
-}\r
-\r
-void khm_update_standard_toolbar(void)\r
-{\r
-    khui_menu_def * def;\r
-    khui_action_ref * aref;\r
-    khui_action * act;\r
-\r
-    def = khui_find_menu(KHUI_TOOLBAR_STANDARD);\r
-\r
-    aref = def->items;\r
-\r
-    while(aref && aref->action != KHUI_MENU_END) {\r
-        if(aref->action == KHUI_MENU_SEP) {\r
-            aref++;\r
-            continue;\r
-        }\r
-\r
-        act = khui_find_action(aref->action);\r
-        if(act) {\r
-            BOOL enable;\r
-\r
-            enable = !(act->state & KHUI_ACTIONSTATE_DISABLED);\r
-            SendMessage(khui_hwnd_standard_toolbar, \r
-                        TB_ENABLEBUTTON, \r
-                        (WPARAM) act->cmd,\r
-                        MAKELPARAM(enable, 0));\r
-        }\r
-\r
-        aref++;\r
-    }\r
-}\r
-\r
-void khm_create_standard_toolbar(HWND rebar) {\r
-    HWND hwtb;\r
-    SIZE sz;\r
-    HBITMAP hbm_blank;\r
-    HIMAGELIST hiList;\r
-    REBARBANDINFO rbi;\r
-    khui_menu_def * def;\r
-    khui_action * act;\r
-    khui_action_ref * aref;\r
-    int idx_blank;\r
-\r
-    def = khui_find_menu(KHUI_TOOLBAR_STANDARD);\r
-\r
-    if (!def) {\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-        return;\r
-    }\r
-\r
-    hwtb = CreateWindowEx(0 ,\r
-                          TOOLBARCLASSNAME,\r
-                          (LPWSTR) NULL,\r
-                          WS_CHILD |\r
-                          TBSTYLE_FLAT |\r
-                          TBSTYLE_AUTOSIZE | \r
-                          TBSTYLE_TOOLTIPS |\r
-                          CCS_NORESIZE | \r
-                          CCS_NOPARENTALIGN |\r
-                          CCS_ADJUSTABLE |\r
-                          CCS_NODIVIDER,\r
-                          0, 0, 0, 0, rebar,\r
-                          (HMENU) NULL, khm_hInstance,\r
-                          NULL);\r
-\r
-    if(!hwtb) {\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-        return;\r
-    }\r
-\r
-#if (_WIN32_IE >= 0x0501)\r
-    SendMessage(hwtb, TB_SETEXTENDEDSTYLE, 0,\r
-                TBSTYLE_EX_MIXEDBUTTONS | TBSTYLE_EX_DRAWDDARROWS);\r
-#endif\r
-\r
-    hiList = ImageList_Create(\r
-        KHUI_TOOLBAR_IMAGE_WIDTH,\r
-        KHUI_TOOLBAR_IMAGE_HEIGHT,\r
-        ILC_MASK,\r
-        (int) khui_action_list_length(def->items),\r
-        3);\r
-\r
-    hbm_blank = LoadImage(khm_hInstance, \r
-                          MAKEINTRESOURCE(IDB_TB_BLANK), \r
-                          IMAGE_BITMAP, \r
-                          KHUI_TOOLBAR_IMAGE_WIDTH, \r
-                          KHUI_TOOLBAR_IMAGE_HEIGHT, 0);\r
-    idx_blank = ImageList_AddMasked(hiList, hbm_blank, RGB(0,0,0));\r
-\r
-    khui_hwnd_standard_toolbar = hwtb;\r
-    khui_tb_blank = idx_blank;\r
-\r
-    def = khui_find_menu(KHUI_TOOLBAR_STANDARD);\r
-\r
-    aref = def->items;\r
-\r
-    SendMessage(hwtb,\r
-        TB_BUTTONSTRUCTSIZE,\r
-        sizeof(TBBUTTON),\r
-        0);\r
-\r
-    SendMessage(hwtb,\r
-        TB_SETBITMAPSIZE,\r
-        0,\r
-        MAKELONG(KHUI_TOOLBAR_IMAGE_WIDTH,KHUI_TOOLBAR_IMAGE_HEIGHT));\r
-\r
-    SendMessage(hwtb,\r
-        TB_SETIMAGELIST,\r
-        0,\r
-        (LPARAM) hiList);\r
-\r
-    SendMessage(hwtb,\r
-        TB_SETBUTTONSIZE,\r
-        0,\r
-        MAKELONG(KHUI_TOOLBAR_IMAGE_WIDTH,KHUI_TOOLBAR_IMAGE_HEIGHT));\r
-\r
-    while(aref && aref->action != KHUI_MENU_END) {\r
-        if(aref->action == KHUI_MENU_SEP) {\r
-            khui_add_action_to_toolbar(hwtb, \r
-                                       NULL, \r
-                                       KHUI_TOOLBAR_ADD_SEP, \r
-                                       hiList);\r
-        } else {\r
-            act = khui_find_action(aref->action);\r
-            khui_add_action_to_toolbar(hwtb, \r
-                                       act, \r
-                                       KHUI_TOOLBAR_ADD_BITMAP |\r
-                                       ((aref->flags & KHUI_ACTIONREF_SUBMENU)?\r
-                                        KHUI_TOOLBAR_ADD_DROPDOWN: 0),\r
-                                       hiList);\r
-        }\r
-        aref ++;\r
-    }\r
-\r
-    SendMessage(hwtb,\r
-                TB_AUTOSIZE,\r
-                0,0);\r
-\r
-    SendMessage(hwtb,\r
-                TB_GETMAXSIZE,\r
-                0,\r
-                (LPARAM) &sz);\r
-\r
-    sz.cy += 5;\r
-\r
-    ZeroMemory(&rbi, sizeof(rbi));\r
-\r
-    rbi.cbSize = sizeof(rbi);\r
-    rbi.fMask = \r
-        RBBIM_ID |\r
-        RBBIM_CHILD | \r
-        RBBIM_CHILDSIZE | \r
-        RBBIM_IDEALSIZE | \r
-        RBBIM_SIZE | \r
-        RBBIM_STYLE;\r
-    rbi.fStyle =  \r
-        RBBS_USECHEVRON |\r
-        RBBS_BREAK;\r
-    rbi.hwndChild = hwtb;\r
-\r
-    rbi.wID = KHUI_TOOLBAR_STANDARD;\r
-    rbi.cx = sz.cx;\r
-    rbi.cxMinChild = sz.cx;\r
-    rbi.cyMinChild = sz.cy;\r
-    rbi.cyChild = rbi.cyMinChild;\r
-    rbi.cyMaxChild = rbi.cyMinChild;\r
-    rbi.cyIntegral = rbi.cyMinChild;\r
-\r
-    rbi.cxIdeal = rbi.cx;\r
-\r
-    SendMessage(rebar,\r
-        RB_INSERTBAND,\r
-        0,\r
-        (LPARAM) &rbi);\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 TBN_GETINFOTIP:
+        {
+            LPNMTBGETINFOTIP git = (LPNMTBGETINFOTIP) notice;
+            int cmd;
+            khui_action * a;
+
+            cmd = git->iItem;
+            a = khui_find_action(cmd);
+
+            if (a) {
+                if (a->caption) {
+                    StringCchCopy(git->pszText, git->cchTextMax, a->caption);
+                } else if (a->tooltip) {
+                    StringCchCopy(git->pszText, git->cchTextMax, a->tooltip);
+                } else if (a->is_caption) {
+                    wchar_t buf[INFOTIPSIZE];
+
+                    buf[0] = L'\0';
+                    LoadString(khm_hInstance, a->is_caption,
+                               buf, ARRAYLENGTH(buf));
+
+                    StringCchCopy(git->pszText, git->cchTextMax, buf);
+                } else {
+                    StringCchCopy(git->pszText, git->cchTextMax, L"");
+                }
+            } else {
+                StringCchCopy(git->pszText,
+                              git->cchTextMax,
+                              L"");
+            }
+        }
+        break;
+
+    case TBN_HOTITEMCHANGE:
+        {
+            LPNMTBHOTITEM hi = (LPNMTBHOTITEM) notice;
+
+            if (hi->dwFlags & HICF_LEAVING) {
+                khm_statusbar_set_part(KHUI_SBPART_INFO, NULL, L"");
+            } else {
+                khui_action * a;
+                int cmd;
+                wchar_t buf[256];
+
+                cmd = hi->idNew;
+                a = khui_find_action(cmd);
+
+                buf[0] = L'\0';
+
+                if (a) {
+                    if (a->tooltip)
+                        StringCbCopy(buf, sizeof(buf), a->tooltip);
+                    else if (a->is_tooltip) {
+                        LoadString(khm_hInstance, a->is_tooltip,
+                                   buf, ARRAYLENGTH(buf));
+                    }
+                }
+
+                khm_statusbar_set_part(KHUI_SBPART_INFO, NULL, buf);
+            }
+        }
+        break;
+
+    case TBN_DROPDOWN:
+        {
+            LPNMTOOLBAR nmtb = (LPNMTOOLBAR) notice;
+            RECT r;
+
+            GetWindowRect(khui_hwnd_standard_toolbar, &r);
+            if (nmtb->iItem == KHUI_ACTION_DESTROY_CRED) {
+                khm_menu_show_panel(KHUI_MENU_DESTROY_CRED,
+                                    r.left + nmtb->rcButton.left,
+                                    r.top + nmtb->rcButton.bottom);
+            } else if (nmtb->iItem == KHUI_ACTION_RENEW_CRED) {
+                khm_menu_show_panel(KHUI_MENU_RENEW_CRED,
+                                    r.left + nmtb->rcButton.left,
+                                    r.top + nmtb->rcButton.bottom);
+            } else {
+                return TBDDRET_NODEFAULT;
+            }
+
+            return TBDDRET_DEFAULT;
+        }
+        break;
+
+    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.bottom - r.top) -
+                          KHUI_TOOLBAR_IMAGE_HEIGHT) / 2;
+                r.top += ((r.bottom - r.top) -
+                          KHUI_TOOLBAR_IMAGE_HEIGHT) / 2;
+#if 0
+                r.left += ((r.right - r.left) - 
+                           KHUI_TOOLBAR_IMAGE_WIDTH) / 2;
+#endif
+                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);
+#if (_WIN32_IE >= 0x0501)
+            bn.fsStyle |= BTNS_SHOWTEXT;
+#endif
+            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 {
+#if (_WIN32_IE >= 0x0501)
+        bn.iBitmap = I_IMAGENONE;
+#endif
+    }
+
+    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);
+
+    if (!def) {
+#ifdef DEBUG
+        assert(FALSE);
+#endif
+        return;
+    }
+
+    hwtb = CreateWindowEx(0 ,
+                          TOOLBARCLASSNAME,
+                          (LPWSTR) NULL,
+                          WS_CHILD |
+                          TBSTYLE_FLAT |
+                          TBSTYLE_AUTOSIZE | 
+                          TBSTYLE_TOOLTIPS |
+                          CCS_NORESIZE | 
+                          CCS_NOPARENTALIGN |
+                          CCS_ADJUSTABLE |
+                          CCS_NODIVIDER,
+                          0, 0, 0, 0, rebar,
+                          (HMENU) NULL, khm_hInstance,
+                          NULL);
+
+    if(!hwtb) {
+#ifdef DEBUG
+        assert(FALSE);
+#endif
+        return;
+    }
+
+#if (_WIN32_IE >= 0x0501)
+    SendMessage(hwtb, TB_SETEXTENDEDSTYLE, 0,
+                TBSTYLE_EX_MIXEDBUTTONS | TBSTYLE_EX_DRAWDDARROWS);
+#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 |
+                                       ((aref->flags & KHUI_ACTIONREF_SUBMENU)?
+                                        KHUI_TOOLBAR_ADD_DROPDOWN: 0),
+                                       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,
+        0,
+        (LPARAM) &rbi);
+}
index 8068a6be240871e71ab86724728bf5f9fd0259a6..ac09c6619ba7ab940a239ed1e829bff0e6a028f4 100644 (file)
@@ -1,52 +1,52 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_TOOLBAR_H\r
-#define __KHIMAIRA_TOOLBAR_H\r
-\r
-extern HWND khui_hwnd_standard_toolbar;\r
-\r
-void khui_init_toolbar(void);\r
-void khui_exit_toolbar(void);\r
-LRESULT khm_toolbar_notify(LPNMHDR notice);\r
-void khm_create_standard_toolbar(HWND rebar);\r
-void khui_add_action_to_toolbar(HWND toolbar, khui_action * act, int opt, HIMAGELIST hiList);\r
-void khm_update_standard_toolbar(void);\r
-\r
-/* options for khui_add_action_to_toolbar */\r
-#define KHUI_TOOLBAR_ADD_TEXT      0x00000001\r
-#define KHUI_TOOLBAR_ADD_BITMAP    0x00000002\r
-#define KHUI_TOOLBAR_ADD_LONGTEXT  0x00000005\r
-#define KHUI_TOOLBAR_ADD_DROPDOWN  0x00000008\r
-#define KHUI_TOOLBAR_ADD_SEP       0x00000010\r
-#define KHUI_TOOLBAR_VARSIZE       0x00000020\r
-\r
-#define KHUI_TOOLBAR_IMAGE_WIDTH 29\r
-#define KHUI_TOOLBAR_IMAGE_HEIGHT 27\r
-#define KHUI_TOOLBAR_BGCOLOR RGB(0xd7,0xd7,0xd7)\r
-#define KHUI_TOOLBAR_MAX_BTNS 64\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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      0x00000001
+#define KHUI_TOOLBAR_ADD_BITMAP    0x00000002
+#define KHUI_TOOLBAR_ADD_LONGTEXT  0x00000005
+#define KHUI_TOOLBAR_ADD_DROPDOWN  0x00000008
+#define KHUI_TOOLBAR_ADD_SEP       0x00000010
+#define KHUI_TOOLBAR_VARSIZE       0x00000020
+
+#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
index ae017e1d0a220b3ad59685208cd854527f1f84bd..d5556620d474e7f7f02b5933819e4224885a6a21 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#define NOEXPORT\r
-#include<khuidefs.h>\r
-#include<intaction.h>\r
-#include<utils.h>\r
-#include<assert.h>\r
-\r
-#include<strsafe.h>\r
-\r
-khui_action_ref khui_main_menu[] = {\r
-    MENU_SUBMENU(KHUI_MENU_FILE),\r
-    MENU_SUBMENU(KHUI_MENU_CRED),\r
-    MENU_SUBMENU(KHUI_MENU_VIEW),\r
-    MENU_SUBMENU(KHUI_MENU_OPTIONS),\r
-    MENU_SUBMENU(KHUI_MENU_HELP),\r
-    MENU_END()\r
-};\r
-\r
-khui_action_ref khui_menu_file[] = {\r
-    MENU_ACTION(KHUI_ACTION_PROPERTIES),\r
-    MENU_SEP(),\r
-    MENU_ACTION(KHUI_ACTION_EXIT),\r
-    MENU_END()\r
-};\r
-\r
-khui_action_ref khui_menu_cred[] = {\r
-    MENU_ACTION(KHUI_ACTION_NEW_CRED),\r
-    MENU_SEP(),\r
-    MENU_SUBMENU(KHUI_MENU_RENEW_CRED),\r
-    MENU_ACTION(KHUI_ACTION_IMPORT),\r
-    MENU_SUBMENU(KHUI_MENU_DESTROY_CRED),\r
-    MENU_SEP(),\r
-    MENU_ACTION(KHUI_ACTION_SET_DEF_ID),\r
-#if 0\r
-    /* not implemented yet */\r
-    MENU_ACTION(KHUI_ACTION_SET_SRCH_ID),\r
-#endif\r
-    MENU_SEP(),\r
-    MENU_ACTION(KHUI_ACTION_PASSWD_ID),\r
-    MENU_END()\r
-};\r
-\r
-khui_action_ref khui_menu_layout[] = {\r
-    MENU_ACTION(KHUI_ACTION_LAYOUT_ID),\r
-    MENU_ACTION(KHUI_ACTION_LAYOUT_TYPE),\r
-    MENU_ACTION(KHUI_ACTION_LAYOUT_LOC),\r
-    MENU_ACTION(KHUI_ACTION_LAYOUT_CUST),\r
-    MENU_END()\r
-};\r
-\r
-khui_action_ref khui_menu_toolbars[] = {\r
-    MENU_ACTION(KHUI_ACTION_TB_STANDARD),\r
-    MENU_END()\r
-};\r
-\r
-khui_action_ref khui_menu_view[] = {\r
-    MENU_ACTION(KHUI_ACTION_LAYOUT_MINI),\r
-    MENU_SUBMENU(KHUI_MENU_COLUMNS),\r
-    MENU_SUBMENU(KHUI_MENU_LAYOUT),\r
-#if 0\r
-    /* not implemented yet */\r
-    MENU_SUBMENU(KHUI_MENU_TOOLBARS),\r
-#endif\r
-    MENU_SEP(),\r
-#if 0\r
-    /* not implemented yet */\r
-    MENU_ACTION(KHUI_ACTION_DEBUG_WINDOW),\r
-    MENU_SEP(),\r
-#endif\r
-    MENU_ACTION(KHUI_ACTION_VIEW_REFRESH),\r
-    MENU_END()\r
-};\r
-\r
-khui_action_ref khui_menu_options[] = {\r
-    MENU_ACTION(KHUI_ACTION_OPT_KHIM),\r
-    MENU_ACTION(KHUI_ACTION_OPT_APPEAR),\r
-    MENU_ACTION(KHUI_ACTION_OPT_IDENTS),\r
-    MENU_ACTION(KHUI_ACTION_OPT_NOTIF),\r
-    MENU_ACTION(KHUI_ACTION_OPT_PLUGINS),\r
-    MENU_SEP(),\r
-    MENU_END()\r
-};\r
-\r
-khui_action_ref khui_menu_help[] = {\r
-    MENU_ACTION(KHUI_ACTION_HELP_CTX),\r
-    MENU_SEP(),\r
-    MENU_ACTION(KHUI_ACTION_HELP_INDEX),\r
-    MENU_SEP(),\r
-    MENU_ACTION(KHUI_ACTION_HELP_ABOUT),\r
-    MENU_END()\r
-};\r
-\r
-khui_action_ref khui_toolbar_standard[] = {\r
-    MENU_ACTION(KHUI_ACTION_NEW_CRED),\r
-    MENU_SUBMENU(KHUI_ACTION_RENEW_CRED),\r
-    MENU_ACTION(KHUI_ACTION_IMPORT),\r
-    MENU_SUBMENU(KHUI_ACTION_DESTROY_CRED),\r
-    MENU_SEP(),\r
-    MENU_ACTION(KHUI_ACTION_PASSWD_ID),\r
-    MENU_SEP(),\r
-    MENU_ACTION(KHUI_ACTION_VIEW_REFRESH),\r
-    MENU_ACTION(KHUI_PACTION_BLANK),\r
-    MENU_ACTION(KHUI_ACTION_HELP_CTX),\r
-    MENU_END()\r
-};\r
-\r
-khui_action_ref khui_menu_ident_ctx[] = {\r
-    MENU_ACTION(KHUI_ACTION_PROPERTIES),\r
-    MENU_SEP(),\r
-    MENU_ACTION(KHUI_ACTION_SET_DEF_ID),\r
-    MENU_ACTION(KHUI_ACTION_SET_SRCH_ID),\r
-    MENU_SEP(),\r
-    MENU_ACTION(KHUI_ACTION_NEW_CRED),\r
-    MENU_ACTION(KHUI_ACTION_RENEW_CRED),\r
-    MENU_ACTION(KHUI_ACTION_DESTROY_CRED),\r
-    MENU_END()\r
-};\r
-\r
-khui_action_ref khui_menu_tok_ctx[] = {\r
-    MENU_ACTION(KHUI_ACTION_PROPERTIES),\r
-    MENU_SEP(),\r
-    MENU_ACTION(KHUI_ACTION_NEW_CRED),\r
-    MENU_ACTION(KHUI_ACTION_RENEW_CRED),\r
-    MENU_ACTION(KHUI_ACTION_DESTROY_CRED),\r
-    MENU_END()\r
-};\r
-\r
-khui_action_ref khui_menu_ico_ctx_min[] = {\r
-    MENU_DEFACTION(KHUI_ACTION_OPEN_APP),\r
-    MENU_SEP(),\r
-    MENU_ACTION(KHUI_ACTION_NEW_CRED),\r
-    MENU_SUBMENU(KHUI_MENU_RENEW_CRED),\r
-    MENU_ACTION(KHUI_ACTION_IMPORT),\r
-    MENU_SUBMENU(KHUI_MENU_DESTROY_CRED),\r
-    MENU_SEP(),\r
-    MENU_ACTION(KHUI_ACTION_PASSWD_ID),\r
-    MENU_SEP(),\r
-    MENU_ACTION(KHUI_ACTION_HELP_CTX),\r
-    MENU_ACTION(KHUI_ACTION_HELP_ABOUT),\r
-    MENU_SEP(),\r
-    MENU_ACTION(KHUI_ACTION_EXIT),\r
-    MENU_END()\r
-};\r
-\r
-khui_action_ref khui_menu_ico_ctx_normal[] = {\r
-    MENU_DEFACTION(KHUI_ACTION_CLOSE_APP),\r
-    MENU_SEP(),\r
-    MENU_ACTION(KHUI_ACTION_NEW_CRED),\r
-    MENU_SUBMENU(KHUI_MENU_RENEW_CRED),\r
-    MENU_ACTION(KHUI_ACTION_IMPORT),\r
-    MENU_SUBMENU(KHUI_MENU_DESTROY_CRED),\r
-    MENU_SEP(),\r
-    MENU_ACTION(KHUI_ACTION_PASSWD_ID),\r
-    MENU_SEP(),\r
-    MENU_ACTION(KHUI_ACTION_HELP_CTX),\r
-    MENU_ACTION(KHUI_ACTION_HELP_ABOUT),\r
-    MENU_SEP(),\r
-    MENU_ACTION(KHUI_ACTION_EXIT),\r
-    MENU_END()\r
-};\r
-\r
-khui_action_ref khui_menu_cwheader_ctx[] = {\r
-    MENU_SUBMENU(KHUI_MENU_COLUMNS),\r
-    MENU_SUBMENU(KHUI_MENU_LAYOUT),\r
-    MENU_END()\r
-};\r
-\r
-khui_action_ref khui_menu_columns[] = {\r
-    MENU_END()\r
-};\r
-\r
-khui_action_ref khui_menu_destroy_cred[] = {\r
-    MENU_DEFACTION(KHUI_ACTION_DESTROY_ALL),\r
-    MENU_END()\r
-};\r
-\r
-khui_action_ref khui_menu_renew_cred[] = {\r
-    MENU_DEFACTION(KHUI_ACTION_RENEW_ALL),\r
-    MENU_END()\r
-};\r
-\r
-khui_action_ref khui_pmenu_tok_sel[] = {\r
-    MENU_ACTION(KHUI_ACTION_RENEW_CRED),\r
-    MENU_ACTION(KHUI_ACTION_DESTROY_CRED),\r
-    MENU_END()\r
-};\r
-\r
-khui_action_ref khui_pmenu_id_sel[] = {\r
-    MENU_ACTION(KHUI_ACTION_DESTROY_CRED),\r
-    MENU_ACTION(KHUI_ACTION_RENEW_CRED),\r
-    MENU_END()\r
-};\r
-\r
-/* all stock menus and toolbars */\r
-khui_menu_def khui_all_menus[] = {\r
-    CONSTMENU(KHUI_MENU_MAIN, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_main_menu),\r
-    CONSTMENU(KHUI_MENU_FILE, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_file),\r
-    CONSTMENU(KHUI_MENU_CRED, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_cred),\r
-    CONSTMENU(KHUI_MENU_VIEW, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_view),\r
-    CONSTMENU(KHUI_MENU_LAYOUT, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_layout),\r
-    CONSTMENU(KHUI_MENU_TOOLBARS, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_toolbars),\r
-    CONSTMENU(KHUI_MENU_OPTIONS, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_options),\r
-    CONSTMENU(KHUI_MENU_HELP, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_help),\r
-    CONSTMENU(KHUI_MENU_COLUMNS, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_columns),\r
-    CONSTMENU(KHUI_MENU_RENEW_CRED, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_renew_cred),\r
-    CONSTMENU(KHUI_MENU_DESTROY_CRED, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_destroy_cred),\r
-\r
-    /* toolbars */\r
-    CONSTMENU(KHUI_TOOLBAR_STANDARD, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_toolbar_standard),\r
-\r
-    /* context menus */\r
-    CONSTMENU(KHUI_MENU_IDENT_CTX, KHUI_MENUSTATE_CONSTANT, khui_menu_ident_ctx),\r
-    CONSTMENU(KHUI_MENU_TOK_CTX, KHUI_MENUSTATE_CONSTANT, khui_menu_tok_ctx),\r
-    CONSTMENU(KHUI_MENU_ICO_CTX_MIN, KHUI_MENUSTATE_CONSTANT, khui_menu_ico_ctx_min),\r
-    CONSTMENU(KHUI_MENU_ICO_CTX_NORMAL, KHUI_MENUSTATE_CONSTANT, khui_menu_ico_ctx_normal),\r
-    CONSTMENU(KHUI_MENU_CWHEADER_CTX, KHUI_MENUSTATE_CONSTANT, khui_menu_cwheader_ctx),\r
-\r
-    /* pseudo menus */\r
-    CONSTMENU(KHUI_PMENU_TOK_SEL, KHUI_MENUSTATE_CONSTANT, khui_pmenu_tok_sel),\r
-    CONSTMENU(KHUI_PMENU_ID_SEL, KHUI_MENUSTATE_CONSTANT, khui_pmenu_id_sel)\r
-};\r
-\r
-int khui_n_all_menus = sizeof(khui_all_menus) / sizeof(khui_menu_def);\r
-khui_menu_def ** khui_cust_menus = NULL;\r
-int khui_nc_cust_menus = 0;\r
-int khui_n_cust_menus = 0;\r
-CRITICAL_SECTION cs_actions;\r
-\r
-#define CACT_NC_ALLOC 32\r
-\r
-khui_action ** khui_cust_actions = NULL;\r
-int khui_nc_cust_actions = 0;\r
-int khui_n_cust_actions = 0;\r
-\r
-HWND khui_hwnd_main;                   /* main window, for notifying\r
-                                           action launches and\r
-                                           dispatching messages to the\r
-                                           application. */\r
-\r
-KHMEXP void KHMAPI \r
-khui_init_actions(void) {\r
-    InitializeCriticalSection(&cs_actions);\r
-}\r
-\r
-KHMEXP void KHMAPI \r
-khui_exit_actions(void) {\r
-    DeleteCriticalSection(&cs_actions);\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_refresh_actions(void) {\r
-    kmq_post_message(KMSG_ACT, KMSG_ACT_REFRESH, 0, 0);\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_action_lock(void) {\r
-    EnterCriticalSection(&cs_actions);\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_action_unlock(void) {\r
-    LeaveCriticalSection(&cs_actions);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_action_create(const wchar_t * name,\r
-                   const wchar_t * caption,\r
-                   const wchar_t * tooltip,\r
-                   void * userdata,\r
-                   khm_int32 type,\r
-                   khm_handle hsub) {\r
-    khui_action * act;\r
-    khm_int32 action = 0;\r
-    int i;\r
-    size_t s;\r
-\r
-    if ((name && FAILED(StringCchLength(name, KHUI_MAXCCH_NAME, &s))) ||\r
-        !caption ||\r
-        FAILED(StringCchLength(caption, KHUI_MAXCCH_SHORT_DESC, &s)) ||\r
-        (tooltip && FAILED(StringCchLength(tooltip, KHUI_MAXCCH_SHORT_DESC, &s))) ||\r
-        (type != KHUI_ACTIONTYPE_TRIGGER && type != KHUI_ACTIONTYPE_TOGGLE)) {\r
-        return 0;\r
-    }\r
-\r
-    EnterCriticalSection(&cs_actions);\r
-    if (name && (act = khui_find_named_action(name))) {\r
-        /* named action already exists */\r
-        action = act->cmd;\r
-        goto _done;\r
-    }\r
-\r
-    for (i=0; i < khui_n_cust_actions; i++) {\r
-        if (khui_cust_actions[i] == NULL ||\r
-            (khui_cust_actions[i]->state & KHUI_ACTIONSTATE_DELETED))\r
-            break;\r
-    }\r
-\r
-    if (i >= khui_n_cust_actions &&\r
-        (khui_cust_actions == NULL ||\r
-         khui_n_cust_actions + 1 > khui_nc_cust_actions)) {\r
-\r
-        khui_nc_cust_actions = UBOUNDSS(khui_n_cust_actions + 1,\r
-                                        CACT_NC_ALLOC,\r
-                                        CACT_NC_ALLOC);\r
-#ifdef DEBUG\r
-        assert(khui_nc_cust_actions > khui_n_cust_actions + 1);\r
-#endif\r
-        khui_cust_actions = PREALLOC(khui_cust_actions,\r
-                                     sizeof(*khui_cust_actions) * khui_nc_cust_actions);\r
-#ifdef DEBUG\r
-        assert(khui_cust_actions);\r
-#endif\r
-    }\r
-\r
-    if (i >= khui_n_cust_actions) {\r
-        i = khui_n_cust_actions ++;\r
-        act = PMALLOC(sizeof(khui_action));\r
-    } else {\r
-        act = khui_cust_actions[i];\r
-        if (act == NULL)\r
-            act = PMALLOC(sizeof(khui_action));\r
-    }\r
-\r
-#ifdef DEBUG\r
-    assert(act);\r
-#endif\r
-\r
-    khui_cust_actions[i] = act;\r
-\r
-    ZeroMemory(act, sizeof(*act));\r
-\r
-    act->cmd = KHUI_USERACTION_BASE + i;\r
-    act->type = type;\r
-    act->name = (name? PWCSDUP(name) : 0);\r
-    act->caption = PWCSDUP(caption);\r
-    act->tooltip = (tooltip? PWCSDUP(tooltip) : 0);\r
-    act->listener = hsub;\r
-    act->data = userdata;\r
-    act->state = 0;\r
-\r
-    action = act->cmd;\r
-\r
- _done:\r
-    LeaveCriticalSection(&cs_actions);\r
-\r
-    if (action)\r
-        kmq_post_message(KMSG_ACT, KMSG_ACT_NEW, action, NULL);\r
-\r
-    return action;\r
-}\r
-\r
-KHMEXP void * KHMAPI\r
-khui_action_get_data(khm_int32 action) {\r
-    khui_action * act;\r
-    void * data;\r
-\r
-    EnterCriticalSection(&cs_actions);\r
-    act = khui_find_action(action);\r
-    if (act == NULL || (act->state & KHUI_ACTIONSTATE_DELETED))\r
-        data = NULL;\r
-    else\r
-        data = act->data;\r
-    LeaveCriticalSection(&cs_actions);\r
-\r
-    return data;\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_action_delete(khm_int32 action) {\r
-    khui_action * act;\r
-\r
-    EnterCriticalSection(&cs_actions);\r
-\r
-    act = khui_find_action(action);\r
-\r
-    if (act == NULL) {\r
-        LeaveCriticalSection(&cs_actions);\r
-        return;\r
-    }\r
-\r
-    /* for the moment, even when the action is deleted, we don't free\r
-       up the block of memory used by the khui_action structure.  When\r
-       a new action is created, it will reuse deleted action\r
-       structures. */\r
-    act->state |= KHUI_ACTIONSTATE_DELETED;\r
-    if (act->name)\r
-        PFREE(act->name);\r
-    if (act->caption)\r
-        PFREE(act->caption);\r
-    if (act->tooltip)\r
-        PFREE(act->tooltip);\r
-    if (act->listener)\r
-        kmq_delete_subscription(act->listener);\r
-    act->name = NULL;\r
-    act->caption = NULL;\r
-    act->tooltip = NULL;\r
-    act->listener = NULL;\r
-    LeaveCriticalSection(&cs_actions);\r
-\r
-    kmq_post_message(KMSG_ACT, KMSG_ACT_DELETE, action, NULL);\r
-}\r
-\r
-#define MENU_NC_ITEMS 8\r
-\r
-KHMEXP khui_menu_def * KHMAPI \r
-khui_menu_create(khm_int32 action)\r
-{\r
-    khui_menu_def * d;\r
-\r
-    d = PMALLOC(sizeof(*d));\r
-    ZeroMemory(d, sizeof(*d));\r
-\r
-    d->cmd = action;\r
-    d->nc_items = MENU_NC_ITEMS;\r
-    d->items = PMALLOC(sizeof(*(d->items)) * d->nc_items);\r
-\r
-    d->state = KHUI_MENUSTATE_ALLOCD;\r
-\r
-    if (action) {\r
-        int i;\r
-        EnterCriticalSection(&cs_actions);\r
-\r
-        for (i=0; i < khui_n_cust_menus; i++) {\r
-            if (khui_cust_menus[i] == NULL)\r
-                break;\r
-        }\r
-\r
-        if (i >= khui_n_cust_menus) {\r
-\r
-            if (khui_n_cust_menus + 1 >= khui_nc_cust_menus) {\r
-                khui_nc_cust_menus = UBOUNDSS(khui_n_cust_menus + 1,\r
-                                              CACT_NC_ALLOC, CACT_NC_ALLOC);\r
-                khui_cust_menus =\r
-                    PREALLOC(khui_cust_menus,\r
-                             sizeof(khui_cust_menus[0]) * khui_nc_cust_menus);\r
-            }\r
-\r
-            i = khui_n_cust_menus ++;\r
-        }\r
-\r
-        khui_cust_menus[i] = d;\r
-\r
-        LeaveCriticalSection(&cs_actions);\r
-    }\r
-\r
-    return d;\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_set_main_window(HWND hwnd) {\r
-    khui_hwnd_main = hwnd;\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_action_trigger(khm_int32 action, khui_action_context * ctx) {\r
-    khui_action_context save;\r
-\r
-    if (!khui_hwnd_main)\r
-       return;\r
-\r
-    if (ctx) {\r
-       khui_context_get(&save);\r
-\r
-       khui_context_set_indirect(ctx);\r
-    }\r
-\r
-    SendMessage(khui_hwnd_main, WM_COMMAND,\r
-               MAKEWPARAM(action, 0), (LPARAM) 0);\r
-\r
-    if (ctx) {\r
-       khui_context_set_indirect(&save);\r
-    }\r
-}\r
-\r
-KHMEXP khui_menu_def * KHMAPI \r
-khui_menu_dup(khui_menu_def * src)\r
-{\r
-    khui_menu_def * d;\r
-    size_t i;\r
-    size_t n;\r
-\r
-    EnterCriticalSection(&cs_actions);\r
-\r
-    d = khui_menu_create(src->cmd);\r
-\r
-    if (!(src->state & KHUI_MENUSTATE_ALLOCD))\r
-        n = khui_action_list_length(src->items);\r
-    else\r
-        n = src->n_items;\r
-\r
-    for (i=0; i<n; i++) {\r
-        if (src->items[i].flags & KHUI_ACTIONREF_PACTION) {\r
-            khui_menu_insert_paction(d, -1, src->items[i].p_action, src->items[i].flags);\r
-        } else {\r
-            khui_menu_insert_action(d, -1, src->items[i].action, 0);\r
-        }\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_actions);\r
-\r
-    return d;\r
-}\r
-\r
-KHMEXP void KHMAPI \r
-khui_menu_delete(khui_menu_def * d)\r
-{\r
-    int i;\r
-\r
-    /* non-allocated menus are assumed to have no pointers to other\r
-       allocated blocks */\r
-    if(!(d->state & KHUI_MENUSTATE_ALLOCD)) {\r
-        /* we shouldn't have tried to delete a constant menu */\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-        return;\r
-    }\r
-\r
-    EnterCriticalSection(&cs_actions);\r
-\r
-    for (i=0; i < khui_n_cust_menus; i++) {\r
-        if (khui_cust_menus[i] == d) {\r
-            khui_cust_menus[i] = NULL;\r
-            break;\r
-        }\r
-    }\r
-\r
-    for(i=0; i< (int) d->n_items; i++) {\r
-        if(d->items[i].flags & KHUI_ACTIONREF_FREE_PACTION)\r
-            PFREE(d->items[i].p_action);\r
-    }\r
-\r
-    if(d->items)\r
-        PFREE(d->items);\r
-    PFREE(d);\r
-\r
-    LeaveCriticalSection(&cs_actions);\r
-}\r
-\r
-static void\r
-menu_assert_size(khui_menu_def * d, size_t n)\r
-{\r
-\r
-    assert(d->state & KHUI_MENUSTATE_ALLOCD);\r
-\r
-    if(n > (int) d->nc_items) {\r
-        khui_action_ref * ni;\r
-\r
-        d->nc_items = UBOUNDSS(n, MENU_NC_ITEMS, MENU_NC_ITEMS);\r
-        ni = PMALLOC(sizeof(*(d->items)) * d->nc_items);\r
-        memcpy(ni, d->items, sizeof(*(d->items)) * d->n_items);\r
-        PFREE(d->items);\r
-        d->items = ni;\r
-    }\r
-}\r
-\r
-static void\r
-menu_const_to_allocd(khui_menu_def * d)\r
-{\r
-    khui_action_ref * olist;\r
-    khui_action_ref * nlist;\r
-    khm_size n;\r
-\r
-    assert(!(d->state & KHUI_MENUSTATE_ALLOCD));\r
-\r
-    olist = d->items;\r
-    n = khui_action_list_length(d->items);\r
-\r
-    d->nc_items = UBOUNDSS(n, MENU_NC_ITEMS, MENU_NC_ITEMS);\r
-    nlist = PMALLOC(sizeof(d->items[0]) * d->nc_items);\r
-    memcpy(nlist, olist, sizeof(d->items[0]) * n);\r
-\r
-    d->items = nlist;\r
-    d->n_items = n;\r
-    d->state |= KHUI_MENUSTATE_ALLOCD;\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_menu_insert_action(khui_menu_def * d, khm_size idx, khm_int32 action, khm_int32 flags)\r
-{\r
-    khm_size i;\r
-\r
-    EnterCriticalSection(&cs_actions);\r
-\r
-    if (!(d->state & KHUI_MENUSTATE_ALLOCD))\r
-        menu_const_to_allocd(d);\r
-\r
-    assert(d->state & KHUI_MENUSTATE_ALLOCD);\r
-    assert(action == KHUI_MENU_SEP || action > 0);\r
-\r
-    if (idx < 0 || idx > d->n_items)\r
-        idx = d->n_items;\r
-\r
-    menu_assert_size(d, d->n_items + 1);\r
-\r
-    if (idx < d->n_items) {\r
-        memmove(&d->items[idx + 1], &d->items[idx], (d->n_items - idx) * sizeof(d->items[0]));\r
-    }\r
-\r
-    d->items[idx].flags = flags;\r
-    d->items[idx].action = action;\r
-    if (action == KHUI_MENU_SEP)\r
-        d->items[idx].flags |= KHUI_ACTIONREF_SEP;\r
-\r
-    d->n_items++;\r
-\r
-    /* only one action is allowed to have the KHUI_ACTIONREF_DEFAULT\r
-       flag */\r
-    if (flags & KHUI_ACTIONREF_DEFAULT) {\r
-        for (i=0; i < d->n_items; i++) {\r
-            if (i != idx && (d->items[i].flags & KHUI_ACTIONREF_DEFAULT))\r
-                d->items[i].flags &= ~KHUI_ACTIONREF_DEFAULT;\r
-        }\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_actions);\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_menu_insert_paction(khui_menu_def * d, khm_size idx, khui_action * paction, int flags)\r
-{\r
-    khm_size i;\r
-\r
-    if (paction == NULL)\r
-        return;\r
-\r
-    EnterCriticalSection(&cs_actions);\r
-\r
-    if (!(d->state & KHUI_MENUSTATE_ALLOCD))\r
-        menu_const_to_allocd(d);\r
-\r
-    assert(d->state & KHUI_MENUSTATE_ALLOCD);\r
-\r
-    if (idx < 0 || idx > d->n_items)\r
-        idx = d->n_items;\r
-\r
-    menu_assert_size(d, d->n_items + 1);\r
-\r
-    if (idx < d->n_items) {\r
-        memmove(&d->items[idx + 1], &d->items[idx], (d->n_items - idx) * sizeof(d->items[0]));\r
-    }\r
-\r
-    d->items[idx].flags = flags | KHUI_ACTIONREF_PACTION;\r
-    d->items[idx].p_action = paction;\r
-\r
-    d->n_items++;\r
-\r
-    /* only one action is allowed to have the KHUI_ACTIONREF_DEFAULT\r
-       flag */\r
-    if (flags & KHUI_ACTIONREF_DEFAULT) {\r
-        for (i=0; i < d->n_items; i++) {\r
-            if (i != idx && (d->items[i].flags & KHUI_ACTIONREF_DEFAULT))\r
-                d->items[i].flags &= ~KHUI_ACTIONREF_DEFAULT;\r
-        }\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_actions);\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_menu_remove_action(khui_menu_def * d, khm_size idx) {\r
-\r
-    EnterCriticalSection(&cs_actions);\r
-\r
-    if (!(d->state & KHUI_MENUSTATE_ALLOCD))\r
-        menu_const_to_allocd(d);\r
-\r
-    assert(d->state & KHUI_MENUSTATE_ALLOCD);\r
-\r
-    if (idx >= 0 && idx < d->n_items) {\r
-\r
-        if (idx < d->n_items - 1) {\r
-            memmove(&d->items[idx], &d->items[idx + 1],\r
-                    ((d->n_items - 1) - idx) * sizeof(d->items[0]));\r
-        }\r
-\r
-        d->n_items--;\r
-\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_actions);\r
-}\r
-\r
-KHMEXP khm_size KHMAPI\r
-khui_menu_get_size(khui_menu_def * d) {\r
-\r
-    khm_size size;\r
-\r
-    EnterCriticalSection(&cs_actions);\r
-\r
-    if (d->state & KHUI_MENUSTATE_ALLOCD)\r
-        size = d->n_items;\r
-    else\r
-        size = khui_action_list_length(d->items);\r
-\r
-    LeaveCriticalSection(&cs_actions);\r
-\r
-    return size;\r
-}\r
-\r
-KHMEXP khui_action_ref *\r
-khui_menu_get_action(khui_menu_def * d, khm_size idx) {\r
-\r
-    khui_action_ref * act = NULL;\r
-    khm_size n;\r
-\r
-    EnterCriticalSection(&cs_actions);\r
-\r
-    if (d->state & KHUI_MENUSTATE_ALLOCD)\r
-        n = d->n_items;\r
-    else\r
-        n = khui_action_list_length(d->items);\r
-\r
-    if (idx < 0 || idx >= n)\r
-        act = NULL;\r
-    else\r
-        act = &d->items[idx];\r
-\r
-    LeaveCriticalSection(&cs_actions);\r
-\r
-    return act;\r
-}\r
-\r
-KHMEXP khui_menu_def * KHMAPI\r
-khui_find_menu(khm_int32 id) {\r
-    khui_menu_def * d;\r
-    int i;\r
-\r
-    if (id < KHUI_USERACTION_BASE) {\r
-\r
-        /* the list of system menus are considered immutable. */\r
-\r
-        d = khui_all_menus;\r
-        for(i=0;i<khui_n_all_menus;i++) {\r
-            if(id == d[i].cmd)\r
-                return &d[i];\r
-        }\r
-\r
-        return NULL;\r
-    } else {\r
-        d = NULL;\r
-\r
-        EnterCriticalSection(&cs_actions);\r
-        for (i=0; i < khui_n_cust_menus; i++) {\r
-            if (khui_cust_menus[i] &&\r
-                khui_cust_menus[i]->cmd == id) {\r
-                d = khui_cust_menus[i];\r
-                break;\r
-            }\r
-        }\r
-        LeaveCriticalSection(&cs_actions);\r
-\r
-        return d;\r
-    }\r
-}\r
-\r
-KHMEXP khui_action * KHMAPI\r
-khui_find_action(khm_int32 id) {\r
-    khui_action * act;\r
-    int i;\r
-\r
-    act = khui_actions;\r
-    for(i=0;i<khui_n_actions;i++) {\r
-        if(act[i].cmd == id)\r
-            return &act[i];\r
-    }\r
-\r
-    act = NULL;\r
-\r
-    EnterCriticalSection(&cs_actions);\r
-    if (id >= KHUI_USERACTION_BASE &&\r
-        (id - KHUI_USERACTION_BASE) < khui_n_cust_actions) {\r
-        act = khui_cust_actions[id - KHUI_USERACTION_BASE];\r
-#ifdef DEBUG\r
-        assert(!act || act->cmd == id);\r
-#endif\r
-        if (act && (act->state & KHUI_ACTIONSTATE_DELETED))\r
-            act = NULL;\r
-    }\r
-    LeaveCriticalSection(&cs_actions);\r
-\r
-    return act;\r
-}\r
-\r
-KHMEXP khui_action * KHMAPI\r
-khui_find_named_action(const wchar_t * name) {\r
-    int i;\r
-    khui_action * act;\r
-    khui_action ** pact;\r
-\r
-    if(!name)\r
-        return NULL;\r
-\r
-    act = khui_actions;\r
-    for(i=0;i<khui_n_actions;i++) {\r
-        if(!act[i].name)\r
-            continue;\r
-        if(!wcscmp(act[i].name, name))\r
-            return &act[i];\r
-    }\r
-\r
-    act = NULL;\r
-\r
-    EnterCriticalSection(&cs_actions);\r
-\r
-    pact = khui_cust_actions;\r
-    for(i=0;i<khui_n_cust_actions;i++) {\r
-        if(!pact[i] || !pact[i]->name)\r
-            continue;\r
-\r
-        if(!wcscmp(pact[i]->name, name)) {\r
-\r
-            if (!(pact[i]->state & KHUI_ACTIONSTATE_DELETED)) {\r
-                act = pact[i];\r
-            }\r
-            break;\r
-        }\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_actions);\r
-\r
-    return act;\r
-}\r
-\r
-KHMEXP size_t KHMAPI\r
-khui_action_list_length(khui_action_ref * ref) {\r
-    size_t c = 0;\r
-\r
-    EnterCriticalSection(&cs_actions);\r
-\r
-    while(ref && ref->action != KHUI_MENU_END &&\r
-          !(ref->flags & KHUI_ACTIONREF_END)) {\r
-        c++;\r
-        ref++;\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_actions);\r
-\r
-    return c;\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_check_radio_action(khui_menu_def * d, khm_int32 cmd)\r
-{\r
-    khui_action_ref * r;\r
-    khui_action * act;\r
-\r
-    EnterCriticalSection(&cs_actions);\r
-\r
-    r = d->items;\r
-    while(r && r->action != KHUI_MENU_END &&\r
-          (!(d->state & KHUI_MENUSTATE_ALLOCD) || (r - d->items) < (int) d->n_items)) {\r
-        if(r->flags & KHUI_ACTIONREF_PACTION) {\r
-            act = r->p_action;\r
-        } else {\r
-            act = khui_find_action(r->action);\r
-        }\r
-\r
-        if(act) {\r
-            if(act->cmd == cmd)\r
-                act->state |= KHUI_ACTIONSTATE_CHECKED;\r
-            else\r
-                act->state &= ~KHUI_ACTIONSTATE_CHECKED;\r
-        }\r
-        r++;\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_actions);\r
-\r
-    kmq_post_message(KMSG_ACT, KMSG_ACT_CHECK, 0, 0);\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_check_action(khm_int32 cmd, khm_boolean check) {\r
-    khui_action * act;\r
-\r
-    act = khui_find_action(cmd);\r
-    if (!act)\r
-        return;\r
-\r
-    EnterCriticalSection(&cs_actions);\r
-\r
-    if (check && !(act->state & KHUI_ACTIONSTATE_CHECKED))\r
-        act->state |= KHUI_ACTIONSTATE_CHECKED;\r
-    else if (!check && (act->state & KHUI_ACTIONSTATE_CHECKED))\r
-        act->state &= ~KHUI_ACTIONSTATE_CHECKED;\r
-    else {\r
-        LeaveCriticalSection(&cs_actions);\r
-        return;\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_actions);\r
-\r
-    kmq_post_message(KMSG_ACT, KMSG_ACT_CHECK, 0, 0);\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_enable_actions(khui_menu_def * d, khm_boolean enable)\r
-{\r
-    khui_action_ref * r;\r
-    int delta = FALSE;\r
-    khui_action * act;\r
-\r
-    EnterCriticalSection(&cs_actions);\r
-\r
-    r = d->items;\r
-    while(r && r->action != KHUI_MENU_END &&\r
-          (!(d->state & KHUI_MENUSTATE_ALLOCD) || (r - d->items) < (int) d->n_items)) {\r
-        if(r->flags & KHUI_ACTIONREF_PACTION) {\r
-            act = r->p_action;\r
-        } else {\r
-            act = khui_find_action(r->action);\r
-        }\r
-\r
-        if(act) {\r
-            int old_state = act->state;\r
-\r
-            if(enable)\r
-                act->state &= ~KHUI_ACTIONSTATE_DISABLED;\r
-            else\r
-                act->state |= KHUI_ACTIONSTATE_DISABLED;\r
-\r
-            if(old_state != act->state)\r
-                delta = TRUE;\r
-        }\r
-        r++;\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_actions);\r
-\r
-    if(delta) {\r
-        kmq_post_message(KMSG_ACT, KMSG_ACT_ENABLE, 0, 0);\r
-    }\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_enable_action(khm_int32 cmd, khm_boolean enable) {\r
-    khui_action * act;\r
-\r
-    act = khui_find_action(cmd);\r
-    if (!act)\r
-        return;\r
-\r
-    EnterCriticalSection(&cs_actions);\r
-\r
-    if (enable && (act->state & KHUI_ACTIONSTATE_DISABLED)) {\r
-        act->state &= ~KHUI_ACTIONSTATE_DISABLED;\r
-    } else if (!enable && !(act->state & KHUI_ACTIONSTATE_DISABLED)) {\r
-        act->state |= KHUI_ACTIONSTATE_DISABLED;\r
-    } else {\r
-        LeaveCriticalSection(&cs_actions);\r
-        return;\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_actions);\r
-\r
-    kmq_post_message(KMSG_ACT, KMSG_ACT_ENABLE, 0, 0);\r
-}\r
-\r
-KHMEXP HACCEL KHMAPI\r
-khui_create_global_accel_table(void) {\r
-    int i;\r
-    ACCEL * accels;\r
-    HACCEL ha;\r
-\r
-    accels = PMALLOC(sizeof(ACCEL) * khui_n_accel_global);\r
-    for(i=0;i<khui_n_accel_global;i++) {\r
-        accels[i].cmd = khui_accel_global[i].cmd;\r
-        accels[i].fVirt = khui_accel_global[i].mod;\r
-        accels[i].key = khui_accel_global[i].key;\r
-    }\r
-\r
-    ha = CreateAcceleratorTable(accels, khui_n_accel_global);\r
-\r
-    PFREE(accels);\r
-\r
-    return ha;\r
-}\r
-\r
-KHMEXP khm_boolean KHMAPI \r
-khui_get_cmd_accel_string(khm_int32 cmd, \r
-                          wchar_t * buf, \r
-                          khm_size bufsiz) {\r
-    int i;\r
-    khui_accel_def * def;\r
-\r
-    /* should at least hold 2 characters */\r
-    if(bufsiz < sizeof(wchar_t) * 2)\r
-        return FALSE;\r
-\r
-    buf[0] = L'\0';\r
-\r
-    for(i=0;i<khui_n_accel_global;i++) {\r
-        if(khui_accel_global[i].cmd == cmd)\r
-            break;\r
-    }\r
-\r
-    if(i==khui_n_accel_global)\r
-        return FALSE;\r
-\r
-    def = &khui_accel_global[i];\r
-\r
-    if(def->mod & FALT) {\r
-        if(FAILED(StringCbCat(buf, bufsiz, L"Alt+")))\r
-            return FALSE;\r
-    }\r
-\r
-\r
-    if(def->mod & FCONTROL) {\r
-        if(FAILED(StringCbCat(buf, bufsiz, L"Ctrl+")))\r
-            return FALSE;\r
-    }\r
-\r
-    if(def->mod & FSHIFT) {\r
-        if(FAILED(StringCbCat(buf, bufsiz, L"Shift+")))\r
-            return FALSE;\r
-    }\r
-\r
-    if(def->mod & FVIRTKEY) {\r
-        wchar_t mbuf[6];\r
-        wchar_t * ap = NULL;\r
-        switch(def->key) {\r
-        case VK_TAB:\r
-            ap = L"Tab";\r
-            break;\r
-\r
-        case VK_ESCAPE:\r
-            ap = L"Esc";\r
-            break;\r
-\r
-        case VK_RETURN:\r
-            ap = L"Enter";\r
-            break;\r
-\r
-        case VK_F1:\r
-            ap = L"F1";\r
-            break;\r
-\r
-        case VK_F2:\r
-            ap = L"F2";\r
-            break;\r
-\r
-        case VK_F3:\r
-            ap = L"F3";\r
-            break;\r
-\r
-        case VK_F4:\r
-            ap = L"F4";\r
-            break;\r
-\r
-        case VK_F5:\r
-            ap = L"F5";\r
-            break;\r
-\r
-        case VK_F6:\r
-            ap = L"F6";\r
-            break;\r
-\r
-        case VK_F7:\r
-            ap = L"F7";\r
-            break;\r
-\r
-        case VK_F8:\r
-            ap = L"F8";\r
-            break;\r
-\r
-        case VK_F9:\r
-            ap = L"F9";\r
-            break;\r
-\r
-        case VK_F10:\r
-            ap = L"F10";\r
-            break;\r
-\r
-        case VK_F11:\r
-            ap = L"F11";\r
-            break;\r
-\r
-        case VK_F12:\r
-            ap = L"F12";\r
-            break;\r
-\r
-        case VK_DELETE:\r
-            ap = L"Del";\r
-            break;\r
-\r
-        default:\r
-            if((def->key >= '0' && \r
-                def->key <= '9') || \r
-               (def->key >= 'A' && \r
-                def->key <= 'Z')) {\r
-                ap = mbuf;\r
-                mbuf[0] = (wchar_t) def->key;\r
-                mbuf[1] = L'\0';\r
-            }\r
-        }\r
-        if(ap) {\r
-            if(FAILED(StringCbCat(buf, bufsiz, ap)))\r
-                return FALSE;\r
-        }\r
-        else {\r
-            if(FAILED(StringCbCat(buf, bufsiz,L"???")))\r
-                return FALSE;\r
-        }\r
-\r
-    } else {\r
-        wchar_t mbuf[2];\r
-\r
-        mbuf[0] = def->key;\r
-        mbuf[1] = L'\0';\r
-\r
-        if(FAILED(StringCbCat(buf, bufsiz, mbuf)))\r
-            return FALSE;\r
-    }\r
-\r
-    return TRUE;\r
-}\r
-\r
-/******************************************/\r
-/* contexts */\r
-\r
-#define KHUI_ACTION_CONTEXT_MAGIC 0x39c49db5\r
-\r
-static khm_int32 KHMAPI\r
-khuiint_filter_selected(khm_handle cred,\r
-                        khm_int32 vflags,\r
-                        void * rock) {\r
-    khm_int32 flags;\r
-    if (KHM_SUCCEEDED(kcdb_cred_get_flags(cred, &flags)) &&\r
-        (flags & KCDB_CRED_FLAG_SELECTED))\r
-        return TRUE;\r
-    else\r
-        return FALSE;\r
-}\r
-\r
-static void\r
-khuiint_context_release(khui_action_context * ctx) {\r
-    ctx->scope = KHUI_SCOPE_NONE;\r
-    if (ctx->identity)\r
-        kcdb_identity_release(ctx->identity);\r
-    ctx->identity = NULL;\r
-    ctx->cred_type = KCDB_CREDTYPE_INVALID;\r
-    if (ctx->cred)\r
-        kcdb_cred_release(ctx->cred);\r
-    ctx->cred = NULL;\r
-    ctx->n_headers = 0;\r
-    if (ctx->credset)\r
-        kcdb_credset_flush(ctx->credset);\r
-    ctx->n_sel_creds = 0;\r
-    ctx->int_cb_used = 0;\r
-    ctx->vparam = NULL;\r
-    ctx->cb_vparam = 0;\r
-}\r
-\r
-static void\r
-khuiint_copy_context(khui_action_context * ctxdest,\r
-                     const khui_action_context * ctxsrc)\r
-{\r
-    ctxdest->scope = ctxsrc->scope;\r
-\r
-    if (ctxsrc->scope == KHUI_SCOPE_IDENT) {\r
-        ctxdest->identity = ctxsrc->identity;\r
-        kcdb_identity_hold(ctxsrc->identity);\r
-    } else if (ctxsrc->scope == KHUI_SCOPE_CREDTYPE) {\r
-        ctxdest->identity = ctxsrc->identity;\r
-        ctxdest->cred_type = ctxsrc->cred_type;\r
-        if (ctxsrc->identity != NULL)\r
-            kcdb_identity_hold(ctxsrc->identity);\r
-    } else if (ctxsrc->scope == KHUI_SCOPE_CRED) {\r
-        kcdb_cred_get_identity(ctxsrc->cred, &ctxdest->identity);\r
-        kcdb_cred_get_type(ctxsrc->cred, &ctxdest->cred_type);\r
-        ctxdest->cred = ctxsrc->cred;\r
-        kcdb_cred_hold(ctxsrc->cred);\r
-    } else if (ctxsrc->scope == KHUI_SCOPE_GROUP) {\r
-        khm_size cb_total;\r
-        int i;\r
-\r
-        ctxdest->n_headers = ctxsrc->n_headers;\r
-        cb_total = 0;\r
-        for (i=0; i < (int) ctxsrc->n_headers; i++) {\r
-            cb_total += UBOUND32(ctxsrc->headers[i].cb_data);\r
-        }\r
-\r
-        if (ctxdest->int_cb_buf < cb_total) {\r
-\r
-            if (ctxdest->int_buf)\r
-                PFREE(ctxdest->int_buf);\r
-\r
-            ctxdest->int_cb_buf = cb_total;\r
-            ctxdest->int_buf = PMALLOC(cb_total);\r
-        }\r
-\r
-#ifdef DEBUG\r
-        assert(ctxdest->int_buf || cb_total == 0);\r
-#endif\r
-        ctxdest->int_cb_used = 0;\r
-\r
-        for (i=0; i < (int) ctxsrc->n_headers; i++) {\r
-            ctxdest->headers[i].attr_id = ctxsrc->headers[i].attr_id;\r
-            ctxdest->headers[i].cb_data = ctxsrc->headers[i].cb_data;\r
-            if (ctxsrc->headers[i].cb_data > 0) {\r
-                ctxdest->headers[i].data = \r
-                    BYTEOFFSET(ctxdest->int_buf,\r
-                               ctxdest->int_cb_used);\r
-                memcpy(ctxdest->headers[i].data,\r
-                       ctxsrc->headers[i].data,\r
-                       ctxsrc->headers[i].cb_data);\r
-                ctxdest->int_cb_used += \r
-                    UBOUND32(ctxsrc->headers[i].cb_data);\r
-            } else {\r
-                ctxdest->headers[i].data = NULL;\r
-            }\r
-        }\r
-    }\r
-\r
-    if (ctxsrc->credset) {\r
-\r
-        if (ctxdest->credset == NULL)\r
-            kcdb_credset_create(&ctxdest->credset);\r
-#ifdef DEBUG\r
-        assert(ctxdest->credset != NULL);\r
-#endif\r
-\r
-        kcdb_credset_flush(ctxdest->credset);\r
-        \r
-        kcdb_credset_extract_filtered(ctxdest->credset,\r
-                                      ctxsrc->credset,\r
-                                      khuiint_filter_selected,\r
-                                      NULL);\r
-\r
-        kcdb_credset_get_size(ctxdest->credset,\r
-                              &ctxdest->n_sel_creds);\r
-    } else {\r
-        if (ctxdest->credset != NULL)\r
-            kcdb_credset_flush(ctxdest->credset);\r
-        ctxdest->n_sel_creds = 0;\r
-    }\r
-\r
-    /* For now, we simply transfer the vparam buffer into the new\r
-       context.  If we are copying, we also need to modify\r
-       khui_context_release() to free the allocated buffer */\r
-#if 0\r
-    if (ctxsrc->vparam && ctxsrc->cb_vparam) {\r
-        ctxdest->vparam = PMALLOC(ctxsrc->cb_vparam);\r
-#ifdef DEBUG\r
-        assert(ctxdest->vparam);\r
-#endif\r
-        memcpy(ctxdest->vparam, ctxsrc->vparam, ctxsrc->cb_vparam);\r
-        ctxdest->cb_vparam = ctxsrc->cb_vparam;\r
-    } else {\r
-#endif\r
-        ctxdest->vparam = ctxsrc->vparam;\r
-        ctxdest->cb_vparam = ctxsrc->cb_vparam;\r
-#if 0\r
-    }\r
-#endif\r
-}\r
-\r
-static void \r
-khuiint_context_init(khui_action_context * ctx) {\r
-    ctx->magic = KHUI_ACTION_CONTEXT_MAGIC;\r
-    ctx->scope = KHUI_SCOPE_NONE;\r
-    ctx->identity = NULL;\r
-    ctx->cred_type = KCDB_CREDTYPE_INVALID;\r
-    ctx->cred = NULL;\r
-    ZeroMemory(ctx->headers, sizeof(ctx->headers));\r
-    ctx->n_headers = 0;\r
-    ctx->credset = NULL;\r
-    ctx->n_sel_creds = 0;\r
-    ctx->int_buf = NULL;\r
-    ctx->int_cb_buf = 0;\r
-    ctx->int_cb_used = 0;\r
-    ctx->vparam = NULL;\r
-    ctx->cb_vparam = 0;\r
-}\r
-\r
-khui_action_context khui_ctx = {\r
-    KHUI_ACTION_CONTEXT_MAGIC,\r
-    KHUI_SCOPE_NONE,\r
-    NULL, \r
-    KCDB_CREDTYPE_INVALID, \r
-    NULL,\r
-    {\r
-        {KCDB_ATTR_INVALID,NULL,0},\r
-        {KCDB_ATTR_INVALID,NULL,0},\r
-        {KCDB_ATTR_INVALID,NULL,0},\r
-        {KCDB_ATTR_INVALID,NULL,0},\r
-        {KCDB_ATTR_INVALID,NULL,0},\r
-        {KCDB_ATTR_INVALID,NULL,0}\r
-    },\r
-    0,\r
-    NULL,\r
-    0,\r
-    NULL,\r
-    0,\r
-    0,\r
-    NULL,\r
-    0};\r
-\r
-khm_int32 KHMAPI\r
-set_cred_select_flag(khm_handle cred, void * rock) {\r
-    kcdb_cred_set_flags(cred, KCDB_CRED_FLAG_SELECTED,\r
-                        KCDB_CRED_FLAG_SELECTED);\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_context_create(khui_action_context * ctx,\r
-                    khui_scope scope,\r
-                    khm_handle identity,\r
-                    khm_int32 cred_type,\r
-                    khm_handle cred)\r
-{\r
-    khui_action_context tctx;\r
-\r
-    khuiint_context_init(&tctx);\r
-    khuiint_context_init(ctx);\r
-\r
-    tctx.scope = scope;\r
-    tctx.identity = identity;\r
-    tctx.cred_type = cred_type;\r
-    tctx.cred = cred;\r
-\r
-    /* fill up the credset based on the scope */\r
-    if (scope != KHUI_SCOPE_NONE) {\r
-        if (tctx.credset == NULL)\r
-            kcdb_credset_create(&tctx.credset);\r
-        else\r
-            kcdb_credset_flush(tctx.credset);\r
-\r
-        if (scope == KHUI_SCOPE_IDENT) {\r
-            kcdb_credset_extract(tctx.credset,\r
-                                 NULL,\r
-                                 tctx.identity,\r
-                                 KCDB_CREDTYPE_INVALID);\r
-        } else if (scope == KHUI_SCOPE_CREDTYPE) {\r
-            kcdb_credset_extract(tctx.credset,\r
-                                 NULL,\r
-                                 tctx.identity,\r
-                                 tctx.cred_type);\r
-        } else if (scope == KHUI_SCOPE_CRED) {\r
-            khm_handle dupcred = NULL;\r
-            kcdb_cred_dup(cred, &dupcred);\r
-\r
-            kcdb_credset_add_cred(tctx.credset, dupcred, -1);\r
-        } else {\r
-#ifdef DEBUG\r
-            /* KHUI_SCOPE_GROUP is not used with\r
-               khui_context_create() */\r
-            assert(FALSE);\r
-#endif\r
-        }\r
-\r
-        kcdb_credset_apply(tctx.credset, set_cred_select_flag,\r
-                           NULL);\r
-\r
-        kcdb_credset_seal(tctx.credset);\r
-    }\r
-\r
-    khuiint_copy_context(ctx, &tctx);\r
-}\r
-\r
-KHMEXP void KHMAPI \r
-khui_context_set(khui_scope scope, \r
-                 khm_handle identity, \r
-                 khm_int32 cred_type, \r
-                 khm_handle cred,\r
-                 khui_header *headers,\r
-                 khm_size n_headers,\r
-                 khm_handle cs_src) {\r
-\r
-    khui_context_set_ex(scope,\r
-                        identity,\r
-                        cred_type,\r
-                        cred,\r
-                        headers,\r
-                        n_headers,\r
-                        cs_src,\r
-                        NULL,\r
-                        0);\r
-}\r
-\r
-KHMEXP void KHMAPI \r
-khui_context_set_ex(khui_scope scope, \r
-                    khm_handle identity, \r
-                    khm_int32 cred_type, \r
-                    khm_handle cred,\r
-                    khui_header *headers,\r
-                    khm_size n_headers,\r
-                    khm_handle cs_src,\r
-                    void * vparam,\r
-                    khm_size cb_vparam)\r
-{\r
-    khui_action_context tctx;\r
-\r
-    EnterCriticalSection(&cs_actions);\r
-\r
-    khuiint_context_release(&khui_ctx);\r
-\r
-    khuiint_context_init(&tctx);\r
-\r
-    tctx.scope = scope;\r
-    tctx.identity = identity;\r
-    tctx.cred_type = cred_type;\r
-    tctx.cred = cred;\r
-    if (headers) {\r
-        tctx.n_headers = n_headers;\r
-        memcpy(tctx.headers,\r
-               headers,\r
-               sizeof(*headers) * n_headers);\r
-    } else {\r
-        tctx.n_headers = 0;\r
-    }\r
-    tctx.credset = cs_src;\r
-    tctx.n_sel_creds = 0;       /* ignored */\r
-    tctx.vparam = vparam;\r
-    tctx.cb_vparam = cb_vparam;\r
-    tctx.int_buf = NULL;\r
-    tctx.int_cb_buf = 0;\r
-    tctx.int_cb_used = 0;\r
-\r
-    khuiint_copy_context(&khui_ctx, &tctx);\r
-\r
-    khui_context_refresh();\r
-\r
-    LeaveCriticalSection(&cs_actions);\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_context_set_indirect(khui_action_context * ctx)\r
-{\r
-    EnterCriticalSection(&cs_actions);\r
-\r
-    khuiint_context_release(&khui_ctx);\r
-\r
-    khuiint_copy_context(&khui_ctx, ctx);\r
-\r
-    khui_context_refresh();\r
-\r
-    LeaveCriticalSection(&cs_actions);\r
-}\r
-\r
-KHMEXP void KHMAPI \r
-khui_context_refresh(void) {\r
-    khm_int32 flags;\r
-\r
-    EnterCriticalSection(&cs_actions);\r
-    if (khui_ctx.identity) {\r
-        /* an identity is selected */\r
-\r
-        if (KHM_SUCCEEDED(kcdb_identity_get_flags(khui_ctx.identity,\r
-                                                  &flags)) &&\r
-            (flags & KCDB_IDENT_FLAG_DEFAULT)) {\r
-            khui_check_action(KHUI_ACTION_SET_DEF_ID, TRUE);\r
-            khui_enable_action(KHUI_ACTION_SET_DEF_ID, FALSE);\r
-        } else {\r
-            khui_check_action(KHUI_ACTION_SET_DEF_ID, FALSE);\r
-            khui_enable_action(KHUI_ACTION_SET_DEF_ID, TRUE);\r
-        }\r
-    } else {\r
-        khui_check_action(KHUI_ACTION_SET_DEF_ID, FALSE);\r
-        khui_enable_action(KHUI_ACTION_SET_DEF_ID, FALSE);\r
-    }\r
-\r
-    if (khui_ctx.scope != KHUI_SCOPE_NONE) {\r
-        khui_enable_action(KHUI_ACTION_PROPERTIES, TRUE);\r
-    } else {\r
-        khui_enable_action(KHUI_ACTION_PROPERTIES, FALSE);\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_actions);\r
-\r
-    kmq_post_message(KMSG_ACT, KMSG_ACT_REFRESH, 0, 0);\r
-}\r
-\r
-KHMEXP void KHMAPI \r
-khui_context_get(khui_action_context * ctx)\r
-{\r
-    EnterCriticalSection(&cs_actions);\r
-\r
-    khuiint_context_init(ctx);\r
-    khuiint_copy_context(ctx, &khui_ctx);\r
-\r
-    if (ctx->credset) {\r
-        kcdb_credset_seal(ctx->credset);\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_actions);\r
-}\r
-\r
-KHMEXP void KHMAPI \r
-khui_context_release(khui_action_context * ctx)\r
-{\r
-#ifdef DEBUG\r
-    assert(ctx->magic == KHUI_ACTION_CONTEXT_MAGIC);\r
-#endif\r
-\r
-    khuiint_context_release(ctx);\r
-    if (ctx->credset) {\r
-        kcdb_credset_unseal(ctx->credset);\r
-        kcdb_credset_delete(ctx->credset);\r
-    }\r
-    ctx->credset = NULL;\r
-    if (ctx->int_buf)\r
-        PFREE(ctx->int_buf);\r
-    ctx->int_buf = NULL;\r
-#if 0\r
-    if (ctx->vparam && ctx->cb_vparam > 0) {\r
-        PFREE(ctx->vparam);\r
-        ctx->vparam = NULL;\r
-    }\r
-    ctx->cb_vparam = 0;\r
-#else\r
-    ctx->vparam = 0;\r
-    ctx->cb_vparam = 0;\r
-#endif\r
-}\r
-\r
-KHMEXP void KHMAPI \r
-khui_context_reset(void)\r
-{\r
-    EnterCriticalSection(&cs_actions);\r
-\r
-    khuiint_context_release(&khui_ctx);\r
-\r
-    khui_context_refresh();\r
-\r
-    LeaveCriticalSection(&cs_actions);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_context_cursor_filter(khm_handle cred,\r
-                           khm_int32 flags,\r
-                           void * rock) {\r
-    khui_action_context * ctx = (khui_action_context *) rock;\r
-    khm_int32 rv;\r
-\r
-    if (ctx->scope == KHUI_SCOPE_NONE)\r
-        return 0;\r
-    else if (ctx->scope == KHUI_SCOPE_IDENT) {\r
-        khm_handle c_ident;\r
-\r
-        if (KHM_FAILED(kcdb_cred_get_identity(cred, &c_ident)))\r
-            return 0;\r
-\r
-        rv = (c_ident == ctx->identity);\r
-\r
-        kcdb_identity_release(c_ident);\r
-\r
-        return rv;\r
-    } else if (ctx->scope == KHUI_SCOPE_CREDTYPE) {\r
-        khm_handle c_ident;\r
-        khm_int32 c_type;\r
-\r
-        if (KHM_FAILED(kcdb_cred_get_type(cred, &c_type)) ||\r
-            c_type != ctx->cred_type)\r
-            return 0;\r
-\r
-        if (ctx->identity == NULL)\r
-            return 1;\r
-\r
-        if (KHM_FAILED(kcdb_cred_get_identity(cred, &c_ident)))\r
-            return 0;\r
-\r
-        rv = (c_ident == ctx->identity);\r
-\r
-        kcdb_identity_release(c_ident);\r
-\r
-        return rv;\r
-    } else if (ctx->scope == KHUI_SCOPE_CRED) {\r
-        return kcdb_creds_is_equal(cred, ctx->cred);\r
-    } else if (ctx->scope == KHUI_SCOPE_GROUP) {\r
-        int i;\r
-\r
-        rv = 1;\r
-\r
-        for (i=0; i < (int) ctx->n_headers && rv; i++) {\r
-            kcdb_attrib * pattr;\r
-            kcdb_type * ptype;\r
-            DWORD buffer[1024]; /* 4096 bytes */\r
-            khm_size cb;\r
-\r
-            if (kcdb_cred_get_attr(cred, ctx->headers[i].attr_id,\r
-                                   NULL,\r
-                                   NULL,\r
-                                   &cb) != KHM_ERROR_TOO_LONG) {\r
-                /* the header doesn't exist anyway */\r
-                rv = (ctx->headers[i].cb_data == 0);\r
-                continue;\r
-            }\r
-#ifdef DEBUG\r
-            assert(cb <= sizeof(buffer));\r
-#endif\r
-            cb = sizeof(buffer);\r
-\r
-            if (KHM_FAILED(kcdb_cred_get_attr(cred,\r
-                                              ctx->headers[i].attr_id,\r
-                                              NULL,\r
-                                              (void *) buffer,\r
-                                              &cb))) {\r
-                rv = 0;\r
-                continue;\r
-            }\r
-\r
-            if (KHM_FAILED(kcdb_attrib_get_info(ctx->headers[i].attr_id,\r
-                                                &pattr))) {\r
-                rv = 0;\r
-                continue;\r
-            }\r
-\r
-            if (KHM_FAILED(kcdb_type_get_info(pattr->type, &ptype))) {\r
-                rv = 0;\r
-                kcdb_attrib_release_info(pattr);\r
-                continue;\r
-            }\r
-\r
-            if ((*ptype->comp)(ctx->headers[i].data,\r
-                               ctx->headers[i].cb_data,\r
-                               (void *) buffer,\r
-                               cb) != 0)\r
-                rv = 1;\r
-\r
-            kcdb_type_release_info(ptype);\r
-            kcdb_attrib_release_info(pattr);\r
-        }\r
-\r
-        return rv;\r
-    } else\r
-        return 0;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<intaction.h>
+#include<utils.h>
+#include<assert.h>
+
+#include<strsafe.h>
+
+khui_action_ref khui_main_menu[] = {
+    MENU_SUBMENU(KHUI_MENU_FILE),
+    MENU_SUBMENU(KHUI_MENU_CRED),
+    MENU_SUBMENU(KHUI_MENU_VIEW),
+    MENU_SUBMENU(KHUI_MENU_OPTIONS),
+    MENU_SUBMENU(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_SUBMENU(KHUI_MENU_RENEW_CRED),
+    MENU_ACTION(KHUI_ACTION_IMPORT),
+    MENU_SUBMENU(KHUI_MENU_DESTROY_CRED),
+    MENU_SEP(),
+    MENU_ACTION(KHUI_ACTION_SET_DEF_ID),
+#if 0
+    /* not implemented yet */
+    MENU_ACTION(KHUI_ACTION_SET_SRCH_ID),
+#endif
+    MENU_SEP(),
+    MENU_ACTION(KHUI_ACTION_PASSWD_ID),
+    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_ACTION(KHUI_ACTION_LAYOUT_CUST),
+    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_LAYOUT_MINI),
+    MENU_SUBMENU(KHUI_MENU_COLUMNS),
+    MENU_SUBMENU(KHUI_MENU_LAYOUT),
+#if 0
+    /* not implemented yet */
+    MENU_SUBMENU(KHUI_MENU_TOOLBARS),
+#endif
+    MENU_SEP(),
+#if 0
+    /* not implemented yet */
+    MENU_ACTION(KHUI_ACTION_DEBUG_WINDOW),
+    MENU_SEP(),
+#endif
+    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_APPEAR),
+    MENU_ACTION(KHUI_ACTION_OPT_IDENTS),
+    MENU_ACTION(KHUI_ACTION_OPT_NOTIF),
+    MENU_ACTION(KHUI_ACTION_OPT_PLUGINS),
+    MENU_SEP(),
+    MENU_END()
+};
+
+khui_action_ref khui_menu_help[] = {
+    MENU_ACTION(KHUI_ACTION_HELP_CTX),
+    MENU_SEP(),
+    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_SUBMENU(KHUI_ACTION_RENEW_CRED),
+    MENU_ACTION(KHUI_ACTION_IMPORT),
+    MENU_SUBMENU(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_SUBMENU(KHUI_MENU_RENEW_CRED),
+    MENU_ACTION(KHUI_ACTION_IMPORT),
+    MENU_SUBMENU(KHUI_MENU_DESTROY_CRED),
+    MENU_SEP(),
+    MENU_ACTION(KHUI_ACTION_PASSWD_ID),
+    MENU_SEP(),
+    MENU_ACTION(KHUI_ACTION_HELP_CTX),
+    MENU_ACTION(KHUI_ACTION_HELP_ABOUT),
+    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_SUBMENU(KHUI_MENU_RENEW_CRED),
+    MENU_ACTION(KHUI_ACTION_IMPORT),
+    MENU_SUBMENU(KHUI_MENU_DESTROY_CRED),
+    MENU_SEP(),
+    MENU_ACTION(KHUI_ACTION_PASSWD_ID),
+    MENU_SEP(),
+    MENU_ACTION(KHUI_ACTION_HELP_CTX),
+    MENU_ACTION(KHUI_ACTION_HELP_ABOUT),
+    MENU_SEP(),
+    MENU_ACTION(KHUI_ACTION_EXIT),
+    MENU_END()
+};
+
+khui_action_ref khui_menu_cwheader_ctx[] = {
+    MENU_SUBMENU(KHUI_MENU_COLUMNS),
+    MENU_SUBMENU(KHUI_MENU_LAYOUT),
+    MENU_END()
+};
+
+khui_action_ref khui_menu_columns[] = {
+    MENU_END()
+};
+
+khui_action_ref khui_menu_destroy_cred[] = {
+    MENU_DEFACTION(KHUI_ACTION_DESTROY_ALL),
+    MENU_END()
+};
+
+khui_action_ref khui_menu_renew_cred[] = {
+    MENU_DEFACTION(KHUI_ACTION_RENEW_ALL),
+    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_MENUSTATE_SYSTEM, khui_main_menu),
+    CONSTMENU(KHUI_MENU_FILE, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_file),
+    CONSTMENU(KHUI_MENU_CRED, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_cred),
+    CONSTMENU(KHUI_MENU_VIEW, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_view),
+    CONSTMENU(KHUI_MENU_LAYOUT, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_layout),
+    CONSTMENU(KHUI_MENU_TOOLBARS, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_toolbars),
+    CONSTMENU(KHUI_MENU_OPTIONS, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_options),
+    CONSTMENU(KHUI_MENU_HELP, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_help),
+    CONSTMENU(KHUI_MENU_COLUMNS, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_columns),
+    CONSTMENU(KHUI_MENU_RENEW_CRED, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_renew_cred),
+    CONSTMENU(KHUI_MENU_DESTROY_CRED, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, khui_menu_destroy_cred),
+
+    /* toolbars */
+    CONSTMENU(KHUI_TOOLBAR_STANDARD, KHUI_MENUSTATE_CONSTANT | KHUI_MENUSTATE_SYSTEM, 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),
+    CONSTMENU(KHUI_MENU_CWHEADER_CTX, KHUI_MENUSTATE_CONSTANT, khui_menu_cwheader_ctx),
+
+    /* 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);
+khui_menu_def ** khui_cust_menus = NULL;
+int khui_nc_cust_menus = 0;
+int khui_n_cust_menus = 0;
+CRITICAL_SECTION cs_actions;
+
+#define CACT_NC_ALLOC 32
+
+khui_action ** khui_cust_actions = NULL;
+int khui_nc_cust_actions = 0;
+int khui_n_cust_actions = 0;
+
+HWND khui_hwnd_main;                   /* main window, for notifying
+                                           action launches and
+                                           dispatching messages to the
+                                           application. */
+
+KHMEXP void KHMAPI 
+khui_init_actions(void) {
+    InitializeCriticalSection(&cs_actions);
+}
+
+KHMEXP void KHMAPI 
+khui_exit_actions(void) {
+    DeleteCriticalSection(&cs_actions);
+}
+
+KHMEXP void KHMAPI
+khui_refresh_actions(void) {
+    kmq_post_message(KMSG_ACT, KMSG_ACT_REFRESH, 0, 0);
+}
+
+KHMEXP void KHMAPI
+khui_action_lock(void) {
+    EnterCriticalSection(&cs_actions);
+}
+
+KHMEXP void KHMAPI
+khui_action_unlock(void) {
+    LeaveCriticalSection(&cs_actions);
+}
+
+KHMEXP khm_int32 KHMAPI
+khui_action_create(const wchar_t * name,
+                   const wchar_t * caption,
+                   const wchar_t * tooltip,
+                   void * userdata,
+                   khm_int32 type,
+                   khm_handle hsub) {
+    khui_action * act;
+    khm_int32 action = 0;
+    int i;
+    size_t s;
+
+    if ((name && FAILED(StringCchLength(name, KHUI_MAXCCH_NAME, &s))) ||
+        !caption ||
+        FAILED(StringCchLength(caption, KHUI_MAXCCH_SHORT_DESC, &s)) ||
+        (tooltip && FAILED(StringCchLength(tooltip, KHUI_MAXCCH_SHORT_DESC, &s))) ||
+        (type != KHUI_ACTIONTYPE_TRIGGER && type != KHUI_ACTIONTYPE_TOGGLE)) {
+        return 0;
+    }
+
+    EnterCriticalSection(&cs_actions);
+    if (name && (act = khui_find_named_action(name))) {
+        /* named action already exists */
+        action = act->cmd;
+        goto _done;
+    }
+
+    for (i=0; i < khui_n_cust_actions; i++) {
+        if (khui_cust_actions[i] == NULL ||
+            (khui_cust_actions[i]->state & KHUI_ACTIONSTATE_DELETED))
+            break;
+    }
+
+    if (i >= khui_n_cust_actions &&
+        (khui_cust_actions == NULL ||
+         khui_n_cust_actions + 1 > khui_nc_cust_actions)) {
+
+        khui_nc_cust_actions = UBOUNDSS(khui_n_cust_actions + 1,
+                                        CACT_NC_ALLOC,
+                                        CACT_NC_ALLOC);
+#ifdef DEBUG
+        assert(khui_nc_cust_actions > khui_n_cust_actions + 1);
+#endif
+        khui_cust_actions = PREALLOC(khui_cust_actions,
+                                     sizeof(*khui_cust_actions) * khui_nc_cust_actions);
+#ifdef DEBUG
+        assert(khui_cust_actions);
+#endif
+    }
+
+    if (i >= khui_n_cust_actions) {
+        i = khui_n_cust_actions ++;
+        act = PMALLOC(sizeof(khui_action));
+    } else {
+        act = khui_cust_actions[i];
+        if (act == NULL)
+            act = PMALLOC(sizeof(khui_action));
+    }
+
+#ifdef DEBUG
+    assert(act);
+#endif
+
+    khui_cust_actions[i] = act;
+
+    ZeroMemory(act, sizeof(*act));
+
+    act->cmd = KHUI_USERACTION_BASE + i;
+    act->type = type;
+    act->name = (name? PWCSDUP(name) : 0);
+    act->caption = PWCSDUP(caption);
+    act->tooltip = (tooltip? PWCSDUP(tooltip) : 0);
+    act->listener = hsub;
+    act->data = userdata;
+    act->state = 0;
+
+    action = act->cmd;
+
+ _done:
+    LeaveCriticalSection(&cs_actions);
+
+    if (action)
+        kmq_post_message(KMSG_ACT, KMSG_ACT_NEW, action, NULL);
+
+    return action;
+}
+
+KHMEXP void * KHMAPI
+khui_action_get_data(khm_int32 action) {
+    khui_action * act;
+    void * data;
+
+    EnterCriticalSection(&cs_actions);
+    act = khui_find_action(action);
+    if (act == NULL || (act->state & KHUI_ACTIONSTATE_DELETED))
+        data = NULL;
+    else
+        data = act->data;
+    LeaveCriticalSection(&cs_actions);
+
+    return data;
+}
+
+KHMEXP void KHMAPI
+khui_action_delete(khm_int32 action) {
+    khui_action * act;
+
+    EnterCriticalSection(&cs_actions);
+
+    act = khui_find_action(action);
+
+    if (act == NULL) {
+        LeaveCriticalSection(&cs_actions);
+        return;
+    }
+
+    /* for the moment, even when the action is deleted, we don't free
+       up the block of memory used by the khui_action structure.  When
+       a new action is created, it will reuse deleted action
+       structures. */
+    act->state |= KHUI_ACTIONSTATE_DELETED;
+    if (act->name)
+        PFREE(act->name);
+    if (act->caption)
+        PFREE(act->caption);
+    if (act->tooltip)
+        PFREE(act->tooltip);
+    if (act->listener)
+        kmq_delete_subscription(act->listener);
+    act->name = NULL;
+    act->caption = NULL;
+    act->tooltip = NULL;
+    act->listener = NULL;
+    LeaveCriticalSection(&cs_actions);
+
+    kmq_post_message(KMSG_ACT, KMSG_ACT_DELETE, action, NULL);
+}
+
+#define MENU_NC_ITEMS 8
+
+KHMEXP khui_menu_def * KHMAPI 
+khui_menu_create(khm_int32 action)
+{
+    khui_menu_def * d;
+
+    d = PMALLOC(sizeof(*d));
+    ZeroMemory(d, sizeof(*d));
+
+    d->cmd = action;
+    d->nc_items = MENU_NC_ITEMS;
+    d->items = PMALLOC(sizeof(*(d->items)) * d->nc_items);
+
+    d->state = KHUI_MENUSTATE_ALLOCD;
+
+    if (action) {
+        int i;
+        EnterCriticalSection(&cs_actions);
+
+        for (i=0; i < khui_n_cust_menus; i++) {
+            if (khui_cust_menus[i] == NULL)
+                break;
+        }
+
+        if (i >= khui_n_cust_menus) {
+
+            if (khui_n_cust_menus + 1 >= khui_nc_cust_menus) {
+                khui_nc_cust_menus = UBOUNDSS(khui_n_cust_menus + 1,
+                                              CACT_NC_ALLOC, CACT_NC_ALLOC);
+                khui_cust_menus =
+                    PREALLOC(khui_cust_menus,
+                             sizeof(khui_cust_menus[0]) * khui_nc_cust_menus);
+            }
+
+            i = khui_n_cust_menus ++;
+        }
+
+        khui_cust_menus[i] = d;
+
+        LeaveCriticalSection(&cs_actions);
+    }
+
+    return d;
+}
+
+KHMEXP void KHMAPI
+khui_set_main_window(HWND hwnd) {
+    khui_hwnd_main = hwnd;
+}
+
+KHMEXP void KHMAPI
+khui_action_trigger(khm_int32 action, khui_action_context * ctx) {
+    khui_action_context save;
+
+    if (!khui_hwnd_main)
+       return;
+
+    if (ctx) {
+       khui_context_get(&save);
+
+       khui_context_set_indirect(ctx);
+    }
+
+    SendMessage(khui_hwnd_main, WM_COMMAND,
+               MAKEWPARAM(action, 0), (LPARAM) 0);
+
+    if (ctx) {
+       khui_context_set_indirect(&save);
+    }
+}
+
+KHMEXP khui_menu_def * KHMAPI 
+khui_menu_dup(khui_menu_def * src)
+{
+    khui_menu_def * d;
+    size_t i;
+    size_t n;
+
+    EnterCriticalSection(&cs_actions);
+
+    d = khui_menu_create(src->cmd);
+
+    if (!(src->state & KHUI_MENUSTATE_ALLOCD))
+        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_insert_paction(d, -1, src->items[i].p_action, src->items[i].flags);
+        } else {
+            khui_menu_insert_action(d, -1, src->items[i].action, 0);
+        }
+    }
+
+    LeaveCriticalSection(&cs_actions);
+
+    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)) {
+        /* we shouldn't have tried to delete a constant menu */
+#ifdef DEBUG
+        assert(FALSE);
+#endif
+        return;
+    }
+
+    EnterCriticalSection(&cs_actions);
+
+    for (i=0; i < khui_n_cust_menus; i++) {
+        if (khui_cust_menus[i] == d) {
+            khui_cust_menus[i] = NULL;
+            break;
+        }
+    }
+
+    for(i=0; i< (int) d->n_items; i++) {
+        if(d->items[i].flags & KHUI_ACTIONREF_FREE_PACTION)
+            PFREE(d->items[i].p_action);
+    }
+
+    if(d->items)
+        PFREE(d->items);
+    PFREE(d);
+
+    LeaveCriticalSection(&cs_actions);
+}
+
+static void
+menu_assert_size(khui_menu_def * d, size_t n)
+{
+
+    assert(d->state & KHUI_MENUSTATE_ALLOCD);
+
+    if(n > (int) d->nc_items) {
+        khui_action_ref * ni;
+
+        d->nc_items = UBOUNDSS(n, MENU_NC_ITEMS, MENU_NC_ITEMS);
+        ni = PMALLOC(sizeof(*(d->items)) * d->nc_items);
+        memcpy(ni, d->items, sizeof(*(d->items)) * d->n_items);
+        PFREE(d->items);
+        d->items = ni;
+    }
+}
+
+static void
+menu_const_to_allocd(khui_menu_def * d)
+{
+    khui_action_ref * olist;
+    khui_action_ref * nlist;
+    khm_size n;
+
+    assert(!(d->state & KHUI_MENUSTATE_ALLOCD));
+
+    olist = d->items;
+    n = khui_action_list_length(d->items);
+
+    d->nc_items = UBOUNDSS(n, MENU_NC_ITEMS, MENU_NC_ITEMS);
+    nlist = PMALLOC(sizeof(d->items[0]) * d->nc_items);
+    memcpy(nlist, olist, sizeof(d->items[0]) * n);
+
+    d->items = nlist;
+    d->n_items = n;
+    d->state |= KHUI_MENUSTATE_ALLOCD;
+}
+
+KHMEXP void KHMAPI
+khui_menu_insert_action(khui_menu_def * d, khm_size idx, khm_int32 action, khm_int32 flags)
+{
+    khm_size i;
+
+    EnterCriticalSection(&cs_actions);
+
+    if (!(d->state & KHUI_MENUSTATE_ALLOCD))
+        menu_const_to_allocd(d);
+
+    assert(d->state & KHUI_MENUSTATE_ALLOCD);
+    assert(action == KHUI_MENU_SEP || action > 0);
+
+    if (idx < 0 || idx > d->n_items)
+        idx = d->n_items;
+
+    menu_assert_size(d, d->n_items + 1);
+
+    if (idx < d->n_items) {
+        memmove(&d->items[idx + 1], &d->items[idx], (d->n_items - idx) * sizeof(d->items[0]));
+    }
+
+    d->items[idx].flags = flags;
+    d->items[idx].action = action;
+    if (action == KHUI_MENU_SEP)
+        d->items[idx].flags |= KHUI_ACTIONREF_SEP;
+
+    d->n_items++;
+
+    /* only one action is allowed to have the KHUI_ACTIONREF_DEFAULT
+       flag */
+    if (flags & KHUI_ACTIONREF_DEFAULT) {
+        for (i=0; i < d->n_items; i++) {
+            if (i != idx && (d->items[i].flags & KHUI_ACTIONREF_DEFAULT))
+                d->items[i].flags &= ~KHUI_ACTIONREF_DEFAULT;
+        }
+    }
+
+    LeaveCriticalSection(&cs_actions);
+}
+
+KHMEXP void KHMAPI
+khui_menu_insert_paction(khui_menu_def * d, khm_size idx, khui_action * paction, int flags)
+{
+    khm_size i;
+
+    if (paction == NULL)
+        return;
+
+    EnterCriticalSection(&cs_actions);
+
+    if (!(d->state & KHUI_MENUSTATE_ALLOCD))
+        menu_const_to_allocd(d);
+
+    assert(d->state & KHUI_MENUSTATE_ALLOCD);
+
+    if (idx < 0 || idx > d->n_items)
+        idx = d->n_items;
+
+    menu_assert_size(d, d->n_items + 1);
+
+    if (idx < d->n_items) {
+        memmove(&d->items[idx + 1], &d->items[idx], (d->n_items - idx) * sizeof(d->items[0]));
+    }
+
+    d->items[idx].flags = flags | KHUI_ACTIONREF_PACTION;
+    d->items[idx].p_action = paction;
+
+    d->n_items++;
+
+    /* only one action is allowed to have the KHUI_ACTIONREF_DEFAULT
+       flag */
+    if (flags & KHUI_ACTIONREF_DEFAULT) {
+        for (i=0; i < d->n_items; i++) {
+            if (i != idx && (d->items[i].flags & KHUI_ACTIONREF_DEFAULT))
+                d->items[i].flags &= ~KHUI_ACTIONREF_DEFAULT;
+        }
+    }
+
+    LeaveCriticalSection(&cs_actions);
+}
+
+KHMEXP void KHMAPI
+khui_menu_remove_action(khui_menu_def * d, khm_size idx) {
+
+    EnterCriticalSection(&cs_actions);
+
+    if (!(d->state & KHUI_MENUSTATE_ALLOCD))
+        menu_const_to_allocd(d);
+
+    assert(d->state & KHUI_MENUSTATE_ALLOCD);
+
+    if (idx >= 0 && idx < d->n_items) {
+
+        if (idx < d->n_items - 1) {
+            memmove(&d->items[idx], &d->items[idx + 1],
+                    ((d->n_items - 1) - idx) * sizeof(d->items[0]));
+        }
+
+        d->n_items--;
+
+    }
+
+    LeaveCriticalSection(&cs_actions);
+}
+
+KHMEXP khm_size KHMAPI
+khui_menu_get_size(khui_menu_def * d) {
+
+    khm_size size;
+
+    EnterCriticalSection(&cs_actions);
+
+    if (d->state & KHUI_MENUSTATE_ALLOCD)
+        size = d->n_items;
+    else
+        size = khui_action_list_length(d->items);
+
+    LeaveCriticalSection(&cs_actions);
+
+    return size;
+}
+
+KHMEXP khui_action_ref *
+khui_menu_get_action(khui_menu_def * d, khm_size idx) {
+
+    khui_action_ref * act = NULL;
+    khm_size n;
+
+    EnterCriticalSection(&cs_actions);
+
+    if (d->state & KHUI_MENUSTATE_ALLOCD)
+        n = d->n_items;
+    else
+        n = khui_action_list_length(d->items);
+
+    if (idx < 0 || idx >= n)
+        act = NULL;
+    else
+        act = &d->items[idx];
+
+    LeaveCriticalSection(&cs_actions);
+
+    return act;
+}
+
+KHMEXP khui_menu_def * KHMAPI
+khui_find_menu(khm_int32 id) {
+    khui_menu_def * d;
+    int i;
+
+    if (id < KHUI_USERACTION_BASE) {
+
+        /* the list of system menus are considered immutable. */
+
+        d = khui_all_menus;
+        for(i=0;i<khui_n_all_menus;i++) {
+            if(id == d[i].cmd)
+                return &d[i];
+        }
+
+        return NULL;
+    } else {
+        d = NULL;
+
+        EnterCriticalSection(&cs_actions);
+        for (i=0; i < khui_n_cust_menus; i++) {
+            if (khui_cust_menus[i] &&
+                khui_cust_menus[i]->cmd == id) {
+                d = khui_cust_menus[i];
+                break;
+            }
+        }
+        LeaveCriticalSection(&cs_actions);
+
+        return d;
+    }
+}
+
+KHMEXP khui_action * KHMAPI
+khui_find_action(khm_int32 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];
+    }
+
+    act = NULL;
+
+    EnterCriticalSection(&cs_actions);
+    if (id >= KHUI_USERACTION_BASE &&
+        (id - KHUI_USERACTION_BASE) < khui_n_cust_actions) {
+        act = khui_cust_actions[id - KHUI_USERACTION_BASE];
+#ifdef DEBUG
+        assert(!act || act->cmd == id);
+#endif
+        if (act && (act->state & KHUI_ACTIONSTATE_DELETED))
+            act = NULL;
+    }
+    LeaveCriticalSection(&cs_actions);
+
+    return act;
+}
+
+KHMEXP khui_action * KHMAPI
+khui_find_named_action(const wchar_t * name) {
+    int i;
+    khui_action * act;
+    khui_action ** pact;
+
+    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];
+    }
+
+    act = NULL;
+
+    EnterCriticalSection(&cs_actions);
+
+    pact = khui_cust_actions;
+    for(i=0;i<khui_n_cust_actions;i++) {
+        if(!pact[i] || !pact[i]->name)
+            continue;
+
+        if(!wcscmp(pact[i]->name, name)) {
+
+            if (!(pact[i]->state & KHUI_ACTIONSTATE_DELETED)) {
+                act = pact[i];
+            }
+            break;
+        }
+    }
+
+    LeaveCriticalSection(&cs_actions);
+
+    return act;
+}
+
+KHMEXP size_t KHMAPI
+khui_action_list_length(khui_action_ref * ref) {
+    size_t c = 0;
+
+    EnterCriticalSection(&cs_actions);
+
+    while(ref && ref->action != KHUI_MENU_END &&
+          !(ref->flags & KHUI_ACTIONREF_END)) {
+        c++;
+        ref++;
+    }
+
+    LeaveCriticalSection(&cs_actions);
+
+    return c;
+}
+
+KHMEXP void KHMAPI
+khui_check_radio_action(khui_menu_def * d, khm_int32 cmd)
+{
+    khui_action_ref * r;
+    khui_action * act;
+
+    EnterCriticalSection(&cs_actions);
+
+    r = d->items;
+    while(r && r->action != KHUI_MENU_END &&
+          (!(d->state & KHUI_MENUSTATE_ALLOCD) || (r - d->items) < (int) d->n_items)) {
+        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++;
+    }
+
+    LeaveCriticalSection(&cs_actions);
+
+    kmq_post_message(KMSG_ACT, KMSG_ACT_CHECK, 0, 0);
+}
+
+KHMEXP void KHMAPI
+khui_check_action(khm_int32 cmd, khm_boolean check) {
+    khui_action * act;
+
+    act = khui_find_action(cmd);
+    if (!act)
+        return;
+
+    EnterCriticalSection(&cs_actions);
+
+    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 {
+        LeaveCriticalSection(&cs_actions);
+        return;
+    }
+
+    LeaveCriticalSection(&cs_actions);
+
+    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;
+
+    EnterCriticalSection(&cs_actions);
+
+    r = d->items;
+    while(r && r->action != KHUI_MENU_END &&
+          (!(d->state & KHUI_MENUSTATE_ALLOCD) || (r - d->items) < (int) d->n_items)) {
+        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++;
+    }
+
+    LeaveCriticalSection(&cs_actions);
+
+    if(delta) {
+        kmq_post_message(KMSG_ACT, KMSG_ACT_ENABLE, 0, 0);
+    }
+}
+
+KHMEXP void KHMAPI
+khui_enable_action(khm_int32 cmd, khm_boolean enable) {
+    khui_action * act;
+
+    act = khui_find_action(cmd);
+    if (!act)
+        return;
+
+    EnterCriticalSection(&cs_actions);
+
+    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 {
+        LeaveCriticalSection(&cs_actions);
+        return;
+    }
+
+    LeaveCriticalSection(&cs_actions);
+
+    kmq_post_message(KMSG_ACT, KMSG_ACT_ENABLE, 0, 0);
+}
+
+KHMEXP HACCEL KHMAPI
+khui_create_global_accel_table(void) {
+    int i;
+    ACCEL * accels;
+    HACCEL ha;
+
+    accels = PMALLOC(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);
+
+    PFREE(accels);
+
+    return ha;
+}
+
+KHMEXP khm_boolean KHMAPI 
+khui_get_cmd_accel_string(khm_int32 cmd, 
+                          wchar_t * buf, 
+                          khm_size 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_F1:
+            ap = L"F1";
+            break;
+
+        case VK_F2:
+            ap = L"F2";
+            break;
+
+        case VK_F3:
+            ap = L"F3";
+            break;
+
+        case VK_F4:
+            ap = L"F4";
+            break;
+
+        case VK_F5:
+            ap = L"F5";
+            break;
+
+        case VK_F6:
+            ap = L"F6";
+            break;
+
+        case VK_F7:
+            ap = L"F7";
+            break;
+
+        case VK_F8:
+            ap = L"F8";
+            break;
+
+        case VK_F9:
+            ap = L"F9";
+            break;
+
+        case VK_F10:
+            ap = L"F10";
+            break;
+
+        case VK_F11:
+            ap = L"F11";
+            break;
+
+        case VK_F12:
+            ap = L"F12";
+            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)
+                PFREE(ctxdest->int_buf);
+
+            ctxdest->int_cb_buf = cb_total;
+            ctxdest->int_buf = PMALLOC(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 = PMALLOC(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};
+
+khm_int32 KHMAPI
+set_cred_select_flag(khm_handle cred, void * rock) {
+    kcdb_cred_set_flags(cred, KCDB_CRED_FLAG_SELECTED,
+                        KCDB_CRED_FLAG_SELECTED);
+    return KHM_ERROR_SUCCESS;
+}
+
+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;
+
+    /* fill up the credset based on the scope */
+    if (scope != KHUI_SCOPE_NONE) {
+        if (tctx.credset == NULL)
+            kcdb_credset_create(&tctx.credset);
+        else
+            kcdb_credset_flush(tctx.credset);
+
+        if (scope == KHUI_SCOPE_IDENT) {
+            kcdb_credset_extract(tctx.credset,
+                                 NULL,
+                                 tctx.identity,
+                                 KCDB_CREDTYPE_INVALID);
+        } else if (scope == KHUI_SCOPE_CREDTYPE) {
+            kcdb_credset_extract(tctx.credset,
+                                 NULL,
+                                 tctx.identity,
+                                 tctx.cred_type);
+        } else if (scope == KHUI_SCOPE_CRED) {
+            khm_handle dupcred = NULL;
+            kcdb_cred_dup(cred, &dupcred);
+
+            kcdb_credset_add_cred(tctx.credset, dupcred, -1);
+        } else {
+#ifdef DEBUG
+            /* KHUI_SCOPE_GROUP is not used with
+               khui_context_create() */
+            assert(FALSE);
+#endif
+        }
+
+        kcdb_credset_apply(tctx.credset, set_cred_select_flag,
+                           NULL);
+
+        kcdb_credset_seal(tctx.credset);
+    }
+
+    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_set_indirect(khui_action_context * ctx)
+{
+    EnterCriticalSection(&cs_actions);
+
+    khuiint_context_release(&khui_ctx);
+
+    khuiint_copy_context(&khui_ctx, ctx);
+
+    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);
+        }
+    } else {
+        khui_check_action(KHUI_ACTION_SET_DEF_ID, FALSE);
+        khui_enable_action(KHUI_ACTION_SET_DEF_ID, FALSE);
+    }
+
+    if (khui_ctx.scope != KHUI_SCOPE_NONE) {
+        khui_enable_action(KHUI_ACTION_PROPERTIES, TRUE);
+    } else {
+        khui_enable_action(KHUI_ACTION_PROPERTIES, 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)
+        PFREE(ctx->int_buf);
+    ctx->int_buf = NULL;
+#if 0
+    if (ctx->vparam && ctx->cb_vparam > 0) {
+        PFREE(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;
+}
index e398690e92f19fca8d22cadce81aca6498cca3e4..376eab6e1c6750fd42ee5d03e74e2f9d45621671 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khuidefs.h>\r
-#include<utils.h>\r
-#include<intalert.h>\r
-#include<assert.h>\r
-\r
-#include<strsafe.h>\r
-\r
-/***********************************************************************\r
-  Alerter\r
-***********************************************************************/\r
-\r
-khui_alert * kh_alerts = NULL;\r
-CRITICAL_SECTION cs_alerts;\r
-\r
-void \r
-alert_init(void)\r
-{\r
-    InitializeCriticalSection(&cs_alerts);\r
-}\r
-\r
-void \r
-alert_exit(void)\r
-{\r
-    DeleteCriticalSection(&cs_alerts);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_create_empty(khui_alert ** result)\r
-{\r
-    khui_alert * a;\r
-\r
-    a = PMALLOC(sizeof(*a));\r
-    ZeroMemory(a, sizeof(*a));\r
-\r
-    a->magic = KHUI_ALERT_MAGIC;\r
-\r
-    /* set defaults */\r
-    a->severity = KHERR_INFO;\r
-    a->flags = KHUI_ALERT_FLAG_FREE_STRUCT;\r
-    a->alert_type = KHUI_ALERTTYPE_NONE;\r
-    khui_context_create(&a->ctx, KHUI_SCOPE_NONE,\r
-                        NULL, KCDB_CREDTYPE_INVALID,\r
-                        NULL);\r
-\r
-    khui_alert_hold(a);\r
-    EnterCriticalSection(&cs_alerts);\r
-    LPUSH(&kh_alerts, a);\r
-    LeaveCriticalSection(&cs_alerts);\r
-\r
-    *result = a;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_create_simple(const wchar_t * title, \r
-                         const wchar_t * message, \r
-                         khm_int32 severity, \r
-                         khui_alert ** result)\r
-{\r
-    khui_alert * a;\r
-\r
-    khui_alert_create_empty(&a);\r
-    khui_alert_set_title(a, title);\r
-    khui_alert_set_message(a, message);\r
-    khui_alert_set_severity(a, severity);\r
-\r
-    *result = a;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_set_title(khui_alert * alert, const wchar_t * title)\r
-{\r
-    size_t cb = 0;\r
-\r
-    assert(alert->magic == KHUI_ALERT_MAGIC);\r
-\r
-    if(title) {\r
-        if(FAILED(StringCbLength(title, \r
-                                 KHUI_MAXCB_TITLE, \r
-                                 &cb))) {\r
-            return KHM_ERROR_INVALID_PARAM;\r
-        }\r
-        cb += sizeof(wchar_t);\r
-    }\r
-\r
-    EnterCriticalSection(&cs_alerts);\r
-    if(alert->title && (alert->flags & KHUI_ALERT_FLAG_FREE_TITLE)) {\r
-        PFREE(alert->title);\r
-        alert->title = NULL;\r
-        alert->flags &= ~KHUI_ALERT_FLAG_FREE_TITLE;\r
-    }\r
-    if(title) {\r
-        alert->title = PMALLOC(cb);\r
-        StringCbCopy(alert->title, cb, title);\r
-        alert->flags |= KHUI_ALERT_FLAG_FREE_TITLE;\r
-    }\r
-    LeaveCriticalSection(&cs_alerts);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_alert_set_flags(khui_alert * alert, khm_int32 mask, khm_int32 flags) \r
-{\r
-    assert(alert->magic == KHUI_ALERT_MAGIC);\r
-\r
-    if (mask & ~KHUI_ALERT_FLAGMASK_RDWR)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_alerts);\r
-    alert->flags = \r
-        (alert->flags & ~mask) |\r
-        (flags & mask);\r
-    LeaveCriticalSection(&cs_alerts);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_set_severity(khui_alert * alert, khm_int32 severity)\r
-{\r
-\r
-    assert(alert->magic == KHUI_ALERT_MAGIC);\r
-\r
-    EnterCriticalSection(&cs_alerts);\r
-    alert->severity = severity;\r
-    LeaveCriticalSection(&cs_alerts);\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_set_suggestion(khui_alert * alert,\r
-                          const wchar_t * suggestion) {\r
-    size_t cb = 0;\r
-\r
-    assert(alert->magic == KHUI_ALERT_MAGIC);\r
-\r
-    if(suggestion) {\r
-        if(FAILED(StringCbLength(suggestion, \r
-                                 KHUI_MAXCB_MESSAGE - sizeof(wchar_t), \r
-                                 &cb))) {\r
-            return KHM_ERROR_INVALID_PARAM;\r
-        }\r
-        cb += sizeof(wchar_t);\r
-    }\r
-\r
-    EnterCriticalSection(&cs_alerts);\r
-    if(alert->suggestion && \r
-       (alert->flags & KHUI_ALERT_FLAG_FREE_SUGGEST)) {\r
-\r
-        PFREE(alert->suggestion);\r
-        alert->suggestion = NULL;\r
-        alert->flags &= ~KHUI_ALERT_FLAG_FREE_SUGGEST;\r
-\r
-    }\r
-\r
-    if(suggestion) {\r
-        alert->suggestion = PMALLOC(cb);\r
-        StringCbCopy(alert->suggestion, cb, suggestion);\r
-        alert->flags |= KHUI_ALERT_FLAG_FREE_SUGGEST;\r
-    }\r
-    LeaveCriticalSection(&cs_alerts);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_set_message(khui_alert * alert, const wchar_t * message)\r
-{\r
-    size_t cb = 0;\r
-\r
-    assert(alert->magic == KHUI_ALERT_MAGIC);\r
-\r
-    if(message) {\r
-        if(FAILED(StringCbLength(message, \r
-                                 KHUI_MAXCB_MESSAGE - sizeof(wchar_t), \r
-                                 &cb))) {\r
-            return KHM_ERROR_INVALID_PARAM;\r
-        }\r
-        cb += sizeof(wchar_t);\r
-    }\r
-\r
-    EnterCriticalSection(&cs_alerts);\r
-    if(alert->message && \r
-       (alert->flags & KHUI_ALERT_FLAG_FREE_MESSAGE)) {\r
-\r
-        PFREE(alert->message);\r
-        alert->flags &= ~KHUI_ALERT_FLAG_FREE_MESSAGE;\r
-    }\r
-\r
-    alert->message = NULL;\r
-\r
-    if(message) {\r
-        alert->message = PMALLOC(cb);\r
-        StringCbCopy(alert->message, cb, message);\r
-        alert->flags |= KHUI_ALERT_FLAG_FREE_MESSAGE;\r
-    }\r
-    LeaveCriticalSection(&cs_alerts);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_clear_commands(khui_alert * alert)\r
-{\r
-    assert(alert->magic == KHUI_ALERT_MAGIC);\r
-\r
-    EnterCriticalSection(&cs_alerts);\r
-    alert->n_alert_commands = 0;\r
-    LeaveCriticalSection(&cs_alerts);\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_add_command(khui_alert * alert, khm_int32 command_id)\r
-{\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    assert(alert->magic == KHUI_ALERT_MAGIC);\r
-\r
-    EnterCriticalSection(&cs_alerts);\r
-    if(alert->n_alert_commands >= KHUI_MAX_ALERT_COMMANDS)\r
-        rv = KHM_ERROR_NO_RESOURCES;\r
-    else {\r
-        alert->alert_commands[alert->n_alert_commands++] = command_id;\r
-    }\r
-    LeaveCriticalSection(&cs_alerts);\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_alert_set_type(khui_alert * alert, khui_alert_type alert_type)\r
-{\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    assert(alert->magic == KHUI_ALERT_MAGIC);\r
-\r
-    EnterCriticalSection(&cs_alerts);\r
-    alert->alert_type = alert_type;\r
-    LeaveCriticalSection(&cs_alerts);\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_alert_set_ctx(khui_alert * alert,\r
-                   khui_scope scope,\r
-                   khm_handle identity,\r
-                   khm_int32 cred_type,\r
-                   khm_handle cred)\r
-{\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    assert(alert->magic == KHUI_ALERT_MAGIC);\r
-\r
-    EnterCriticalSection(&cs_alerts);\r
-    khui_context_release(&alert->ctx);\r
-    khui_context_create(&alert->ctx,\r
-                        scope,\r
-                        identity,\r
-                        cred_type,\r
-                        cred);\r
-    LeaveCriticalSection(&cs_alerts);\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_alert_get_response(khui_alert * alert)\r
-{\r
-    khm_int32 response = 0;\r
-\r
-    assert(alert->magic == KHUI_ALERT_MAGIC);\r
-\r
-    EnterCriticalSection(&cs_alerts);\r
-    response = alert->response;\r
-    LeaveCriticalSection(&cs_alerts);\r
-\r
-    return response;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_show(khui_alert * alert)\r
-{\r
-    assert(alert->magic == KHUI_ALERT_MAGIC);\r
-\r
-    khui_alert_hold(alert);\r
-    /* the alert will be released when the message is processed */\r
-    kmq_post_message(KMSG_ALERT, KMSG_ALERT_SHOW, 0, (void *) alert);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_alert_show_modal(khui_alert * alert)\r
-{\r
-    khm_int32 rv;\r
-\r
-    assert(alert->magic == KHUI_ALERT_MAGIC);\r
-\r
-    khui_alert_hold(alert);\r
-    rv = kmq_send_message(KMSG_ALERT, KMSG_ALERT_SHOW_MODAL, 0,\r
-                          (void *) alert);\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_alert_queue(khui_alert * alert)\r
-{\r
-    assert(alert->magic == KHUI_ALERT_MAGIC);\r
-\r
-    khui_alert_hold(alert);\r
-    kmq_post_message(KMSG_ALERT, KMSG_ALERT_QUEUE, 0, (void *) alert);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_show_simple(const wchar_t * title, \r
-                       const wchar_t * message, \r
-                       khm_int32 severity)\r
-{\r
-    khui_alert * a = NULL;\r
-    khm_int32 rv;\r
-\r
-    rv = khui_alert_create_simple(title, message, severity, &a);\r
-\r
-    if(KHM_FAILED(rv))\r
-        return rv;\r
-\r
-    rv = khui_alert_show(a);\r
-\r
-    khui_alert_release(a);\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_hold(khui_alert * alert) \r
-{\r
-    assert(alert->magic == KHUI_ALERT_MAGIC);\r
-\r
-    EnterCriticalSection(&cs_alerts);\r
-    alert->refcount++;\r
-    LeaveCriticalSection(&cs_alerts);\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* called with cs_alert held */\r
-static void \r
-free_alert(khui_alert * alert)\r
-{\r
-    assert(alert->magic == KHUI_ALERT_MAGIC);\r
-\r
-    LDELETE(&kh_alerts, alert);\r
-\r
-    if(alert->flags & KHUI_ALERT_FLAG_FREE_TITLE) {\r
-        assert(alert->title);\r
-        PFREE(alert->title);\r
-        alert->title = NULL;\r
-        alert->flags &= ~KHUI_ALERT_FLAG_FREE_TITLE;\r
-    }\r
-    if(alert->flags & KHUI_ALERT_FLAG_FREE_MESSAGE) {\r
-        assert(alert->message);\r
-        PFREE(alert->message);\r
-        alert->message = NULL;\r
-        alert->flags &= ~KHUI_ALERT_FLAG_FREE_MESSAGE;\r
-    }\r
-    if(alert->flags & KHUI_ALERT_FLAG_FREE_SUGGEST) {\r
-        assert(alert->suggestion);\r
-        PFREE(alert->suggestion);\r
-        alert->suggestion = NULL;\r
-        alert->flags &= ~KHUI_ALERT_FLAG_FREE_SUGGEST;\r
-    }\r
-\r
-    khui_context_release(&alert->ctx);\r
-\r
-    if(alert->flags & KHUI_ALERT_FLAG_FREE_STRUCT) {\r
-        alert->flags &= ~KHUI_ALERT_FLAG_FREE_STRUCT;\r
-        alert->magic = 0;\r
-        PFREE(alert);\r
-    }\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_release(khui_alert * alert) \r
-{\r
-    assert(alert->magic == KHUI_ALERT_MAGIC);\r
-\r
-    EnterCriticalSection(&cs_alerts);\r
-    if((--(alert->refcount)) == 0) {\r
-        free_alert(alert);\r
-    }\r
-    LeaveCriticalSection(&cs_alerts);\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP void KHMAPI khui_alert_lock(khui_alert * alert)\r
-{\r
-    EnterCriticalSection(&cs_alerts);\r
-}\r
-\r
-KHMEXP void KHMAPI khui_alert_unlock(khui_alert * alert)\r
-{\r
-    LeaveCriticalSection(&cs_alerts);\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<utils.h>
+#include<intalert.h>
+#include<assert.h>
+
+#include<strsafe.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 = PMALLOC(sizeof(*a));
+    ZeroMemory(a, sizeof(*a));
+
+    a->magic = KHUI_ALERT_MAGIC;
+
+    /* set defaults */
+    a->severity = KHERR_INFO;
+    a->flags = KHUI_ALERT_FLAG_FREE_STRUCT;
+    a->alert_type = KHUI_ALERTTYPE_NONE;
+    khui_context_create(&a->ctx, KHUI_SCOPE_NONE,
+                        NULL, KCDB_CREDTYPE_INVALID,
+                        NULL);
+
+    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, 
+                                 &cb))) {
+            return KHM_ERROR_INVALID_PARAM;
+        }
+        cb += sizeof(wchar_t);
+    }
+
+    EnterCriticalSection(&cs_alerts);
+    if(alert->title && (alert->flags & KHUI_ALERT_FLAG_FREE_TITLE)) {
+        PFREE(alert->title);
+        alert->title = NULL;
+        alert->flags &= ~KHUI_ALERT_FLAG_FREE_TITLE;
+    }
+    if(title) {
+        alert->title = PMALLOC(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_PARAM;
+
+    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_PARAM;
+        }
+        cb += sizeof(wchar_t);
+    }
+
+    EnterCriticalSection(&cs_alerts);
+    if(alert->suggestion && 
+       (alert->flags & KHUI_ALERT_FLAG_FREE_SUGGEST)) {
+
+        PFREE(alert->suggestion);
+        alert->suggestion = NULL;
+        alert->flags &= ~KHUI_ALERT_FLAG_FREE_SUGGEST;
+
+    }
+
+    if(suggestion) {
+        alert->suggestion = PMALLOC(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_PARAM;
+        }
+        cb += sizeof(wchar_t);
+    }
+
+    EnterCriticalSection(&cs_alerts);
+    if(alert->message && 
+       (alert->flags & KHUI_ALERT_FLAG_FREE_MESSAGE)) {
+
+        PFREE(alert->message);
+        alert->flags &= ~KHUI_ALERT_FLAG_FREE_MESSAGE;
+    }
+
+    alert->message = NULL;
+
+    if(message) {
+        alert->message = PMALLOC(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_set_type(khui_alert * alert, khui_alert_type alert_type)
+{
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+
+    assert(alert->magic == KHUI_ALERT_MAGIC);
+
+    EnterCriticalSection(&cs_alerts);
+    alert->alert_type = alert_type;
+    LeaveCriticalSection(&cs_alerts);
+
+    return rv;
+}
+
+KHMEXP khm_int32 KHMAPI
+khui_alert_set_ctx(khui_alert * alert,
+                   khui_scope scope,
+                   khm_handle identity,
+                   khm_int32 cred_type,
+                   khm_handle cred)
+{
+    khm_int32 rv = KHM_ERROR_SUCCESS;
+
+    assert(alert->magic == KHUI_ALERT_MAGIC);
+
+    EnterCriticalSection(&cs_alerts);
+    khui_context_release(&alert->ctx);
+    khui_context_create(&alert->ctx,
+                        scope,
+                        identity,
+                        cred_type,
+                        cred);
+    LeaveCriticalSection(&cs_alerts);
+
+    return rv;
+}
+
+KHMEXP khm_int32 KHMAPI
+khui_alert_get_response(khui_alert * alert)
+{
+    khm_int32 response = 0;
+
+    assert(alert->magic == KHUI_ALERT_MAGIC);
+
+    EnterCriticalSection(&cs_alerts);
+    response = alert->response;
+    LeaveCriticalSection(&cs_alerts);
+
+    return response;
+}
+
+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_modal(khui_alert * alert)
+{
+    khm_int32 rv;
+
+    assert(alert->magic == KHUI_ALERT_MAGIC);
+
+    khui_alert_hold(alert);
+    rv = kmq_send_message(KMSG_ALERT, KMSG_ALERT_SHOW_MODAL, 0,
+                          (void *) alert);
+
+    return rv;
+}
+
+KHMEXP khm_int32 KHMAPI
+khui_alert_queue(khui_alert * alert)
+{
+    assert(alert->magic == KHUI_ALERT_MAGIC);
+
+    khui_alert_hold(alert);
+    kmq_post_message(KMSG_ALERT, KMSG_ALERT_QUEUE, 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);
+        PFREE(alert->title);
+        alert->title = NULL;
+        alert->flags &= ~KHUI_ALERT_FLAG_FREE_TITLE;
+    }
+    if(alert->flags & KHUI_ALERT_FLAG_FREE_MESSAGE) {
+        assert(alert->message);
+        PFREE(alert->message);
+        alert->message = NULL;
+        alert->flags &= ~KHUI_ALERT_FLAG_FREE_MESSAGE;
+    }
+    if(alert->flags & KHUI_ALERT_FLAG_FREE_SUGGEST) {
+        assert(alert->suggestion);
+        PFREE(alert->suggestion);
+        alert->suggestion = NULL;
+        alert->flags &= ~KHUI_ALERT_FLAG_FREE_SUGGEST;
+    }
+
+    khui_context_release(&alert->ctx);
+
+    if(alert->flags & KHUI_ALERT_FLAG_FREE_STRUCT) {
+        alert->flags &= ~KHUI_ALERT_FLAG_FREE_STRUCT;
+        alert->magic = 0;
+        PFREE(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);
+}
index c8c61f5cb38aa7d44b6276e224fe7ffb8a2f2a62..3de2573ea1777b2a4b5f037a07f60da6dbefc011 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khuidefs.h>\r
-#include<kmm.h>\r
-#include<configui.h>\r
-#include<utils.h>\r
-#include<assert.h>\r
-\r
-#include<strsafe.h>\r
-\r
-khm_int32 cfgui_node_serial;\r
-LONG init_once = 0;\r
-CRITICAL_SECTION cs_cfgui;\r
-khui_config_node_i * cfgui_root_config;\r
-HWND hwnd_cfgui = NULL;\r
-\r
-static khui_config_node_i *\r
-cfgui_create_new_node(void) {\r
-    khui_config_node_i * node;\r
-\r
-    node = PMALLOC(sizeof(*node));\r
-#ifdef DEBUG\r
-    assert(node);\r
-#endif\r
-    ZeroMemory(node, sizeof(*node));\r
-    node->magic = KHUI_CONFIG_NODE_MAGIC;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    node->id = ++cfgui_node_serial;\r
-    LeaveCriticalSection(&cs_cfgui);\r
-\r
-    return node;\r
-}\r
-\r
-/* called with cs_cfgui held */\r
-static void \r
-cfgui_free_node(khui_config_node_i * node) {\r
-    if (!cfgui_is_valid_node(node))\r
-        return;\r
-\r
-    if (node->reg.name)\r
-        PFREE((void *) node->reg.name);\r
-\r
-    if (node->reg.short_desc)\r
-        PFREE((void *) node->reg.short_desc);\r
-\r
-    if (node->reg.long_desc)\r
-        PFREE((void *) node->reg.long_desc);\r
-\r
-    node->magic = 0;\r
-\r
-    if (node->owner)\r
-        kmm_release_plugin(node->owner);\r
-\r
-    ZeroMemory(node, sizeof(*node));\r
-\r
-    PFREE(node);\r
-}\r
-\r
-\r
-static void\r
-cfgui_hold_node(khui_config_node_i * node) {\r
-    EnterCriticalSection(&cs_cfgui);\r
-    node->refcount++;\r
-    LeaveCriticalSection(&cs_cfgui);\r
-}\r
-\r
-\r
-static void\r
-cfgui_release_node(khui_config_node_i * node) {\r
-    EnterCriticalSection(&cs_cfgui);\r
-    node->refcount--;\r
-    if (node->refcount == 0 &&\r
-        (node->flags & KHUI_CN_FLAG_DELETED)) {\r
-        khui_config_node_i * parent;\r
-        parent = TPARENT(node);\r
-#ifdef DEBUG\r
-        assert(TFIRSTCHILD(node) == NULL);\r
-        assert(parent != NULL);\r
-#endif\r
-        TDELCHILD(parent, node);\r
-        cfgui_free_node(node);\r
-        cfgui_release_node(parent);\r
-    }\r
-    LeaveCriticalSection(&cs_cfgui);\r
-}\r
-\r
-static void \r
-cfgui_init_once(void) {\r
-    if (init_once == 0 &&\r
-        InterlockedIncrement(&init_once) == 1) {\r
-        InitializeCriticalSection(&cs_cfgui);\r
-        cfgui_root_config = cfgui_create_new_node();\r
-        cfgui_node_serial = 0;\r
-        hwnd_cfgui = NULL;\r
-    }\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_register(khui_config_node vparent,\r
-                  const khui_config_node_reg * reg) {\r
-\r
-    size_t cb_name;\r
-    size_t cb_short_desc;\r
-    size_t cb_long_desc;\r
-    khui_config_node_i * node;\r
-    khui_config_node_i * parent;\r
-    khui_config_node t;\r
-    wchar_t * name;\r
-    wchar_t * short_desc;\r
-    wchar_t * long_desc;\r
-\r
-    cfgui_init_once();\r
-\r
-    if (!reg ||\r
-        FAILED(StringCbLength(reg->name,\r
-                              KHUI_MAXCB_NAME,\r
-                              &cb_name)) ||\r
-        FAILED(StringCbLength(reg->short_desc,\r
-                              KHUI_MAXCB_SHORT_DESC,\r
-                              &cb_short_desc)) ||\r
-        FAILED(StringCbLength(reg->long_desc,\r
-                              KHUI_MAXCB_LONG_DESC,\r
-                              &cb_long_desc)) ||\r
-        (vparent &&\r
-         !cfgui_is_valid_node_handle(vparent)))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if (KHM_SUCCEEDED(khui_cfg_open(vparent,\r
-                                  reg->name,\r
-                                  &t))) {\r
-        khui_cfg_release(t);\r
-        return KHM_ERROR_DUPLICATE;\r
-    }\r
-\r
-    cb_name += sizeof(wchar_t);\r
-    cb_short_desc += sizeof(wchar_t);\r
-    cb_long_desc += sizeof(wchar_t);\r
-\r
-    node = cfgui_create_new_node();\r
-\r
-    node->reg = *reg;\r
-    node->reg.flags &= KHUI_CNFLAGMASK_STATIC;\r
-\r
-    name = PMALLOC(cb_name);\r
-    StringCbCopy(name, cb_name, reg->name);\r
-    short_desc = PMALLOC(cb_short_desc);\r
-    StringCbCopy(short_desc, cb_short_desc, reg->short_desc);\r
-    long_desc = PMALLOC(cb_long_desc);\r
-    StringCbCopy(long_desc, cb_long_desc, reg->long_desc);\r
-\r
-    node->reg.name = name;\r
-    node->reg.short_desc = short_desc;\r
-    node->reg.long_desc = long_desc;\r
-    node->flags = node->reg.flags;\r
-\r
-    if (vparent == NULL) {\r
-        parent = cfgui_root_config;\r
-    } else {\r
-        parent = cfgui_node_i_from_handle(vparent);\r
-    }\r
-\r
-    /* plugin handles should not be obtained lightly.  For the moment,\r
-       the cleanup of nodes doesn't happen until module unload and\r
-       module unload doesn't happen until all the plugin and module\r
-       handles have been freed. */\r
-    /* node->owner = kmm_this_plugin(); */\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    TADDCHILD(parent, node);\r
-\r
-    if (hwnd_cfgui) {\r
-        SendMessage(hwnd_cfgui, KHUI_WM_CFG_NOTIFY,\r
-                    MAKEWPARAM(0, WMCFG_SYNC_NODE_LIST), 0);\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_cfgui);\r
-\r
-    /* when the root config list changes, we need to notify the UI.\r
-       this way, the Options menu can be kept in sync. */\r
-    if (parent == cfgui_root_config) {\r
-        kmq_post_message(KMSG_ACT, KMSG_ACT_SYNC_CFG, 0, 0);\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_open(khui_config_node vparent,\r
-              const wchar_t * name,\r
-              khui_config_node * result) {\r
-    khui_config_node_i * parent;\r
-    khui_config_node_i * c;\r
-    size_t sz;\r
-\r
-    cfgui_init_once();\r
-\r
-    if ((vparent &&\r
-         !cfgui_is_valid_node_handle(vparent)) ||\r
-        FAILED(StringCbLength(name, KHUI_MAXCCH_NAME, &sz)) ||\r
-        !result)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    if (vparent)\r
-        parent = cfgui_node_i_from_handle(vparent);\r
-    else\r
-        parent = cfgui_root_config;\r
-\r
-    c = TFIRSTCHILD(parent);\r
-    while(c) {\r
-        if (!(c->flags & KHUI_CN_FLAG_DELETED) &&\r
-            !wcscmp(c->reg.name, name))\r
-            break;\r
-        c = LNEXT(c);\r
-    }\r
-\r
-    if (c) {\r
-        *result = cfgui_handle_from_node_i(c);\r
-        cfgui_hold_node(c);\r
-    } else {\r
-        *result = NULL;\r
-    }\r
-    LeaveCriticalSection(&cs_cfgui);\r
-\r
-    if (*result)\r
-        return KHM_ERROR_SUCCESS;\r
-    else\r
-        return KHM_ERROR_NOT_FOUND;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_remove(khui_config_node vnode) {\r
-    khui_config_node_i * node;\r
-    if (!cfgui_is_valid_node_handle(vnode))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    node = cfgui_node_i_from_handle(vnode);\r
-    node->flags |= KHUI_CN_FLAG_DELETED;\r
-\r
-    if (hwnd_cfgui) {\r
-        SendMessage(hwnd_cfgui, KHUI_WM_CFG_NOTIFY,\r
-                    MAKEWPARAM(0, WMCFG_SYNC_NODE_LIST), 0);\r
-    }\r
-\r
-    LeaveCriticalSection(&cs_cfgui);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_hold(khui_config_node vnode) {\r
-    if (!cfgui_is_valid_node_handle(vnode))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cfgui_hold_node(cfgui_node_i_from_handle(vnode));\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_release(khui_config_node vnode) {\r
-    if (!cfgui_is_valid_node_handle(vnode))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cfgui_release_node(cfgui_node_i_from_handle(vnode));\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_get_parent(khui_config_node vnode,\r
-                    khui_config_node * result) {\r
-\r
-    khui_config_node_i * node;\r
-    khui_config_node_i * parent;\r
-\r
-    if(!cfgui_is_valid_node_handle(vnode) ||\r
-       !result)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    if (cfgui_is_valid_node_handle(vnode)) {\r
-        node = cfgui_node_i_from_handle(vnode);\r
-        parent = TPARENT(node);\r
-        if (parent == cfgui_root_config)\r
-            parent = NULL;\r
-    } else {\r
-        parent = NULL;\r
-    }\r
-    if (parent) {\r
-        cfgui_hold_node(parent);\r
-    }\r
-    LeaveCriticalSection(&cs_cfgui);\r
-\r
-    *result = parent;\r
-\r
-    if (parent)\r
-        return KHM_ERROR_SUCCESS;\r
-    else\r
-        return KHM_ERROR_NOT_FOUND;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_get_first_child(khui_config_node vparent,\r
-                         khui_config_node * result) {\r
-    khui_config_node_i * parent;\r
-    khui_config_node_i * c;\r
-\r
-    cfgui_init_once();\r
-\r
-    if((vparent && !cfgui_is_valid_node_handle(vparent)) ||\r
-       !result)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    if (cfgui_is_valid_node_handle(vparent)) {\r
-        parent = cfgui_node_i_from_handle(vparent);\r
-    } else if (!vparent) {\r
-        parent = cfgui_root_config;\r
-    } else {\r
-        parent = NULL;\r
-    }\r
-\r
-    if (parent) {\r
-        for(c = TFIRSTCHILD(parent);\r
-            c &&\r
-                ((c->reg.flags & KHUI_CNFLAG_SUBPANEL) ||\r
-                 (c->flags & KHUI_CN_FLAG_DELETED));\r
-            c = LNEXT(c));\r
-    } else {\r
-        c = NULL;\r
-    }\r
-\r
-    if (c)\r
-        cfgui_hold_node(c);\r
-    LeaveCriticalSection(&cs_cfgui);\r
-\r
-    *result = c;\r
-\r
-    if (c)\r
-        return KHM_ERROR_SUCCESS;\r
-    else\r
-        return KHM_ERROR_NOT_FOUND;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_get_first_subpanel(khui_config_node vparent,\r
-                            khui_config_node * result) {\r
-    khui_config_node_i * parent;\r
-    khui_config_node_i * c;\r
-\r
-    cfgui_init_once();\r
-\r
-    if((vparent && !cfgui_is_valid_node_handle(vparent)) ||\r
-       !result)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    if (cfgui_is_valid_node_handle(vparent)) {\r
-        parent = cfgui_node_i_from_handle(vparent);\r
-    } else if (!vparent) {\r
-        parent = cfgui_root_config;\r
-    } else {\r
-        parent = NULL;\r
-    }\r
-\r
-    if (parent) {\r
-        for(c = TFIRSTCHILD(parent);\r
-            c &&\r
-                (!(c->reg.flags & KHUI_CNFLAG_SUBPANEL) ||\r
-                 (c->flags & KHUI_CN_FLAG_DELETED));\r
-            c = LNEXT(c));\r
-    } else {\r
-        c = NULL;\r
-    }\r
-\r
-    if (c)\r
-        cfgui_hold_node(c);\r
-    LeaveCriticalSection(&cs_cfgui);\r
-\r
-    *result = c;\r
-\r
-    if (c)\r
-        return KHM_ERROR_SUCCESS;\r
-    else\r
-        return KHM_ERROR_NOT_FOUND;\r
-}\r
-\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_get_next(khui_config_node vnode,\r
-                  khui_config_node * result) {\r
-\r
-    khui_config_node_i * node;\r
-    khui_config_node_i * nxt_node;\r
-\r
-    if (!cfgui_is_valid_node_handle(vnode) ||\r
-        !result)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    if (cfgui_is_valid_node_handle(vnode)) {\r
-        node = cfgui_node_i_from_handle(vnode);\r
-        for(nxt_node = LNEXT(node);\r
-            nxt_node &&\r
-                ((node->reg.flags ^ nxt_node->reg.flags) & \r
-                 KHUI_CNFLAG_SUBPANEL);\r
-            nxt_node = LNEXT(nxt_node));\r
-        if (nxt_node)\r
-            cfgui_hold_node(nxt_node);\r
-    } else {\r
-        nxt_node = NULL;\r
-    }\r
-    LeaveCriticalSection(&cs_cfgui);\r
-\r
-    *result = cfgui_handle_from_node_i(nxt_node);\r
-\r
-    if (nxt_node)\r
-        return KHM_ERROR_SUCCESS;\r
-    else\r
-        return KHM_ERROR_NOT_FOUND;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_get_next_release(khui_config_node * pvnode) {\r
-\r
-    khui_config_node_i * node;\r
-    khui_config_node_i * nxt_node;\r
-\r
-    if (!pvnode || \r
-        !cfgui_is_valid_node_handle(*pvnode))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    if (cfgui_is_valid_node_handle(*pvnode)) {\r
-        node = cfgui_node_i_from_handle(*pvnode);\r
-        for(nxt_node = LNEXT(node);\r
-            nxt_node &&\r
-                (((node->reg.flags ^ nxt_node->reg.flags) & \r
-                  KHUI_CNFLAG_SUBPANEL) ||\r
-                 (nxt_node->flags & KHUI_CN_FLAG_DELETED));\r
-            nxt_node = LNEXT(nxt_node));\r
-        if (nxt_node)\r
-            cfgui_hold_node(nxt_node);\r
-        cfgui_release_node(node);\r
-    } else {\r
-        nxt_node = NULL;\r
-    }\r
-    LeaveCriticalSection(&cs_cfgui);\r
-\r
-    *pvnode = cfgui_handle_from_node_i(nxt_node);\r
-\r
-    if (nxt_node)\r
-        return KHM_ERROR_SUCCESS;\r
-    else\r
-        return KHM_ERROR_NOT_FOUND;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_get_reg(khui_config_node vnode,\r
-                 khui_config_node_reg * reg) {\r
-\r
-    khui_config_node_i * node;\r
-\r
-    cfgui_init_once();\r
-\r
-    if ((vnode && !cfgui_is_valid_node_handle(vnode)) ||\r
-        !reg)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    if (cfgui_is_valid_node_handle(vnode)) {\r
-        node = cfgui_node_i_from_handle(vnode);\r
-        *reg = node->reg;\r
-    } else if (!vnode) {\r
-        node = cfgui_root_config;\r
-        *reg = node->reg;\r
-    } else {\r
-        node = NULL;\r
-        ZeroMemory(reg, sizeof(*reg));\r
-    }\r
-    LeaveCriticalSection(&cs_cfgui);\r
-\r
-    if (node)\r
-        return KHM_ERROR_SUCCESS;\r
-    else\r
-        return KHM_ERROR_INVALID_PARAM;\r
-}\r
-\r
-KHMEXP HWND KHMAPI\r
-khui_cfg_get_hwnd(khui_config_node vnode) {\r
-    khui_config_node_i * node;\r
-    HWND hwnd;\r
-\r
-    cfgui_init_once();\r
-\r
-    if (vnode &&\r
-        !cfgui_is_valid_node_handle(vnode))\r
-        return NULL;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    if (cfgui_is_valid_node_handle(vnode))\r
-        node = cfgui_node_i_from_handle(vnode);\r
-    else if (!vnode)\r
-        node = cfgui_root_config;\r
-    else \r
-        node = NULL;\r
-\r
-    if (node)\r
-        hwnd = node->hwnd;\r
-    else\r
-        hwnd = NULL;\r
-    LeaveCriticalSection(&cs_cfgui);\r
-\r
-    return hwnd;\r
-}\r
-\r
-KHMEXP LPARAM KHMAPI\r
-khui_cfg_get_param(khui_config_node vnode) {\r
-    khui_config_node_i * node;\r
-    LPARAM param;\r
-\r
-    cfgui_init_once();\r
-\r
-    if (vnode &&\r
-        !cfgui_is_valid_node_handle(vnode))\r
-        return 0;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    if (cfgui_is_valid_node_handle(vnode))\r
-        node = cfgui_node_i_from_handle(vnode);\r
-    else if (!vnode)\r
-        node = cfgui_root_config;\r
-    else \r
-        node = NULL;\r
-\r
-    if (node)\r
-        param = node->param;\r
-    else\r
-        param = 0;\r
-    LeaveCriticalSection(&cs_cfgui);\r
-\r
-    return param;\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_cfg_set_hwnd(khui_config_node vnode, HWND hwnd) {\r
-    khui_config_node_i * node;\r
-\r
-    cfgui_init_once();\r
-\r
-    if (vnode &&\r
-        !cfgui_is_valid_node_handle(vnode))\r
-        return;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    if (cfgui_is_valid_node_handle(vnode))\r
-        node = cfgui_node_i_from_handle(vnode);\r
-    else if (!vnode)\r
-        node = cfgui_root_config;\r
-    else\r
-        node = NULL;\r
-\r
-    if (node)\r
-        node->hwnd = hwnd;\r
-    LeaveCriticalSection(&cs_cfgui);\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_cfg_set_param(khui_config_node vnode, LPARAM param) {\r
-    khui_config_node_i * node;\r
-\r
-    cfgui_init_once();\r
-\r
-    if (vnode &&\r
-        !cfgui_is_valid_node_handle(vnode))\r
-        return;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    if (cfgui_is_valid_node_handle(vnode))\r
-        node = cfgui_node_i_from_handle(vnode);\r
-    else if (!vnode)\r
-        node = cfgui_root_config;\r
-    else\r
-        node = NULL;\r
-\r
-    if (node)\r
-        node->param = param;\r
-    LeaveCriticalSection(&cs_cfgui);\r
-}\r
-\r
-static void\r
-clear_node_data(khui_config_node_i * node) {\r
-    node->n_data = 0;\r
-}\r
-\r
-static cfg_node_data *\r
-get_node_data(khui_config_node_i * node,\r
-              void * key, \r
-              khm_boolean create) {\r
-    khm_size i;\r
-\r
-    for (i=0; i<node->n_data; i++) {\r
-        if (node->data[i].key == key)\r
-            return &(node->data[i]);\r
-    }\r
-\r
-    if (!create)\r
-        return NULL;\r
-\r
-    if (node->n_data + 1 > node->nc_data) {\r
-        cfg_node_data * newdata;\r
-\r
-        node->nc_data = UBOUNDSS((node->n_data + 1),\r
-                                 KHUI_NODEDATA_ALLOC_INCR,\r
-                                 KHUI_NODEDATA_ALLOC_INCR);\r
-#ifdef DEBUG\r
-        assert(node->nc_data >= node->n_data + 1);\r
-#endif\r
-        newdata = PMALLOC(sizeof(*newdata) * node->nc_data);\r
-#ifdef DEBUG\r
-        assert(newdata);\r
-#endif\r
-        ZeroMemory(newdata, sizeof(*newdata) * node->nc_data);\r
-\r
-        if (node->data && node->n_data > 0) {\r
-            memcpy(newdata, node->data, node->n_data * sizeof(*newdata));\r
-            PFREE(node->data);\r
-        }\r
-        node->data = newdata;\r
-    }\r
-\r
-    node->data[node->n_data].key = key;\r
-    node->data[node->n_data].hwnd = NULL;\r
-    node->data[node->n_data].param = 0;\r
-    node->data[node->n_data].flags = 0;\r
-\r
-    node->n_data++;\r
-\r
-    return &(node->data[node->n_data - 1]);\r
-}\r
-\r
-KHMEXP HWND KHMAPI\r
-khui_cfg_get_hwnd_inst(khui_config_node vnode,\r
-                       khui_config_node noderef) {\r
-    khui_config_node_i * node;\r
-    cfg_node_data * data;\r
-    HWND hwnd;\r
-\r
-    cfgui_init_once();\r
-\r
-    if (vnode &&\r
-        !cfgui_is_valid_node_handle(vnode))\r
-        return NULL;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    if (cfgui_is_valid_node_handle(vnode))\r
-        node = cfgui_node_i_from_handle(vnode);\r
-    else if (!vnode)\r
-        node = cfgui_root_config;\r
-    else \r
-        node = NULL;\r
-\r
-    if (node) {\r
-        data = get_node_data(node, noderef, FALSE);\r
-        if (data)\r
-            hwnd = data->hwnd;\r
-        else\r
-            hwnd = NULL;\r
-    } else\r
-        hwnd = NULL;\r
-    LeaveCriticalSection(&cs_cfgui);\r
-\r
-    return hwnd;\r
-}\r
-\r
-KHMEXP LPARAM KHMAPI\r
-khui_cfg_get_param_inst(khui_config_node vnode,\r
-                        khui_config_node noderef) {\r
-    khui_config_node_i * node;\r
-    cfg_node_data * data;\r
-    LPARAM lParam;\r
-\r
-    cfgui_init_once();\r
-\r
-    if (vnode &&\r
-        !cfgui_is_valid_node_handle(vnode))\r
-        return 0;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    if (cfgui_is_valid_node_handle(vnode))\r
-        node = cfgui_node_i_from_handle(vnode);\r
-    else if (!vnode)\r
-        node = cfgui_root_config;\r
-    else \r
-        node = NULL;\r
-\r
-    if (node) {\r
-        data = get_node_data(node, noderef, FALSE);\r
-        if (data)\r
-            lParam = data->param;\r
-        else\r
-            lParam = 0;\r
-    } else\r
-        lParam = 0;\r
-    LeaveCriticalSection(&cs_cfgui);\r
-\r
-    return lParam;\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_cfg_set_hwnd_inst(khui_config_node vnode, \r
-                       khui_config_node noderef,\r
-                       HWND hwnd) {\r
-    khui_config_node_i * node;\r
-    cfg_node_data * data;\r
-\r
-    cfgui_init_once();\r
-\r
-    if (vnode &&\r
-        !cfgui_is_valid_node_handle(vnode))\r
-        return;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    if (cfgui_is_valid_node_handle(vnode))\r
-        node = cfgui_node_i_from_handle(vnode);\r
-    else if (!vnode)\r
-        node = cfgui_root_config;\r
-    else \r
-        node = NULL;\r
-\r
-    if (node) {\r
-        data = get_node_data(node, noderef, TRUE);\r
-        if (data)\r
-            data->hwnd = hwnd;\r
-    }\r
-    LeaveCriticalSection(&cs_cfgui);\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_cfg_set_param_inst(khui_config_node vnode, \r
-                        khui_config_node noderef,\r
-                        LPARAM param) {\r
-    khui_config_node_i * node;\r
-    cfg_node_data * data;\r
-\r
-    cfgui_init_once();\r
-\r
-    if (vnode &&\r
-        !cfgui_is_valid_node_handle(vnode))\r
-        return;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    if (cfgui_is_valid_node_handle(vnode))\r
-        node = cfgui_node_i_from_handle(vnode);\r
-    else if (!vnode)\r
-        node = cfgui_root_config;\r
-    else \r
-        node = NULL;\r
-\r
-    if (node) {\r
-        data = get_node_data(node, noderef, TRUE);\r
-        if (data)\r
-            data->param = param;\r
-    }\r
-    LeaveCriticalSection(&cs_cfgui);\r
-}\r
-\r
-\r
-/* called with cs_cfgui held  */\r
-static void \r
-cfgui_clear_params(khui_config_node_i * node) {\r
-    khui_config_node_i * c;\r
-\r
-    node->hwnd = NULL;\r
-    node->param = 0;\r
-    node->flags &= KHUI_CNFLAGMASK_STATIC;\r
-    clear_node_data(node);\r
-\r
-    c = TFIRSTCHILD(node);\r
-    while(c) {\r
-        cfgui_clear_params(c);\r
-        c = LNEXT(c);\r
-    }\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_cfg_clear_params(void) {\r
-\r
-    cfgui_init_once();\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    cfgui_clear_params(cfgui_root_config);\r
-    LeaveCriticalSection(&cs_cfgui);\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_cfg_set_configui_handle(HWND hwnd) {\r
-    EnterCriticalSection(&cs_cfgui);\r
-    hwnd_cfgui = hwnd;\r
-    LeaveCriticalSection(&cs_cfgui);\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_cfg_set_flags(khui_config_node vnode, \r
-                   khm_int32 flags,\r
-                   khm_int32 mask) {\r
-    khui_config_node_i * node;\r
-    khm_int32 newflags;\r
-\r
-    if (vnode &&\r
-        !cfgui_is_valid_node_handle(vnode))\r
-        return;\r
-\r
-    mask &= KHUI_CNFLAGMASK_DYNAMIC;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    if (cfgui_is_valid_node_handle(vnode)) {\r
-\r
-        node = cfgui_node_i_from_handle(vnode);\r
-\r
-        newflags = \r
-            (flags & mask) |\r
-            (node->flags & ~mask);\r
-\r
-        if (newflags != node->flags) {\r
-            node->flags = newflags;\r
-\r
-            if (hwnd_cfgui)\r
-                PostMessage(hwnd_cfgui, KHUI_WM_CFG_NOTIFY,\r
-                            MAKEWPARAM((WORD)newflags, WMCFG_UPDATE_STATE),\r
-                            (LPARAM) vnode);\r
-        }\r
-    }\r
-    LeaveCriticalSection(&cs_cfgui);\r
-}\r
-\r
-/* called with cs_cfgui held */\r
-static void\r
-recalc_node_flags(khui_config_node vnode, khm_boolean plural) {\r
-    khui_config_node_i * node;\r
-    khui_config_node_i * parent;\r
-    khui_config_node_i * subpanel;\r
-    cfg_node_data * data;\r
-    khm_int32 flags;\r
-\r
-#ifdef DEBUG\r
-    assert(cfgui_is_valid_node_handle(vnode));\r
-#endif\r
-\r
-    node = cfgui_node_i_from_handle(vnode);\r
-\r
-    if (plural)\r
-        parent = TPARENT(node);\r
-    else\r
-        parent = node;\r
-#ifdef DEBUG\r
-    assert(parent);\r
-#endif\r
-\r
-    flags = 0;\r
-\r
-    for(subpanel = TFIRSTCHILD(parent); subpanel;\r
-        subpanel = LNEXT(subpanel)) {\r
-        if (!(subpanel->reg.flags & KHUI_CNFLAG_SUBPANEL) ||\r
-            (plural && !(subpanel->reg.flags & KHUI_CNFLAG_PLURAL)) ||\r
-            (!plural && (subpanel->reg.flags & KHUI_CNFLAG_PLURAL)))\r
-            continue;\r
-\r
-        data = get_node_data(subpanel,\r
-                             vnode,\r
-                             FALSE);\r
-\r
-        if (data) {\r
-            flags |= data->flags;\r
-        }\r
-    }\r
-\r
-    flags &= KHUI_CNFLAGMASK_DYNAMIC;\r
-\r
-    if ((node->flags & KHUI_CNFLAGMASK_DYNAMIC) == flags)\r
-        return;\r
-\r
-    node->flags = (node->flags & ~KHUI_CNFLAGMASK_DYNAMIC) | flags;\r
-\r
-    if (hwnd_cfgui)\r
-        PostMessage(hwnd_cfgui, KHUI_WM_CFG_NOTIFY,\r
-                    MAKEWPARAM((WORD) node->flags, WMCFG_UPDATE_STATE),\r
-                    (LPARAM) vnode);\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_cfg_set_flags_inst(khui_config_init_data * d,\r
-                        khm_int32 flags,\r
-                        khm_int32 mask) {\r
-    khui_config_node_i * node;\r
-    cfg_node_data * data;\r
-\r
-    cfgui_init_once();\r
-    if (!cfgui_is_valid_node_handle(d->this_node))\r
-        return;\r
-\r
-    mask &= KHUI_CNFLAGMASK_DYNAMIC;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    if (cfgui_is_valid_node_handle(d->this_node))\r
-        node = cfgui_node_i_from_handle(d->this_node);\r
-    else \r
-        node = NULL;\r
-\r
-    if (node) {\r
-        data = get_node_data(node, d->ctx_node, TRUE);\r
-        if (data) {\r
-            khm_int32 new_flags;\r
-\r
-            new_flags = (flags & mask) |\r
-                (data->flags & ~mask);\r
-\r
-            if (new_flags != data->flags) {\r
-                data->flags = new_flags;\r
-\r
-                if (d->ctx_node != d->ref_node)\r
-                    recalc_node_flags(d->ctx_node, TRUE);\r
-                else\r
-                    recalc_node_flags(d->ctx_node, FALSE);\r
-            }\r
-        }\r
-    }\r
-    LeaveCriticalSection(&cs_cfgui);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_get_flags(khui_config_node vnode) {\r
-    khui_config_node_i * node;\r
-    khm_int32 flags = 0;\r
-\r
-    if (vnode &&\r
-        !cfgui_is_valid_node_handle(vnode))\r
-        return 0;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    if (cfgui_is_valid_node_handle(vnode)) {\r
-\r
-        node = cfgui_node_i_from_handle(vnode);\r
-\r
-        flags = node->flags;\r
-    }\r
-    LeaveCriticalSection(&cs_cfgui);\r
-\r
-    return flags;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_get_name(khui_config_node vnode,\r
-                  wchar_t * buf,\r
-                  khm_size * cb_buf) {\r
-    khui_config_node_i * node;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if (!cb_buf ||\r
-        !cfgui_is_valid_node_handle(vnode))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    EnterCriticalSection(&cs_cfgui);\r
-    if (cfgui_is_valid_node_handle(vnode)) {\r
-        khm_size cb;\r
-\r
-        node = cfgui_node_i_from_handle(vnode);\r
-\r
-        StringCbLength(node->reg.name, KHUI_MAXCCH_NAME, &cb);\r
-\r
-        if (buf == NULL || cb > *cb_buf) {\r
-            *cb_buf = cb;\r
-            rv = KHM_ERROR_TOO_LONG;\r
-        } else {\r
-            StringCbCopy(buf, *cb_buf, node->reg.name);\r
-            *cb_buf = cb;\r
-        }\r
-    } else {\r
-        rv = KHM_ERROR_INVALID_PARAM;\r
-    }\r
-    LeaveCriticalSection(&cs_cfgui);\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_init_dialog_data(HWND hwnd_dlg,\r
-                          const khui_config_init_data * data,\r
-                          khm_size cb_extra,\r
-                          khui_config_init_data ** new_data,\r
-                          void ** extra) {\r
-    khm_size cb;\r
-    khui_config_init_data * d;\r
-\r
-    cb = sizeof(khui_config_init_data) + cb_extra;\r
-    d = PMALLOC(cb);\r
-#ifdef DEBUG\r
-    assert(d);\r
-#endif\r
-    ZeroMemory(d, cb);\r
-\r
-    *d = *data;\r
-\r
-    if (d->ctx_node)\r
-        khui_cfg_hold(d->ctx_node);\r
-    if (d->this_node)\r
-        khui_cfg_hold(d->this_node);\r
-    if (d->ref_node)\r
-        khui_cfg_hold(d->ref_node);\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-    SetWindowLongPtr(hwnd_dlg, DWLP_USER, (LONG_PTR) d);\r
-#pragma warning(pop)\r
-\r
-    if (new_data)\r
-        *new_data = d;\r
-    if (extra)\r
-        *extra = (void *) (d + 1);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_get_dialog_data(HWND hwnd_dlg,\r
-                         khui_config_init_data ** data,\r
-                         void ** extra) {\r
-    khui_config_init_data * d;\r
-\r
-    d = (khui_config_init_data *) (LONG_PTR) GetWindowLongPtr(hwnd_dlg,\r
-                                                              DWLP_USER);\r
-#ifdef DEBUG\r
-    assert(d);\r
-#endif\r
-\r
-    *data = d;\r
-    if (extra)\r
-        *extra = (void *) (d + 1);\r
-\r
-    return (d)?KHM_ERROR_SUCCESS: KHM_ERROR_NOT_FOUND;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_free_dialog_data(HWND hwnd_dlg) {\r
-    khui_config_init_data * d;\r
-\r
-    d = (khui_config_init_data *) (LONG_PTR) GetWindowLongPtr(hwnd_dlg,\r
-                                                              DWLP_USER);\r
-#ifdef DEBUG\r
-    assert(d);\r
-#endif\r
-\r
-    if (d) {\r
-        PFREE(d);\r
-    }\r
-\r
-    return (d)?KHM_ERROR_SUCCESS: KHM_ERROR_NOT_FOUND;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<utils.h>
+#include<assert.h>
+
+#include<strsafe.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 = PMALLOC(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)
+        PFREE((void *) node->reg.name);
+
+    if (node->reg.short_desc)
+        PFREE((void *) node->reg.short_desc);
+
+    if (node->reg.long_desc)
+        PFREE((void *) node->reg.long_desc);
+
+    node->magic = 0;
+
+    if (node->owner)
+        kmm_release_plugin(node->owner);
+
+    ZeroMemory(node, sizeof(*node));
+
+    PFREE(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_PARAM;
+
+    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 = PMALLOC(cb_name);
+    StringCbCopy(name, cb_name, reg->name);
+    short_desc = PMALLOC(cb_short_desc);
+    StringCbCopy(short_desc, cb_short_desc, reg->short_desc);
+    long_desc = PMALLOC(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);
+    }
+
+    /* plugin handles should not be obtained lightly.  For the moment,
+       the cleanup of nodes doesn't happen until module unload and
+       module unload doesn't happen until all the plugin and module
+       handles have been freed. */
+    /* node->owner = kmm_this_plugin(); */
+
+    EnterCriticalSection(&cs_cfgui);
+    TADDCHILD(parent, node);
+
+    if (hwnd_cfgui) {
+        SendMessage(hwnd_cfgui, KHUI_WM_CFG_NOTIFY,
+                    MAKEWPARAM(0, WMCFG_SYNC_NODE_LIST), 0);
+    }
+
+    LeaveCriticalSection(&cs_cfgui);
+
+    /* when the root config list changes, we need to notify the UI.
+       this way, the Options menu can be kept in sync. */
+    if (parent == cfgui_root_config) {
+        kmq_post_message(KMSG_ACT, KMSG_ACT_SYNC_CFG, 0, 0);
+    }
+
+    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_PARAM;
+
+    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_PARAM;
+
+    EnterCriticalSection(&cs_cfgui);
+    node = cfgui_node_i_from_handle(vnode);
+    node->flags |= KHUI_CN_FLAG_DELETED;
+
+    if (hwnd_cfgui) {
+        SendMessage(hwnd_cfgui, KHUI_WM_CFG_NOTIFY,
+                    MAKEWPARAM(0, WMCFG_SYNC_NODE_LIST), 0);
+    }
+
+    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_PARAM;
+
+    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_PARAM;
+
+    cfgui_release_node(cfgui_node_i_from_handle(vnode));
+
+    return KHM_ERROR_SUCCESS;
+}
+
+KHMEXP khm_int32 KHMAPI
+khui_cfg_get_parent(khui_config_node vnode,
+                    khui_config_node * result) {
+
+    khui_config_node_i * node;
+    khui_config_node_i * parent;
+
+    if(!cfgui_is_valid_node_handle(vnode) ||
+       !result)
+        return KHM_ERROR_INVALID_PARAM;
+
+    EnterCriticalSection(&cs_cfgui);
+    if (cfgui_is_valid_node_handle(vnode)) {
+        node = cfgui_node_i_from_handle(vnode);
+        parent = TPARENT(node);
+        if (parent == cfgui_root_config)
+            parent = NULL;
+    } else {
+        parent = NULL;
+    }
+    if (parent) {
+        cfgui_hold_node(parent);
+    }
+    LeaveCriticalSection(&cs_cfgui);
+
+    *result = parent;
+
+    if (parent)
+        return KHM_ERROR_SUCCESS;
+    else
+        return KHM_ERROR_NOT_FOUND;
+}
+
+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_PARAM;
+
+    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->flags & KHUI_CN_FLAG_DELETED));
+            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_PARAM;
+
+    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->flags & KHUI_CN_FLAG_DELETED));
+            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_PARAM;
+
+    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_PARAM;
+
+    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->flags & KHUI_CN_FLAG_DELETED));
+            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_PARAM;
+
+    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_PARAM;
+}
+
+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 void
+clear_node_data(khui_config_node_i * node) {
+    node->n_data = 0;
+}
+
+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 = PMALLOC(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));
+            PFREE(node->data);
+        }
+        node->data = newdata;
+    }
+
+    node->data[node->n_data].key = key;
+    node->data[node->n_data].hwnd = NULL;
+    node->data[node->n_data].param = 0;
+    node->data[node->n_data].flags = 0;
+
+    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;
+    clear_node_data(node);
+
+    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)) {
+
+        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, khm_boolean plural) {
+    khui_config_node_i * node;
+    khui_config_node_i * parent;
+    khui_config_node_i * subpanel;
+    cfg_node_data * data;
+    khm_int32 flags;
+
+#ifdef DEBUG
+    assert(cfgui_is_valid_node_handle(vnode));
+#endif
+
+    node = cfgui_node_i_from_handle(vnode);
+
+    if (plural)
+        parent = TPARENT(node);
+    else
+        parent = node;
+#ifdef DEBUG
+    assert(parent);
+#endif
+
+    flags = 0;
+
+    for(subpanel = TFIRSTCHILD(parent); subpanel;
+        subpanel = LNEXT(subpanel)) {
+        if (!(subpanel->reg.flags & KHUI_CNFLAG_SUBPANEL) ||
+            (plural && !(subpanel->reg.flags & KHUI_CNFLAG_PLURAL)) ||
+            (!plural && (subpanel->reg.flags & KHUI_CNFLAG_PLURAL)))
+            continue;
+
+        data = get_node_data(subpanel,
+                             vnode,
+                             FALSE);
+
+        if (data) {
+            flags |= data->flags;
+        }
+    }
+
+    flags &= KHUI_CNFLAGMASK_DYNAMIC;
+
+    if ((node->flags & KHUI_CNFLAGMASK_DYNAMIC) == flags)
+        return;
+
+    node->flags = (node->flags & ~KHUI_CNFLAGMASK_DYNAMIC) | flags;
+
+    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, TRUE);
+                else
+                    recalc_node_flags(d->ctx_node, FALSE);
+            }
+        }
+    }
+    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)) {
+
+        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_PARAM;
+
+    EnterCriticalSection(&cs_cfgui);
+    if (cfgui_is_valid_node_handle(vnode)) {
+        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_PARAM;
+    }
+    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 = PMALLOC(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) {
+        PFREE(d);
+    }
+
+    return (d)?KHM_ERROR_SUCCESS: KHM_ERROR_NOT_FOUND;
+}
index c7ff88bdb1cb67f60c4b34dc21ff23c7779f975d..bd3a13e8ef42f80f0e1415a5522f17ce68d47f62 100644 (file)
@@ -1,74 +1,74 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_CONFIGUI_H\r
-#define __KHIMAIRA_CONFIGUI_H\r
-\r
-typedef struct tag_cfg_node_data {\r
-    void *      key;\r
-    HWND        hwnd;\r
-    LPARAM      param;\r
-    khm_int32   flags;\r
-} cfg_node_data;\r
-\r
-typedef struct tag_khui_config_node_i {\r
-    khm_int32   magic;\r
-\r
-    khui_config_node_reg reg;\r
-    kmm_plugin  owner;\r
-    khm_int32   id;\r
-\r
-    HWND        hwnd;\r
-    LPARAM      param;\r
-\r
-    cfg_node_data * data;\r
-    khm_size    n_data;\r
-    khm_size    nc_data;\r
-\r
-    khm_int32   refcount;\r
-    khm_int32   flags;\r
-    TDCL(struct tag_khui_config_node_i);\r
-} khui_config_node_i;\r
-\r
-#define KHUI_CONFIG_NODE_MAGIC 0x38f4cb52\r
-\r
-#define KHUI_NODEDATA_ALLOC_INCR 8\r
-\r
-#define KHUI_CN_FLAG_DELETED 0x0008\r
-\r
-#define cfgui_is_valid_node_handle(v) \\r
-((v) && ((khui_config_node_i *) (v))->magic == KHUI_CONFIG_NODE_MAGIC)\r
-\r
-#define cfgui_is_valid_node(n) \\r
-((n)->magic == KHUI_CONFIG_NODE_MAGIC)\r
-\r
-#define cfgui_node_i_from_handle(v) \\r
-((khui_config_node_i *) v)\r
-\r
-#define cfgui_handle_from_node_i(n) \\r
-((khui_config_node) n)\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
index c89a446203f0813aca80bcee18a700fae468b0a7..08b0a149d690802245d959a5eb897e448ad4d405 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khuidefs.h>\r
-#include<utils.h>\r
-#include<assert.h>\r
-\r
-#include<strsafe.h>\r
-\r
-#define CW_ALLOC_INCR 8\r
-\r
-static void cw_free_prompts(khui_new_creds * c);\r
-\r
-static void cw_free_prompt(khui_new_creds_prompt * p);\r
-\r
-static khui_new_creds_prompt * \r
-cw_create_prompt(\r
-    khm_size idx,\r
-    khm_int32 type,\r
-    wchar_t * prompt,\r
-    wchar_t * def,\r
-    khm_int32 flags);\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_create_cred_blob(khui_new_creds ** ppnc)\r
-{\r
-    khui_new_creds * c;\r
-\r
-    c = PMALLOC(sizeof(*c));\r
-    ZeroMemory(c, sizeof(*c));\r
-\r
-    c->magic = KHUI_NC_MAGIC;\r
-    InitializeCriticalSection(&c->cs);\r
-    c->result = KHUI_NC_RESULT_CANCEL;\r
-    c->mode = KHUI_NC_MODE_MINI;\r
-\r
-    khui_context_create(&c->ctx, KHUI_SCOPE_NONE, NULL, KCDB_CREDTYPE_INVALID, NULL);\r
-\r
-    *ppnc = c;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_destroy_cred_blob(khui_new_creds *c)\r
-{\r
-    khm_size i;\r
-    size_t len;\r
-    EnterCriticalSection(&c->cs);\r
-    for(i=0;i<c->n_identities;i++) {\r
-        kcdb_identity_release(c->identities[i]);\r
-    }\r
-    cw_free_prompts(c);\r
-    khui_context_release(&c->ctx);\r
-    LeaveCriticalSection(&c->cs);\r
-    DeleteCriticalSection(&c->cs);\r
-\r
-    if (c->password) {\r
-        len = wcslen(c->password);\r
-        SecureZeroMemory(c->password, sizeof(wchar_t) * len);\r
-        PFREE(c->password);\r
-    }\r
-\r
-    if (c->identities)\r
-        PFREE(c->identities);\r
-\r
-    if (c->types)\r
-        PFREE(c->types);\r
-\r
-    if (c->type_subs)\r
-        PFREE(c->type_subs);\r
-\r
-    if (c->window_title)\r
-        PFREE(c->window_title);\r
-\r
-    PFREE(c);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_lock_nc(khui_new_creds * c)\r
-{\r
-    EnterCriticalSection(&c->cs);\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_unlock_nc(khui_new_creds * c)\r
-{\r
-    LeaveCriticalSection(&c->cs);\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-#define NC_N_IDENTITIES 4\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_add_identity(khui_new_creds * c, \r
-                     khm_handle id)\r
-{\r
-    if(id == NULL)\r
-        return KHM_ERROR_SUCCESS; /* we return success because adding\r
-                                  a NULL id is equivalent to adding\r
-                                  nothing. */\r
-    EnterCriticalSection(&(c->cs));\r
-\r
-    if(c->identities == NULL) {\r
-        c->nc_identities = NC_N_IDENTITIES;\r
-        c->identities = PMALLOC(sizeof(*(c->identities)) * \r
-                               c->nc_identities);\r
-        c->n_identities = 0;\r
-    } else if(c->n_identities + 1 > c->nc_identities) {\r
-        khm_handle * ni;\r
-\r
-        c->nc_identities = UBOUNDSS(c->n_identities + 1, \r
-                                    NC_N_IDENTITIES, \r
-                                    NC_N_IDENTITIES);\r
-        ni = PMALLOC(sizeof(*(c->identities)) * c->nc_identities);\r
-        memcpy(ni, c->identities, \r
-               sizeof(*(c->identities)) * c->n_identities);\r
-        PFREE(c->identities);\r
-        c->identities = ni;\r
-    }\r
-\r
-    kcdb_identity_hold(id);\r
-    c->identities[c->n_identities++] = id;\r
-    LeaveCriticalSection(&(c->cs));\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_set_primary_id(khui_new_creds * c, \r
-                       khm_handle id)\r
-{\r
-    khm_size  i;\r
-    khm_int32 rv;\r
-\r
-    EnterCriticalSection(&c->cs);\r
-\r
-    /* no change */\r
-    if((c->n_identities > 0 && c->identities[0] == id) ||\r
-       (c->n_identities == 0 && id == NULL)) {\r
-        LeaveCriticalSection(&c->cs);\r
-        return KHM_ERROR_SUCCESS;\r
-    }\r
-\r
-    for(i=0; i<c->n_identities; i++) {\r
-        kcdb_identity_release(c->identities[i]);\r
-    }\r
-    c->n_identities = 0;\r
-\r
-    LeaveCriticalSection(&(c->cs));\r
-    rv = khui_cw_add_identity(c,id);\r
-    if(c->hwnd != NULL) {\r
-        PostMessage(c->hwnd, KHUI_WM_NC_NOTIFY, \r
-                    MAKEWPARAM(0, WMNC_IDENTITY_CHANGE), 0);\r
-    }\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_add_type(khui_new_creds * c, \r
-                 khui_new_creds_by_type * t)\r
-{\r
-    EnterCriticalSection(&c->cs);\r
-\r
-    if(c->n_types >= KHUI_MAX_NCTYPES) {\r
-        LeaveCriticalSection(&c->cs);\r
-        return KHM_ERROR_OUT_OF_BOUNDS;\r
-    }\r
-\r
-    if(c->types == NULL) {\r
-        c->nc_types = CW_ALLOC_INCR;\r
-        c->types = PMALLOC(sizeof(*(c->types)) * c->nc_types);\r
-        c->type_subs = PMALLOC(sizeof(*(c->type_subs)) * c->nc_types);\r
-        c->n_types = 0;\r
-    }\r
-\r
-    if(c->nc_types < c->n_types + 1) {\r
-        void * t;\r
-        khm_size n;\r
-\r
-        n = UBOUNDSS(c->n_types + 1, CW_ALLOC_INCR, CW_ALLOC_INCR);\r
-\r
-        t = PMALLOC(sizeof(*(c->types)) * n);\r
-        memcpy(t, (void *) c->types, sizeof(*(c->types)) * c->n_types);\r
-        PFREE(c->types);\r
-        c->types = t;\r
-\r
-        t = PMALLOC(sizeof(*(c->type_subs)) * n);\r
-        memcpy(t, (void *) c->type_subs, sizeof(*(c->type_subs)) * c->n_types);\r
-        PFREE(c->type_subs);\r
-        c->type_subs = t;\r
-\r
-        c->nc_types = n;\r
-    }\r
-\r
-    c->type_subs[c->n_types] = kcdb_credtype_get_sub(t->type);\r
-    c->types[c->n_types++] = t;\r
-    t->nc = c;\r
-    LeaveCriticalSection(&c->cs);\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_del_type(khui_new_creds * c, \r
-                 khm_int32 type_id)\r
-{\r
-    khm_size  i;\r
-\r
-    EnterCriticalSection(&c->cs);\r
-    for(i=0; i < c->n_types; i++) {\r
-        if(c->types[i]->type == type_id)\r
-            break;\r
-    }\r
-    if(i >= c->n_types) {\r
-        LeaveCriticalSection(&c->cs);\r
-        return KHM_ERROR_NOT_FOUND;\r
-    }\r
-    c->n_types--;\r
-    for(;i < c->n_types; i++) {\r
-        c->types[i] = c->types[i+1];\r
-        c->type_subs[i] = c->type_subs[i+1];\r
-    }\r
-    LeaveCriticalSection(&c->cs);\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_find_type(khui_new_creds * c, \r
-                  khm_int32 type, \r
-                  khui_new_creds_by_type **t)\r
-{\r
-    khm_size i;\r
-\r
-    EnterCriticalSection(&c->cs);\r
-    *t = NULL;\r
-    for(i=0;i<c->n_types;i++) {\r
-        if(c->types[i]->type == type) {\r
-            *t = c->types[i];\r
-            break;\r
-        }\r
-    }\r
-    LeaveCriticalSection(&c->cs);\r
-\r
-    if(*t)\r
-        return KHM_ERROR_SUCCESS;\r
-    return KHM_ERROR_NOT_FOUND;\r
-}\r
-\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_enable_type(khui_new_creds * c,\r
-                    khm_int32 type,\r
-                    khm_boolean enable)\r
-{\r
-    khui_new_creds_by_type * t = NULL;\r
-    BOOL delta = FALSE;\r
-\r
-    EnterCriticalSection(&c->cs);\r
-    if(KHM_SUCCEEDED(khui_cw_find_type(c, type, &t))) {\r
-        if(enable) {\r
-            delta = t->flags & KHUI_NCT_FLAG_DISABLED;\r
-            t->flags &= ~KHUI_NCT_FLAG_DISABLED;\r
-        }\r
-        else {\r
-            delta = !(t->flags & KHUI_NCT_FLAG_DISABLED);\r
-            t->flags |= KHUI_NCT_FLAG_DISABLED;\r
-        }\r
-    }\r
-    LeaveCriticalSection(&c->cs);\r
-\r
-    if(delta)\r
-        PostMessage(c->hwnd, KHUI_WM_NC_NOTIFY, MAKEWPARAM(0,WMNC_TYPE_STATE), (LPARAM) type);\r
-\r
-    return (t)?KHM_ERROR_SUCCESS:KHM_ERROR_NOT_FOUND;\r
-}\r
-\r
-KHMEXP khm_boolean KHMAPI \r
-khui_cw_type_succeeded(khui_new_creds * c,\r
-                       khm_int32 type)\r
-{\r
-    khui_new_creds_by_type * t;\r
-    khm_boolean s;\r
-\r
-    EnterCriticalSection(&c->cs);\r
-    if(KHM_SUCCEEDED(khui_cw_find_type(c, type, &t))) {\r
-        s = (t->flags & KHUI_NCT_FLAG_PROCESSED) && !(t->flags & KHUI_NC_RESPONSE_FAILED);\r
-    } else {\r
-        s = FALSE;\r
-    }\r
-    LeaveCriticalSection(&c->cs);\r
-\r
-    return s;\r
-}\r
-\r
-static khui_new_creds_prompt * \r
-cw_create_prompt(khm_size idx,\r
-                 khm_int32 type,\r
-                 wchar_t * prompt,\r
-                 wchar_t * def,\r
-                 khm_int32 flags)\r
-{\r
-    khui_new_creds_prompt * p;\r
-    size_t cb_prompt = 0;\r
-    size_t cb_def = 0;\r
-\r
-    if(prompt && FAILED(StringCbLength(prompt, KHUI_MAXCB_PROMPT, &cb_prompt)))\r
-        return NULL;\r
-    if(def && FAILED(StringCbLength(def, KHUI_MAXCB_PROMPT_VALUE, &cb_def)))\r
-        return NULL;\r
-\r
-    p = PMALLOC(sizeof(*p));\r
-    ZeroMemory(p, sizeof(*p));\r
-\r
-    if(prompt) {\r
-        cb_prompt += sizeof(wchar_t);\r
-        p->prompt = PMALLOC(cb_prompt);\r
-        StringCbCopy(p->prompt, cb_prompt, prompt);\r
-    }\r
-\r
-    if(def && cb_def > 0) {\r
-        cb_def += sizeof(wchar_t);\r
-        p->def = PMALLOC(cb_def);\r
-        StringCbCopy(p->def, cb_def, def);\r
-    }\r
-\r
-    p->value = PMALLOC(KHUI_MAXCB_PROMPT_VALUE);\r
-    ZeroMemory(p->value, KHUI_MAXCB_PROMPT_VALUE);\r
-\r
-    p->type = type;\r
-    p->flags = flags;\r
-    p->index = idx;\r
-\r
-    return p;\r
-}\r
-\r
-static void \r
-cw_free_prompt(khui_new_creds_prompt * p) {\r
-    size_t cb;\r
-\r
-    if(p->prompt) {\r
-        if(SUCCEEDED(StringCbLength(p->prompt, KHUI_MAXCB_PROMPT, &cb)))\r
-            SecureZeroMemory(p->prompt, cb);\r
-        PFREE(p->prompt);\r
-    }\r
-\r
-    if(p->def) {\r
-        if(SUCCEEDED(StringCbLength(p->def, KHUI_MAXCB_PROMPT, &cb)))\r
-            SecureZeroMemory(p->def, cb);\r
-        PFREE(p->def);\r
-    }\r
-\r
-    if(p->value) {\r
-        if(SUCCEEDED(StringCbLength(p->value, KHUI_MAXCB_PROMPT_VALUE, &cb)))\r
-            SecureZeroMemory(p->value, cb);\r
-        PFREE(p->value);\r
-    }\r
-\r
-    PFREE(p);\r
-}\r
-\r
-static void \r
-cw_free_prompts(khui_new_creds * c)\r
-{\r
-    khm_size i;\r
-\r
-    if(c->banner != NULL) {\r
-        PFREE(c->banner);\r
-        c->banner = NULL;\r
-    }\r
-\r
-    if(c->pname != NULL) {\r
-        PFREE(c->pname);\r
-        c->pname = NULL;\r
-    }\r
-\r
-    for(i=0;i < c->n_prompts; i++) {\r
-        if(c->prompts[i]) {\r
-            cw_free_prompt(c->prompts[i]);\r
-            c->prompts[i] = NULL;\r
-        }\r
-    }\r
-\r
-    if(c->prompts != NULL) {\r
-        PFREE(c->prompts);\r
-        c->prompts = NULL;\r
-    }\r
-\r
-    c->nc_prompts = 0;\r
-    c->n_prompts = 0;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_clear_prompts(khui_new_creds * c)\r
-{\r
-    /* the WMNC_CLEAR_PROMPT message needs to be sent before freeing\r
-       the prompts, because the prompts structure still holds the\r
-       window handles for the custom prompt controls. */\r
-    SendMessage(c->hwnd, KHUI_WM_NC_NOTIFY, \r
-                MAKEWPARAM(0,WMNC_CLEAR_PROMPTS), (LPARAM) c);\r
-\r
-    EnterCriticalSection(&c->cs);\r
-    cw_free_prompts(c);\r
-    LeaveCriticalSection(&c->cs);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_begin_custom_prompts(khui_new_creds * c, \r
-                             khm_size n_prompts, \r
-                             wchar_t * banner, \r
-                             wchar_t * pname)\r
-{\r
-    size_t cb;\r
-\r
-    PostMessage(c->hwnd, KHUI_WM_NC_NOTIFY, \r
-                MAKEWPARAM(0,WMNC_CLEAR_PROMPTS), (LPARAM) c);\r
-\r
-    EnterCriticalSection(&c->cs);\r
-#ifdef DEBUG\r
-    assert(c->n_prompts == 0);\r
-#endif\r
-    cw_free_prompts(c);\r
-\r
-    if(SUCCEEDED(StringCbLength(banner, KHUI_MAXCB_BANNER, &cb)) && \r
-       cb > 0) {\r
-        cb += sizeof(wchar_t);\r
-        c->banner = PMALLOC(cb);\r
-        StringCbCopy(c->banner, cb, banner);\r
-    } else {\r
-        c->banner = NULL;\r
-    }\r
-\r
-    if(SUCCEEDED(StringCbLength(pname, KHUI_MAXCB_PNAME, &cb)) && \r
-       cb > 0) {\r
-\r
-        cb += sizeof(wchar_t);\r
-        c->pname = PMALLOC(cb);\r
-        StringCbCopy(c->pname, cb, pname);\r
-\r
-    } else {\r
-\r
-        c->pname = NULL;\r
-\r
-    }\r
-\r
-    if(n_prompts > 0) {\r
-        c->prompts = PMALLOC(sizeof(*(c->prompts)) * n_prompts);\r
-        ZeroMemory(c->prompts, sizeof(*(c->prompts)) * n_prompts);\r
-        c->nc_prompts = n_prompts;\r
-        c->n_prompts = 0;\r
-\r
-    } else {\r
-\r
-        c->prompts = NULL;\r
-        c->n_prompts = 0;\r
-        c->nc_prompts = 0;\r
-\r
-        PostMessage(c->hwnd, KHUI_WM_NC_NOTIFY, \r
-                    MAKEWPARAM(0, WMNC_SET_PROMPTS), (LPARAM) c);\r
-    }\r
-\r
-    LeaveCriticalSection(&c->cs);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_add_prompt(khui_new_creds * c, \r
-                   khm_int32 type, \r
-                   wchar_t * prompt, \r
-                   wchar_t * def, \r
-                   khm_int32 flags)\r
-{\r
-    khui_new_creds_prompt * p;\r
-\r
-    if(c->nc_prompts == 0 ||\r
-        c->n_prompts == c->nc_prompts)\r
-        return KHM_ERROR_INVALID_OPERATION;\r
-\r
-#ifdef DEBUG\r
-    assert(c->prompts != NULL);\r
-#endif\r
-\r
-    EnterCriticalSection(&c->cs);\r
-    p = cw_create_prompt(c->n_prompts, type, prompt, def, flags);\r
-    if(p == NULL) {\r
-        LeaveCriticalSection(&c->cs);\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-    c->prompts[c->n_prompts++] = p;\r
-    LeaveCriticalSection(&c->cs);\r
-\r
-    if(c->n_prompts == c->nc_prompts) {\r
-        PostMessage(c->hwnd, KHUI_WM_NC_NOTIFY, \r
-                    MAKEWPARAM(0, WMNC_SET_PROMPTS), (LPARAM) c);\r
-        /* once we are done adding prompts, switch to the auth\r
-           panel */\r
-#if 0\r
-        /* Actually, don't. Doing so can mean an unexpected panel\r
-           switch if fiddling on some other panel causes a change in\r
-           custom prompts. */\r
-        SendMessage(c->hwnd, KHUI_WM_NC_NOTIFY, \r
-                    MAKEWPARAM(0, WMNC_DIALOG_SWITCH_PANEL), \r
-                    (LPARAM) c);\r
-#endif\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_get_prompt_count(khui_new_creds * c,\r
-                         khm_size * np) {\r
-\r
-    EnterCriticalSection(&c->cs);\r
-    *np = c->n_prompts;\r
-    LeaveCriticalSection(&c->cs);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_get_prompt(khui_new_creds * c, \r
-                   khm_size idx, \r
-                   khui_new_creds_prompt ** prompt)\r
-{\r
-    khm_int32 rv;\r
-\r
-    EnterCriticalSection(&c->cs);\r
-    if(c->n_prompts <= idx ||\r
-       c->prompts == NULL) {\r
-\r
-        rv = KHM_ERROR_OUT_OF_BOUNDS;\r
-        *prompt = NULL;\r
-    } else {\r
-\r
-        *prompt = c->prompts[idx];\r
-        rv = KHM_ERROR_SUCCESS;\r
-    }\r
-    LeaveCriticalSection(&c->cs);\r
-\r
-    return rv;\r
-}\r
-\r
-void\r
-khuiint_trim_str(wchar_t * s, khm_size cch) {\r
-    wchar_t * c, * last_ws;\r
-\r
-    for (c = s; *c && iswspace(*c) && ((khm_size)(c - s)) < cch; c++);\r
-\r
-    if (((khm_size)(c - s)) >= cch)\r
-        return;\r
-\r
-    if (c != s && ((khm_size)(c - s)) < cch) {\r
-#if _MSC_VER >= 1400\r
-        wmemmove_s(s, cch, c, cch - ((khm_size)(c - s)));\r
-#else\r
-        memmove(s, c, (cch - ((khm_size)(c - s)))* sizeof(wchar_t));\r
-#endif\r
-    }\r
-\r
-    last_ws = NULL;\r
-    for (c = s; *c && ((khm_size)(c - s)) < cch; c++) {\r
-        if (!iswspace(*c))\r
-            last_ws = NULL;\r
-        else if (last_ws == NULL)\r
-            last_ws = c;\r
-    }\r
-\r
-    if (last_ws)\r
-        *last_ws = L'\0';\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_sync_prompt_values(khui_new_creds * c)\r
-{\r
-    khm_size i;\r
-    khm_size n;\r
-    HWND hw;\r
-    wchar_t tmpbuf[KHUI_MAXCCH_PROMPT_VALUE];\r
-\r
-    EnterCriticalSection(&c->cs);\r
- redo_loop:\r
-    n = c->n_prompts;\r
-    for(i=0; i<n; i++) {\r
-        khui_new_creds_prompt * p;\r
-\r
-        p = c->prompts[i];\r
-        if(p->hwnd_edit) {\r
-            hw = p->hwnd_edit;\r
-            LeaveCriticalSection(&c->cs);\r
-\r
-            GetWindowText(hw, tmpbuf, ARRAYLENGTH(tmpbuf));\r
-            khuiint_trim_str(tmpbuf, ARRAYLENGTH(tmpbuf));\r
-\r
-            EnterCriticalSection(&c->cs);\r
-            if (n != c->n_prompts)\r
-                goto redo_loop;\r
-            SecureZeroMemory(p->value, KHUI_MAXCB_PROMPT_VALUE);\r
-            StringCchCopy(p->value, KHUI_MAXCCH_PROMPT_VALUE,\r
-                          tmpbuf);\r
-        }\r
-    }\r
-    LeaveCriticalSection(&c->cs);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_get_prompt_value(khui_new_creds * c, \r
-                         khm_size idx, \r
-                         wchar_t * buf, \r
-                         khm_size *cbbuf)\r
-{\r
-    khui_new_creds_prompt * p;\r
-    khm_int32 rv;\r
-    size_t cb;\r
-\r
-    rv = khui_cw_get_prompt(c, idx, &p);\r
-    if(KHM_FAILED(rv))\r
-        return rv;\r
-\r
-    EnterCriticalSection(&c->cs);\r
-\r
-    if(FAILED(StringCbLength(p->value, KHUI_MAXCB_PROMPT_VALUE, &cb))) {\r
-        *cbbuf = 0;\r
-        if(buf != NULL)\r
-            *buf = 0;\r
-        LeaveCriticalSection(&c->cs);\r
-        return KHM_ERROR_SUCCESS;\r
-    }\r
-    cb += sizeof(wchar_t);\r
-\r
-    if(buf == NULL || *cbbuf < cb) {\r
-        *cbbuf = cb;\r
-        LeaveCriticalSection(&c->cs);\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-\r
-    StringCbCopy(buf, *cbbuf, p->value);\r
-    *cbbuf = cb;\r
-    LeaveCriticalSection(&c->cs);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_set_response(khui_new_creds * c, \r
-                     khm_int32 type, \r
-                     khm_int32 response)\r
-{\r
-    khui_new_creds_by_type * t = NULL;\r
-    EnterCriticalSection(&c->cs);\r
-    khui_cw_find_type(c, type, &t);\r
-    c->response |= response & KHUI_NCMASK_RESPONSE;\r
-    if(t) {\r
-        t->flags &= ~KHUI_NCMASK_RESULT;\r
-        t->flags |= (response & KHUI_NCMASK_RESULT);\r
-\r
-        if (!(response & KHUI_NC_RESPONSE_NOEXIT) &&\r
-            !(response & KHUI_NC_RESPONSE_PENDING))\r
-            t->flags |= KHUI_NC_RESPONSE_COMPLETED;\r
-    }\r
-    LeaveCriticalSection(&c->cs);\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-/* only called from a identity provider callback */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cw_add_control_row(khui_new_creds * c,\r
-                        HWND label,\r
-                        HWND input,\r
-                        khui_control_size size)\r
-{\r
-    if (c && c->hwnd) {\r
-        khui_control_row row;\r
-\r
-        row.label = label;\r
-        row.input = input;\r
-        row.size = size;\r
-\r
-        SendMessage(c->hwnd,\r
-                    KHUI_WM_NC_NOTIFY,\r
-                    MAKEWPARAM(0, WMNC_ADD_CONTROL_ROW),\r
-                    (LPARAM) &row);\r
-\r
-        return KHM_ERROR_SUCCESS;\r
-    } else {\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<utils.h>
+#include<assert.h>
+
+#include<strsafe.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 = PMALLOC(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;
+
+    khui_context_create(&c->ctx, KHUI_SCOPE_NONE, NULL, KCDB_CREDTYPE_INVALID, NULL);
+
+    *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);
+        PFREE(c->password);
+    }
+
+    if (c->identities)
+        PFREE(c->identities);
+
+    if (c->types)
+        PFREE(c->types);
+
+    if (c->type_subs)
+        PFREE(c->type_subs);
+
+    if (c->window_title)
+        PFREE(c->window_title);
+
+    PFREE(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 = PMALLOC(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 = PMALLOC(sizeof(*(c->identities)) * c->nc_identities);
+        memcpy(ni, c->identities, 
+               sizeof(*(c->identities)) * c->n_identities);
+        PFREE(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 = PMALLOC(sizeof(*(c->types)) * c->nc_types);
+        c->type_subs = PMALLOC(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 = PMALLOC(sizeof(*(c->types)) * n);
+        memcpy(t, (void *) c->types, sizeof(*(c->types)) * c->n_types);
+        PFREE(c->types);
+        c->types = t;
+
+        t = PMALLOC(sizeof(*(c->type_subs)) * n);
+        memcpy(t, (void *) c->type_subs, sizeof(*(c->type_subs)) * c->n_types);
+        PFREE(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 = 0;
+    size_t cb_def = 0;
+
+    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 = PMALLOC(sizeof(*p));
+    ZeroMemory(p, sizeof(*p));
+
+    if(prompt) {
+        cb_prompt += sizeof(wchar_t);
+        p->prompt = PMALLOC(cb_prompt);
+        StringCbCopy(p->prompt, cb_prompt, prompt);
+    }
+
+    if(def && cb_def > 0) {
+        cb_def += sizeof(wchar_t);
+        p->def = PMALLOC(cb_def);
+        StringCbCopy(p->def, cb_def, def);
+    }
+
+    p->value = PMALLOC(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);
+        PFREE(p->prompt);
+    }
+
+    if(p->def) {
+        if(SUCCEEDED(StringCbLength(p->def, KHUI_MAXCB_PROMPT, &cb)))
+            SecureZeroMemory(p->def, cb);
+        PFREE(p->def);
+    }
+
+    if(p->value) {
+        if(SUCCEEDED(StringCbLength(p->value, KHUI_MAXCB_PROMPT_VALUE, &cb)))
+            SecureZeroMemory(p->value, cb);
+        PFREE(p->value);
+    }
+
+    PFREE(p);
+}
+
+static void 
+cw_free_prompts(khui_new_creds * c)
+{
+    khm_size i;
+
+    if(c->banner != NULL) {
+        PFREE(c->banner);
+        c->banner = NULL;
+    }
+
+    if(c->pname != NULL) {
+        PFREE(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) {
+        PFREE(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)
+{
+    /* the WMNC_CLEAR_PROMPT message needs to be sent before freeing
+       the prompts, because the prompts structure still holds the
+       window handles for the custom prompt controls. */
+    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 = PMALLOC(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 = PMALLOC(cb);
+        StringCbCopy(c->pname, cb, pname);
+
+    } else {
+
+        c->pname = NULL;
+
+    }
+
+    if(n_prompts > 0) {
+        c->prompts = PMALLOC(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_PARAM;
+    }
+    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;
+}
+
+void
+khuiint_trim_str(wchar_t * s, khm_size cch) {
+    wchar_t * c, * last_ws;
+
+    for (c = s; *c && iswspace(*c) && ((khm_size)(c - s)) < cch; c++);
+
+    if (((khm_size)(c - s)) >= cch)
+        return;
+
+    if (c != s && ((khm_size)(c - s)) < cch) {
+#if _MSC_VER >= 1400
+        wmemmove_s(s, cch, c, cch - ((khm_size)(c - s)));
+#else
+        memmove(s, c, (cch - ((khm_size)(c - s)))* sizeof(wchar_t));
+#endif
+    }
+
+    last_ws = NULL;
+    for (c = s; *c && ((khm_size)(c - s)) < cch; c++) {
+        if (!iswspace(*c))
+            last_ws = NULL;
+        else if (last_ws == NULL)
+            last_ws = c;
+    }
+
+    if (last_ws)
+        *last_ws = L'\0';
+}
+
+KHMEXP khm_int32 KHMAPI 
+khui_cw_sync_prompt_values(khui_new_creds * c)
+{
+    khm_size i;
+    khm_size n;
+    HWND hw;
+    wchar_t tmpbuf[KHUI_MAXCCH_PROMPT_VALUE];
+
+    EnterCriticalSection(&c->cs);
+ redo_loop:
+    n = c->n_prompts;
+    for(i=0; i<n; i++) {
+        khui_new_creds_prompt * p;
+
+        p = c->prompts[i];
+        if(p->hwnd_edit) {
+            hw = p->hwnd_edit;
+            LeaveCriticalSection(&c->cs);
+
+            GetWindowText(hw, tmpbuf, ARRAYLENGTH(tmpbuf));
+            khuiint_trim_str(tmpbuf, ARRAYLENGTH(tmpbuf));
+
+            EnterCriticalSection(&c->cs);
+            if (n != c->n_prompts)
+                goto redo_loop;
+            SecureZeroMemory(p->value, KHUI_MAXCB_PROMPT_VALUE);
+            StringCchCopy(p->value, KHUI_MAXCCH_PROMPT_VALUE,
+                          tmpbuf);
+        }
+    }
+    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_PARAM;
+    }
+}
index 44348b9bbb85d07d26a720d72f5e3e02af863a0a..2b7aa973a3bb18b0a07d2adab175b04af0e5c04d 100644 (file)
-/*\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __NETIDMGR_ACTION_H_INTERNAL\r
-#define __NETIDMGR_ACTION_H_INTERNAL\r
-\r
-/* Internal declarations for exports and data structured used in\r
-   nidmgr32.dll and netidmgr.exe */\r
-\r
-extern HWND khui_hwnd_main;\r
-\r
-typedef struct tag_khui_ui_callback_data {\r
-    khm_int32       magic;\r
-    khm_ui_callback cb;\r
-    void *          rock;\r
-    khm_int32       rv;\r
-} khui_ui_callback_data;\r
-\r
-#define KHUI_UICBDATA_MAGIC 0x8a08572a\r
-\r
-/*! \addtogroup khui_actions\r
-@{ */\r
-\r
-/*! \brief An action */\r
-typedef struct tag_khui_action {\r
-    khm_int32 cmd;            /*!< action identifier */\r
-    khm_int32 type;           /*!< combination of KHUI_ACTIONTYPE_* */\r
-    wchar_t * name;           /*!< name for named actions.  NULL if\r
-                                not named. */\r
-\r
-    /* The following fields are only for use by NetIDMgr */\r
-    khm_int16 ib_normal;      /*!< (internal) normal bitmap (index) (toolbar sized icon) */\r
-    khm_int16 ib_hot;         /*!< (internal) hot bitmap (index) (toolbar sized icon) */\r
-    khm_int16 ib_disabled;    /*!< (internal) disabled bitmap (index) (toolbar sized icon) */\r
-\r
-    khm_int16 ib_icon;        /*!< (internal) index of small (16x16) icon (for menu) (small icon) */\r
-    khm_int16 ib_icon_dis;    /*!< (internal) index of disabled (greyed) icon (small icon) */\r
-\r
-    khm_int16 is_caption;     /*!< (internal) index of string resource for caption */\r
-    khm_int16 is_tooltip;     /*!< (internal) same for description / tooltip */\r
-    khm_int16 ih_topic;       /*!< (internal) help topic */\r
-\r
-    /* The following fields are specified for custom actions */\r
-    wchar_t * caption;        /*!< Caption (localized) (limited by\r
-                                  KHUI_MAXCCH_SHORT_DESC).  The\r
-                                  caption is used for representing the\r
-                                  action in menus and toolbars. */\r
-    wchar_t * tooltip;        /*!< Tooltip (localized) (limited by\r
-                                  KHUI_MAXCCH_SHORT_DESC).  If this is\r
-                                  specified, whenever the user hovers\r
-                                  over the menu item or toolbar button\r
-                                  representing the action, the tooltip\r
-                                  will be displayed either on a\r
-                                  tooltip window or in the status\r
-                                  bar. */\r
-    khm_handle listener;      /*!< Listener of this action.  Should be\r
-                                  a handle to a message\r
-                                  subscription. When the action is\r
-                                  invoked, a message of type\r
-                                  ::KMSG_ACT and subtype\r
-                                  ::KMSG_ACT_ACTIVATE will be posted\r
-                                  to this subscriber. The \a uparam\r
-                                  parameter of the message will have\r
-                                  the identifier of the action. */\r
-    void *    data;           /*!< User data for custom action.  This\r
-                                  field is not used by the UI library.\r
-                                  It is reserved for plugins to store\r
-                                  data that is specific for this\r
-                                  action.  The data that's passed in\r
-                                  in the \a userdata parameter to\r
-                                  khui_action_create() will be stored\r
-                                  here and can be retrieved by calling\r
-                                  khui_action_get_data(). */\r
-    void *    reserved1;      /*!< Reserved. */\r
-    void *    reserved2;      /*!< Reserved. */\r
-    void *    reserved3;      /*!< Reserved. */\r
-\r
-    /* For all actions */\r
-    int state;                /*!< current state. combination of\r
-                                  KHUI_ACTIONSTATE_* */\r
-} khui_action;\r
-\r
-/*@}*/\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 __NETIDMGR_ACTION_H_INTERNAL
+#define __NETIDMGR_ACTION_H_INTERNAL
+
+/* Internal declarations for exports and data structured used in
+   nidmgr32.dll and netidmgr.exe */
+
+extern HWND khui_hwnd_main;
+
+typedef struct tag_khui_ui_callback_data {
+    khm_int32       magic;
+    khm_ui_callback cb;
+    void *          rock;
+    khm_int32       rv;
+} khui_ui_callback_data;
+
+#define KHUI_UICBDATA_MAGIC 0x8a08572a
+
+/*! \addtogroup khui_actions
+@{ */
+
+/*! \brief An action */
+typedef struct tag_khui_action {
+    khm_int32 cmd;            /*!< action identifier */
+    khm_int32 type;           /*!< combination of KHUI_ACTIONTYPE_* */
+    wchar_t * name;           /*!< name for named actions.  NULL if
+                                not named. */
+
+    /* The following fields are only for use by NetIDMgr */
+    khm_int16 ib_normal;      /*!< (internal) normal bitmap (index) (toolbar sized icon) */
+    khm_int16 ib_hot;         /*!< (internal) hot bitmap (index) (toolbar sized icon) */
+    khm_int16 ib_disabled;    /*!< (internal) disabled bitmap (index) (toolbar sized icon) */
+
+    khm_int16 ib_icon;        /*!< (internal) index of small (16x16) icon (for menu) (small icon) */
+    khm_int16 ib_icon_dis;    /*!< (internal) index of disabled (greyed) icon (small icon) */
+
+    khm_int16 is_caption;     /*!< (internal) index of string resource for caption */
+    khm_int16 is_tooltip;     /*!< (internal) same for description / tooltip */
+    khm_int16 ih_topic;       /*!< (internal) help topic */
+
+    /* The following fields are specified for custom actions */
+    wchar_t * caption;        /*!< Caption (localized) (limited by
+                                  KHUI_MAXCCH_SHORT_DESC).  The
+                                  caption is used for representing the
+                                  action in menus and toolbars. */
+    wchar_t * tooltip;        /*!< Tooltip (localized) (limited by
+                                  KHUI_MAXCCH_SHORT_DESC).  If this is
+                                  specified, whenever the user hovers
+                                  over the menu item or toolbar button
+                                  representing the action, the tooltip
+                                  will be displayed either on a
+                                  tooltip window or in the status
+                                  bar. */
+    khm_handle listener;      /*!< Listener of this action.  Should be
+                                  a handle to a message
+                                  subscription. When the action is
+                                  invoked, a message of type
+                                  ::KMSG_ACT and subtype
+                                  ::KMSG_ACT_ACTIVATE will be posted
+                                  to this subscriber. The \a uparam
+                                  parameter of the message will have
+                                  the identifier of the action. */
+    void *    data;           /*!< User data for custom action.  This
+                                  field is not used by the UI library.
+                                  It is reserved for plugins to store
+                                  data that is specific for this
+                                  action.  The data that's passed in
+                                  in the \a userdata parameter to
+                                  khui_action_create() will be stored
+                                  here and can be retrieved by calling
+                                  khui_action_get_data(). */
+    void *    reserved1;      /*!< Reserved. */
+    void *    reserved2;      /*!< Reserved. */
+    void *    reserved3;      /*!< Reserved. */
+
+    /* For all actions */
+    int state;                /*!< current state. combination of
+                                  KHUI_ACTIONSTATE_* */
+} khui_action;
+
+/*@}*/
+
+#endif
index 92fa7b0fa692e15f67a75bafa924b1c39f5ac98e..8182682c484ad298800216e4b7a9a3cb52ad9421 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_ACTION_H\r
-#define __KHIMAIRA_ACTION_H\r
-\r
-/*! \addtogroup khui\r
-  @{*/\r
-/*! \defgroup khui_actions Actions\r
-  @{*/\r
-\r
-struct tag_khui_action;\r
-typedef struct tag_khui_action khui_action;\r
-\r
-/*! \brief Unknown action type\r
-\r
-    Unknown action type.\r
- */\r
-#define KHUI_ACTIONTYPE_NONE    0\r
-\r
-/*! \brief A trigger type action\r
-\r
-    A trigger action usually triggers some event, which is what pretty\r
-    much every action does.\r
-*/\r
-#define KHUI_ACTIONTYPE_TRIGGER 1\r
-\r
-/*! \brief A toggle type action\r
-\r
-    A toggle type action typically changes the CHECKED state of the\r
-    action each time it is invoked.\r
- */\r
-#define KHUI_ACTIONTYPE_TOGGLE  2\r
-\r
-/*! \brief The action is enabled\r
-\r
-    This is the default if no other state is specified.  Just means\r
-    not-disabled.\r
-*/\r
-#define KHUI_ACTIONSTATE_ENABLED    0\r
-\r
-/*! \brief The action is diabled */\r
-#define KHUI_ACTIONSTATE_DISABLED   1\r
-\r
-/*! \brief For toggle type actions, the action is checked */\r
-#define KHUI_ACTIONSTATE_CHECKED    2\r
-\r
-/*! \brief The action is hot\r
-\r
-    Typically this means that the user is hovering the pointing device\r
-    over a UI element representing the action.\r
- */\r
-#define KHUI_ACTIONSTATE_HOT        4\r
-\r
-/*! \brief The action has been marked for deletion\r
-\r
-    For custom actions, this means that the custom action was deleted.\r
-    The contents of the custom action fields are no longer valid.\r
- */\r
-#define KHUI_ACTIONSTATE_DELETED    8\r
-\r
-#ifdef NOEXPORT\r
-#define ACTION_SIMPLE(c,cap,des,top) \\r
-    {c,KHUI_ACTIONTYPE_TRIGGER,NULL,0,0,0,0,0,cap,des,top,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0}\r
-\r
-#define ACTION_FULL(cmd,type,name,inormal,ihot,idis,isml,ismld,capt,toolt,topic,state) \\r
-    {cmd,type,name,inormal,ihot,idis,isml,ismld,capt,toolt,topic,NULL,NULL,NULL,NULL,NULL,NULL,NULL,state}\r
-\r
-#define ACTION_SIMPLE_IMAGE(c,inormal, ihot, idis, isml, ismld,cap, des, top) \\r
-    {c,KHUI_ACTIONTYPE_TRIGGER,NULL,inormal,ihot,idis,isml,ismld,cap,des,top,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0}\r
-#endif\r
-\r
-/*! \brief A reference to an action\r
-\r
-    If the \a flags member has the KHUI_ACTIONREF_PACTION bit set,\r
-    then the action is referenced by the \a p_action member of the\r
-    union.  Otherwise the identifier for the action is specified by \a\r
-    action member.\r
-*/\r
-typedef struct tag_khui_action_ref {\r
-    int flags;                  /*!< A combination of KHUI_ACTIONREF_* */\r
-    union {\r
-        khm_int32     action;   /*!< The action identifier for the\r
-                                  action that is being referrred to.\r
-                                  Only valid if\r
-                                  ::KHUI_ACTIONREF_PACTION is not set\r
-                                  in \a flags. */\r
-        khui_action * p_action; /*!< A pointer to the ::khui_action\r
-                                  structure that describes the action\r
-                                  that is being referred to.  Only\r
-                                  valid if ::KHUI_ACTIONREF_PACTION is\r
-                                  set. */\r
-    };\r
-} khui_action_ref;\r
-\r
-/*! \brief A submenu\r
-\r
-    There should exist a menu associated with the action that is being\r
-    referred.  When displaying this action in a menu, the contents of\r
-    the associated menu will appear as a submenu.\r
- */\r
-#define KHUI_ACTIONREF_SUBMENU      0x01\r
-\r
-/*! \brief Separator\r
-\r
-    This is not an actual action, but represents a separator between\r
-    actions.  When displaying this action in a menu or a toolbar, a\r
-    separating line will be drawn in place of this action.  The \a\r
-    action and \a p_action members of the structures are unused if\r
-    this flag is set.\r
- */\r
-#define KHUI_ACTIONREF_SEP          0x02\r
-\r
-/*! \brief Action by reference\r
-\r
-    The \a p_action member of the structure points to the\r
-    ::khui_action structure that describes the action.\r
- */\r
-#define KHUI_ACTIONREF_PACTION      0x04\r
-\r
-#ifdef NOEXPORT\r
-/*! \brief Action should be freed\r
-\r
-    \note This flag is reserved for internal use in the NetIDMgr\r
-    application.  Do not use.\r
- */\r
-#define KHUI_ACTIONREF_FREE_PACTION 0x08\r
-\r
-/*! \brief Marks the end of an action sequence\r
-\r
-    \note THis flag is reserved for internal use in the NetIDMgr\r
-    application. Do not use.\r
- */\r
-#define KHUI_ACTIONREF_END          0x10\r
-#endif\r
-\r
-/*! \brief The default action\r
-\r
-    When this bit is set in an action reference that describes a menu,\r
-    the menu item will be the default item and will be rendered\r
-    differently from other menu items.  Only useful when defining\r
-    context menus.  In general, it is good practice to place the\r
-    default item at the top of a menu, although the UI library does\r
-    not enforce this.  This is purely meant as a rendering hint.\r
-\r
-    Only one action is allowed to have this flag set.  When an action\r
-    is added to a menu using khui_menu_insert_action() or\r
-    khui_menu_insert_paction() and this flag is set, all other menu\r
-    items will be stripped of this flag.\r
- */\r
-#define KHUI_ACTIONREF_DEFAULT      0x20\r
-\r
-#ifdef NOEXPORT\r
-#define MENU_ACTION(c) {0,c}\r
-#define MENU_DEFACTION(c) {KHUI_ACTIONREF_DEFAULT, c}\r
-#define MENU_SUBMENU(s) {KHUI_ACTIONREF_SUBMENU,s}\r
-#define MENU_SEP() {KHUI_ACTIONREF_SEP,KHUI_MENU_SEP}\r
-#define MENU_END() {KHUI_ACTIONREF_END,KHUI_MENU_END}\r
-#endif\r
-\r
-/*! \brief Menu definition\r
-\r
-    Use the khui_menu_create(), khui_menu_insert_action(),\r
-    khui_menu_insert_paction(), khui_menu_get_size(),\r
-    khui_menu_get_action() functions to create and manipulate custom\r
-    menus.  Do not manipulate this structure directly as doing so may\r
-    cause inconsistencies in the UI library.\r
-*/\r
-typedef struct tag_khui_menu_def {\r
-    khm_int32 cmd;          /*!< Action associated with menu */\r
-    khm_int32 state;        /*!< combination of KHUI_MENUSTATE_* */\r
-    khm_size  n_items;      /*!< The number of actions in the \a items\r
-                              list.  If this is a custom menu, the\r
-                              ::KHUI_MENUSTATE_ALLOCD bit will be set,\r
-                              and the contents of this field will be\r
-                              valid.  Otherwise, the contents of this\r
-                              field is ignored and the list of actions\r
-                              must be terminated with a\r
-                              ACTION_LIST_END action. */\r
-    khm_size  nc_items;     /*!< max number of items in the buffer\r
-                             alocated for items.  Ignored if\r
-                             ::KHUI_MENUSTATE_ALLOCD is not set in \a\r
-                             state. */\r
-    khui_action_ref *items; /*!< Action list terminated by,\r
-                             ACTION_LIST_END.  If \a n_items is set\r
-                             to a value other than -1, the list\r
-                             doesn't necessarily have to end with a\r
-                             ACTION_LIST_END.  When constructing a\r
-                             menu using khui_menu_* functions, they\r
-                             will set the size of this list in the \a\r
-                             n_items member, and there will be no\r
-                             ACTION_LIST_END action to terminate the\r
-                             list. */\r
-} khui_menu_def;\r
-\r
-#ifdef NOEXPORT\r
-#define CONSTMENU(c,s,i) {c,s,-1,-1,i}\r
-#endif\r
-\r
-/*! \brief Unspecified menu\r
-\r
-    Used when there is no single command associated with the entire\r
-    menu, such as for ad-hoc context menus.\r
- */\r
-#define KHUI_MENU_NONE -3\r
-\r
-/*! \brief Menu end indicator\r
-\r
-    For static or constant menus this indicates that this action marks\r
-    the end of the list of actions which defined the menu.  This is\r
-    invalid if used in a dynamic menu (a menu with the\r
-    ::KHUI_MENUSTATE_ALLOCD bit set).\r
- */\r
-#define KHUI_MENU_END -2\r
-\r
-/*! \brief Menu separator\r
-\r
-    A separator for actions.  When displaying a menu or showing a\r
-    toolbar based on a menu definition, a separator is rendered as a\r
-    bar separating the user interface elements for the actions on\r
-    either side of this.\r
-*/\r
-#define KHUI_MENU_SEP -1\r
-\r
-/*! \brief Constant menu\r
-\r
-    The contents of the menu cannot be modified (individual actions in\r
-    the menu may be modified, but the order and the contents of the\r
-    menu itself cannot be modified.\r
-\r
-    This is the default if ::KHUI_MENUSTATE_ALLOCD is not specified.\r
- */\r
-#define KHUI_MENUSTATE_CONSTANT 0\r
-\r
-/*! \brief Variable menu\r
-\r
-    The menu is dnamically allocated.  The list of actions contained\r
-    in the menu can be modified.\r
-*/\r
-#define KHUI_MENUSTATE_ALLOCD   1\r
-\r
-#ifdef NOEXPORT\r
-/* predefined system menu */\r
-#define KHUI_MENUSTATE_SYSTEM   2\r
-#endif\r
-\r
-#ifdef NOEXPORT\r
-\r
-/*! \brief Accelerator definition */\r
-typedef struct tag_khui_accel_def {\r
-    int cmd;\r
-    int mod;\r
-    int key;\r
-    int scope;\r
-} khui_accel_def;\r
-\r
-#define KHUI_ACCEL_SCOPE_GLOBAL 0\r
-\r
-extern khui_accel_def khui_accel_global[];\r
-extern int khui_n_accel_global;\r
-\r
-extern khui_action khui_actions[];\r
-extern int khui_n_actions;\r
-\r
-extern khui_menu_def khui_all_menus[];\r
-extern int khui_n_all_menus;\r
-\r
-#endif /* NOEXPORT */\r
-\r
-/* functions */\r
-\r
-/*! \brief Refresh the global action table\r
-\r
-    Changes to system menus and toolbars may not be immediately\r
-    reflected in the user interface.  Calling this function forces the\r
-    UI to reparse the action tables and menus and refresh the\r
-    application menu bar and toolbars.\r
-\r
- */\r
-KHMEXP void KHMAPI\r
-khui_refresh_actions(void);\r
-\r
-/*! \brief Lock the action and menu tables\r
-\r
-    This function, along with khui_action_unlock() is used to prevent\r
-    changes from being made to shared menus and actions while they are\r
-    being updated.  In particular, changes to shared menus usually\r
-    need to be done in a batch and may suffer corruption of other\r
-    threads access or modify the menu while one thread is updating it.\r
-    Operations on shared menus should always be done with the actions\r
-    locked.\r
-*/\r
-KHMEXP void KHMAPI\r
-khui_action_lock(void);\r
-\r
-/*! \brief Unlock the action and menu tables\r
-\r
-    Unlocks the action and menu tables after a call to\r
-    khui_action_lock().\r
-\r
-    \see khui_action_lock()\r
- */\r
-KHMEXP void KHMAPI\r
-khui_action_unlock(void);\r
-\r
-/*! \brief Create a new menu\r
-\r
-    Creates a new menu.  The returned data structure must be freed by\r
-    a call to khui_menu_delete().  Custom menus that are created this\r
-    way are not reference counted or maintained by the UI library.\r
-    The caller is responsible for calling khui_menu_delete() when the\r
-    data is no longer needed.\r
-\r
-    Specifiying an action in the \a action parameter will associate\r
-    the menu with the specified action.  In this case, if the action\r
-    is added to another menu with the ::KHUI_ACTIONREF_SUBMENU flag,\r
-    this menu will appear as a submenu within that menu.  Only one\r
-    menu can be associated with any given action.  Custom menus can\r
-    not be associated with standard actions.\r
- */\r
-KHMEXP khui_menu_def * KHMAPI\r
-khui_menu_create(khm_int32 action);\r
-\r
-/*! \brief Duplicate a menu\r
-\r
-    Creates a copy of the specified menu.  The returned data structure\r
-    must be freed by a call to khui_menu_delete().  Custom menus are\r
-    not reference counted or maintained by the UI library.  The caller\r
-    is responsible for calling khui_menu_delete() when the data is no\r
-    longer needed.\r
-\r
-    Note that even if the original menu was associated with an action,\r
-    the duplicate will not be.  Modifying the duplicate will not\r
-    modify the original menu.  Only one menu can be associated with an\r
-    action.\r
- */\r
-KHMEXP khui_menu_def * KHMAPI\r
-khui_menu_dup(khui_menu_def * src);\r
-\r
-/*! \brief Delete a menu\r
-\r
-    Deletes a menu created by a call to khui_menu_create() or\r
-    khui_menu_dup().  This frees up the memory and associated\r
-    resources used by the menu definition.  The pointer that is passed\r
-    in will no longer be valid.\r
- */\r
-KHMEXP void KHMAPI\r
-khui_menu_delete(khui_menu_def * d);\r
-\r
-/*! \brief Insert an action into a menu\r
-\r
-    The action specified by \a cmd will be inserted in to the menu \a\r
-    d at index \a idx.\r
-\r
-    \param[in] d The menu to insert the action into\r
-\r
-    \param[in] idx The index at which to insert the action.  The index\r
-        is zero based.  If \a idx is (-1) or larger than the largest\r
-        index in the menu, the item is appended to the menu.\r
-\r
-    \param[in] cmd The command representing the action to insert into\r
-        the menu.  This should be either a standard action, a user\r
-        action created with khui_action_create(), or certain pseudo\r
-        actions.  Not all pseudo actions can be placed on a menu.\r
-\r
-    \param[in] flags Flags for the action.  This is a combination of\r
-        KHUI_ACTIONREF_* constants.  Currently, the only constants\r
-        that are valid for this function are: ::KHUI_ACTIONREF_SEP,\r
-        ::KHUI_ACTIONREF_SUBMENU, ::KHUI_ACTIONREF_DEFAULT.\r
-        ::KHUI_ACTIONREF_SEP will be automatically added if the\r
-        command is ::KHUI_MENU_SEP.  If ::KHUI_ACTIONREF_DEFAULT is\r
-        specified, then all other items in the menu will be stripped\r
-        of that flag leaving this action as the only one with that\r
-        flag set.\r
- */\r
-KHMEXP void KHMAPI\r
-khui_menu_insert_action(khui_menu_def * d, khm_size idx, khm_int32 cmd, khm_int32 flags);\r
-\r
-#define khui_menu_add_action(d,c) khui_menu_insert_action((d),-1,(c),0)\r
-#pragma deprecated(khui_menu_add_action)\r
-\r
-#ifdef NOEXPORT\r
-\r
-/*! \brief Insert an action by reference into a menu\r
-\r
-    The action specified by \a act will be inserted into the menu \a d\r
-    at index \a idx.\r
-\r
-    \param[in] d The menu to inser the action into.\r
-\r
-    \param[in] idx The index at which to insert the action.  The index\r
-        is zero based.  If the index is (-1) or is larger than the\r
-        largest index in the menu, then the action is appended to the\r
-        menu.\r
-\r
-    \param[in] act The action to insert.  This is added by reference.\r
-        It is the callers reponsibility to ensure that the structure\r
-        pointed to by \a act is available throughout the lifetime of\r
-        the menu.\r
-\r
-    \param[in] flags Flags for the action.  This is a combination of\r
-        KHUI_ACTIONREF_* constants.  Currently, the only constants\r
-        that are valid for this function are: ::KHUI_ACTIONREF_SEP,\r
-        ::KHUI_ACTIONREF_SUBMENU, ::KHUI_ACTIONREF_DEFAULT.  For this\r
-        function, ::KHUI_ACTIONREF_PACTION will automatically be aded\r
-        when adding the action.  ::KHUI_ACTIONREF_SEP will be\r
-        automatically added if the command is ::KHUI_MENU_SEP.  If\r
-        ::KHUI_ACTIONREF_DEFAULT is specified, then all other items in\r
-        the menu will be stripped of that flag leaving this action as\r
-        the only one with that flag set.\r
-*/\r
-KHMEXP void KHMAPI\r
-khui_menu_insert_paction(khui_menu_def * d, khm_size idx, khui_action * act, khm_int32 flags);\r
-\r
-#define khui_menu_add_paction(d,a,f) khui_menu_insert_paction((d),-1,(a),(f))\r
-#pragma deprecated(khui_menu_add_paction)\r
-\r
-#endif\r
-\r
-/*! \brief Remove an action from a menu\r
-\r
-    The action at the specified index will be removed from the menu.\r
-  */\r
-KHMEXP void KHMAPI\r
-khui_menu_remove_action(khui_menu_def * d, khm_size idx);\r
-\r
-/*! \brief Get the number of items in the menu\r
-\r
-    Note that the count includes menu separators.  The indices of the\r
-    menu items range from 0 to one less than the value returned by\r
-    this function.\r
- */\r
-KHMEXP khm_size KHMAPI\r
-khui_menu_get_size(khui_menu_def * d);\r
-\r
-/*! \brief Get the menu item at a specified index\r
-\r
-    The returned reference is only valid while the ::khui_menu_def\r
-    structure is valid.  In addition, the reference becomes invalid if\r
-    the list of actions in the menu data structure is modified in any\r
-    way.\r
-\r
-    If the specified index is out of bounds, then the function returns\r
-    NULL.\r
-\r
- */\r
-KHMEXP khui_action_ref *\r
-khui_menu_get_action(khui_menu_def * d, khm_size idx);\r
-\r
-/*! \brief Action scope identifiers \r
-\r
-    The scope identifier is a value which describes the scope of the\r
-    cursor context.  See documentation on individual scope identifiers\r
-    for details.\r
-\r
-    The role of the scope identifier is to provide a summary of the\r
-    current cursor context.  Specifically, these identify several\r
-    special cases of credential selection, such as the selection of an\r
-    entire identity, a credential type or a single credential.  If\r
-    none of these are applicable, then the generic scope identifier\r
-    ::KHUI_SCOPE_GROUP is set or ::KHUI_SCOPE_NONE if there is nothing\r
-    selected.\r
-\r
-    Note that the scope typically only apply to cursor contexts and\r
-    not the selection context.  Please see \r
-    \ref khui_context "UI Contexts" for more information.\r
-\r
-    \see \ref khui_context "UI Contexts"\r
-*/\r
-typedef enum tag_khui_scope {\r
-    KHUI_SCOPE_NONE,\r
-    /*!< No context.  Nothing is selected. */\r
-\r
-    KHUI_SCOPE_IDENT,     \r
-    /*!< Identity.  The selection is the entire identity specified in\r
-      the \a identity field of the context. */\r
-\r
-    KHUI_SCOPE_CREDTYPE,  \r
-    /*!< A credentials type.  The selection is an entire credentials\r
-      type.  If \a identity is non-NULL, then the scope is all the\r
-      credentials of type \a cred_type which belong to \a identity.\r
-      Otherwise, the selection is all credentials of type \a\r
-      cred_type.\r
-\r
-      \note The \a identity can be non-NULL even for the case where\r
-      all credentials of type \a cred_type under \a identity is the\r
-      same scope as all credentials of type \a cred_type under all\r
-      identities. */\r
-\r
-    KHUI_SCOPE_GROUP,\r
-    /*!< A grouping of credentials.  The scope is a group of\r
-      credentials which can not be simplified using one of the other\r
-      context identifiers.  The \a headers array contains \a n_headers\r
-      elements describing the outline level that has been selected.\r
-\r
-      \see ::khui_header\r
-      \see \ref khui_context_sel_ctx_grp "KHUI_SCOPE_GROUP description" */\r
-\r
-    KHUI_SCOPE_CRED\r
-    /*!< A single credential.  Only a single credential was\r
-      selected. The \a cred field of the context specifies the\r
-      credential.  The \a identity and \a cred_type fields specify the\r
-      identity and the credential type respectively. */\r
-} khui_scope;\r
-\r
-\r
-/*! \brief Outline header\r
-\r
-    Describes an outline header in the user interface.\r
-\r
-    \see \ref khui_context_sel_ctx_grp "KHUI_SCOPE_GROUP description"\r
- */\r
-typedef struct tag_khui_header {\r
-    khm_int32 attr_id;          /*!< Attribute ID */\r
-    void *    data;             /*!< Value of attribute */\r
-    khm_size  cb_data;          /*!< Size of the value */\r
-} khui_header;\r
-\r
-/*! \brief Maximum number of outline headers\r
-\r
-    This is the maximum number of fields that the credentials view can\r
-    be grouped by.\r
- */\r
-#define KHUI_MAX_HEADERS  6\r
-\r
-/*! \brief Action context\r
-\r
-    Represents the UI context for an action.\r
- */\r
-typedef struct tag_khui_action_context {\r
-    khm_int32   magic;          /*!< Internal. */\r
-    khui_scope  scope;          /*!< Context scope.  One of ::khui_scope*/\r
-    khm_handle  identity;       /*!< Identity */\r
-    khm_int32   cred_type;      /*!< Credential type ID */\r
-    khm_handle  cred;           /*!< Credential */\r
-\r
-    khui_header headers[KHUI_MAX_HEADERS];\r
-                                /*!< The ordered set of outline\r
-                                  headers which define the current\r
-                                  cursor location. */\r
-\r
-    khm_size    n_headers;      /*!< Number of actual headers defined\r
-                                  above */\r
-\r
-    khm_handle  credset;        /*!< Handle to a credential set\r
-                                  containing the currently selected\r
-                                  credentials.  When the context is\r
-                                  obtained through khui_context_get(),\r
-                                  this credential is returned in a\r
-                                  sealed state. */\r
-\r
-    khm_size    n_sel_creds;    /*!< Number of selected credentials */\r
-\r
-    void *      int_buf;        /*!< Internal.  Do not use. */\r
-    khm_size    int_cb_buf;     /*!< Internal.  Do not use. */\r
-    khm_size    int_cb_used;    /*!< Internal.  Do not use. */\r
-\r
-    void *      vparam;         /*!< Optional data */\r
-    khm_size    cb_vparam;      /*!< Size of optional data */\r
-} khui_action_context;\r
-\r
-/*! \brief Set the current context\r
-\r
-    Changes the UI context to that represented by the parameters to\r
-    the function.  Note that specifying a valid \a identity or \a cred\r
-    parameter will result in an automatic hold on the respective\r
-    object.  The hold will stay until another call to\r
-    khui_context_set() overwrites the identity or credential handle or\r
-    a call to khui_context_reset() is made.\r
-\r
-    While this API is available, it is only called from the main\r
-    NetIDMgr application.  Plugins do not have a good reason to call\r
-    this API directly and should not do so.\r
-\r
-    \param[in] scope The new context scope\r
-\r
-    \param[in] identity A handle to an identity.  If this is not NULL,\r
-        then it should be a valid handle to an identity.  Required if\r
-        \a scope specifies ::KHUI_SCOPE_IDENT.  Optional if \a scope\r
-        specifies ::KHUI_SCOPE_CREDTYPE.  Ignored otherwise.\r
-\r
-    \param[in] cred_type A credentials type.  Specify\r
-        ::KCDB_CREDTYPE_INVALID if this parameter is not given or not\r
-        relevant.  Required if \a scope specifies\r
-        ::KHUI_SCOPE_CREDTYPE.  Ignored otherwise.\r
-\r
-    \param[in] cred A handle to a credential.  If this parameter is\r
-        not NULL it is expected to be a valid handle to a credential.\r
-        Required if \a scope specifies ::KHUI_SCOPE_CRED.  Ignored\r
-        otherwise.\r
-\r
-    \param[in] headers An array of headers.  The \a n_headers\r
-        parameter specifies the number of elements in the array.  Set\r
-        to NULL if not specified.  Required if \a scope specifies\r
-        ::KHUI_SCOPE_GROUP.\r
-\r
-    \param[in] n_headers Number of elements in \a headers.  Must be\r
-        less than or equal to ::KHUI_MAX_HEADERS.  Required if \a\r
-        headers is not NULL. Ignored otherwise.\r
-\r
-    \param[in] cs_src A handle to a credential set from which the\r
-        selected credentials will be extracted.  The credentials that\r
-        are selected must have the ::KCDB_CRED_FLAG_SELECTED flag set.\r
-\r
-    \note This function should only be called from the UI thread.\r
- */\r
-KHMEXP void KHMAPI \r
-khui_context_set(khui_scope  scope, \r
-                 khm_handle  identity, \r
-                 khm_int32   cred_type, \r
-                 khm_handle  cred,\r
-                 khui_header *headers,\r
-                 khm_size    n_headers,\r
-                 khm_handle  cs_src);\r
-\r
-/*! \brief Set the current context\r
-\r
-    Changes the UI context to that represented by the parameters to\r
-    the function.  Note that specifying a valid \a identity or \a cred\r
-    parameter will result in an automatic hold on the respective\r
-    object.  The hold will stay until another call to\r
-    khui_context_set() overwrites the identity or credential handle or\r
-    a call to khui_context_reset() is made.\r
-\r
-    While this API is available, it is only called from the main\r
-    NetIDMgr application.  Plugins do not have a good reason to call\r
-    this API directly and should not do so.\r
-\r
-    \param[in] scope The new context scope\r
-\r
-    \param[in] identity A handle to an identity.  If this is not NULL,\r
-        then it should be a valid handle to an identity.  Required if\r
-        \a scope specifies ::KHUI_SCOPE_IDENT.  Optional if \a scope\r
-        specifies ::KHUI_SCOPE_CREDTYPE.  Ignored otherwise.\r
-\r
-    \param[in] cred_type A credentials type.  Specify\r
-        ::KCDB_CREDTYPE_INVALID if this parameter is not given or not\r
-        relevant.  Required if \a scope specifies\r
-        ::KHUI_SCOPE_CREDTYPE.  Ignored otherwise.\r
-\r
-    \param[in] cred A handle to a credential.  If this parameter is\r
-        not NULL it is expected to be a valid handle to a credential.\r
-        Required if \a scope specifies ::KHUI_SCOPE_CRED.  Ignored\r
-        otherwise.\r
-\r
-    \param[in] headers An array of headers.  The \a n_headers\r
-        parameter specifies the number of elements in the array.  Set\r
-        to NULL if not specified.  Required if \a scope specifies\r
-        ::KHUI_SCOPE_GROUP.\r
-\r
-    \param[in] n_headers Number of elements in \a headers.  Must be\r
-        less than or equal to ::KHUI_MAX_HEADERS.  Required if \a\r
-        headers is not NULL. Ignored otherwise.\r
-\r
-    \param[in] cs_src A handle to a credential set from which the\r
-        selected credentials will be extracted.  The credentials that\r
-        are selected must have the ::KCDB_CRED_FLAG_SELECTED flag set.\r
-\r
-    \param[in] vparam Optional parameter blob\r
-\r
-    \param[in] cb_vparam Size of parameter blob\r
-\r
-    \note This function should only be called from the UI thread.\r
- */\r
-KHMEXP void KHMAPI \r
-khui_context_set_ex(khui_scope scope, \r
-                    khm_handle identity, \r
-                    khm_int32 cred_type, \r
-                    khm_handle cred,\r
-                    khui_header *headers,\r
-                    khm_size n_headers,\r
-                    khm_handle cs_src,\r
-                    void * vparam,\r
-                    khm_size cb_vparam);\r
-\r
-/*! \brief Set the current UI context using an existing context\r
-\r
-    Copies the context specified in \a ctx into the active UI context.\r
-\r
-    \param[in] ctx A pointer to a ::khui_action_context structure that\r
-        specifies the new UI context.  Cannot be NULL.\r
-*/\r
-KHMEXP void KHMAPI\r
-khui_context_set_indirect(khui_action_context * ctx);\r
-\r
-/*! \brief Obtain the current UI context\r
-\r
-    The parameter specified by \a ctx will receive the current UI\r
-    context.  If the context contains an identity or a credential\r
-    handle, a hold will be obtained on the relevant object.  Use\r
-    khui_context_release() to release the holds obtained in a prior\r
-    call to khui_context_get().\r
-\r
-    \note The returned context should not be modified prior to calling\r
-    khui_context_release().\r
-*/\r
-KHMEXP void KHMAPI \r
-khui_context_get(khui_action_context * ctx);\r
-\r
-/*! \brief Create a new UI context\r
-\r
-    The created context does not have any relation to the current UI\r
-    context.  This function is provided for use in situations where an\r
-    application needs to provide a scope description through a\r
-    ::khui_action_context structure.\r
-\r
-    Once the application is done with the context, it should call\r
-    khui_context_release() to release the created context.\r
- */\r
-KHMEXP void KHMAPI\r
-khui_context_create(khui_action_context * ctx,\r
-                    khui_scope scope,\r
-                    khm_handle identity,\r
-                    khm_int32 cred_type,\r
-                    khm_handle cred);\r
-\r
-/*! \brief Release a context obtained using khui_context_get()\r
-\r
-    Releases all holds obtained on related objects in a prior call to\r
-    khui_context_get() and nullifies the context.\r
-\r
-    \note The context should not have been modified between calling\r
-    khui_context_get() and khui_context_release()\r
- */\r
-KHMEXP void KHMAPI \r
-khui_context_release(khui_action_context * ctx);\r
-\r
-/*! \brief Reset the UI context\r
-\r
-    Nullifies the current UI context and releases any holds obtained\r
-    on objects related to the previous context.\r
-*/\r
-KHMEXP void KHMAPI \r
-khui_context_reset(void);\r
-\r
-/*! \brief Refresh context data\r
-\r
-    Setting the UI context involves other side effects such as\r
-    activation of or disabling certain actions based on the selection.\r
-    If an operation is performed which may affect the side effects,\r
-    khui_context_refresh() is called to refresh them.\r
-\r
-    An example is when setting the default identity.  The state of the\r
-    action ::KHUI_ACTION_SET_DEF_ID depends on whether the currently\r
-    selected identity is the default.  However, if the currently\r
-    selected identity becomes the default after selection, then\r
-    khui_context_refresh() should be called to adjust the state of the\r
-    ::KHUI_ACTION_SET_DEF_ID action.\r
- */\r
-KHMEXP void KHMAPI \r
-khui_context_refresh(void);\r
-\r
-/*! \brief A filter function that filters for credentials in the cursor context\r
-\r
-    This is a function of type ::kcdb_cred_filter_func which can be\r
-    used to filter for credentials that are included in the cursor\r
-    context.\r
-\r
-    The \a rock parameter should be a pointer to a\r
-    ::khui_action_context structure which will be used as the filter.\r
-\r
-    For example, the following code will extract the cursor context\r
-    credentials into the credential set \a my_credset based on the UI\r
-    context \a my context:\r
-\r
-    \code\r
-    kcdb_credset_extract_filtered(my_credset,\r
-                                  NULL,\r
-                                  khui_context_cursor_filter,\r
-                                  (void *) my_context);\r
-    \endcode\r
-*/\r
-KHMEXP khm_int32 KHMAPI\r
-khui_context_cursor_filter(khm_handle cred,\r
-                           khm_int32 flags,\r
-                           void * rock);\r
-\r
-/*! \brief Get a string representation of an accelerator\r
-\r
-    \param[in] cmd Command for which to obtain the accelerator string for\r
-    \param[out] buf Buffer to receive the accelerator string\r
-    \param[in] bufsiz Size of the buffer in bytes.  Note that the size of the\r
-        buffer must be sufficient to hold at least one character and a\r
-        NULL terminator.\r
-\r
-    \return TRUE if the operation was successful. FALSE otherwise.\r
- */\r
-KHMEXP khm_boolean KHMAPI khui_get_cmd_accel_string(khm_int32 cmd, wchar_t * buf, khm_size bufsiz);\r
-\r
-#ifdef NOEXPORT\r
-/*! \brief Initializes the global accelerator table\r
- */\r
-KHMEXP HACCEL KHMAPI khui_create_global_accel_table(void);\r
-#endif\r
-\r
-/*! \brief Find a menu by id\r
-\r
-    Finds the menu that is associated with the specified action.\r
- */\r
-KHMEXP khui_menu_def * KHMAPI khui_find_menu(khm_int32 action);\r
-\r
-#ifdef NOEXPORT\r
-\r
-/* internal */\r
-KHMEXP void KHMAPI\r
-khui_set_main_window(HWND hwnd);\r
-\r
-#endif\r
-\r
-/*! \brief Trigger an action\r
-\r
-    Triggers the specified action using the specified UI context.\r
-\r
-    This function does not return until the specified action has been\r
-    processed.  Many standard actions are asynchronous and they will\r
-    return before processing will complete.\r
-\r
-    Pseudo actions should not be triggered using khui_action_trigger()\r
-    as they only carry meaning when invoked from specific windows or\r
-    contexts.\r
-\r
-    \param[in] action Action.  Should be one of the standard actions\r
-        or an action created by khui_action_create()\r
-\r
-    \param[in] ctx The UI context to use for the action.  If this is\r
-        NULL, the action will be triggered under the current UI context.\r
- */\r
-KHMEXP void KHMAPI\r
-khui_action_trigger(khm_int32 action, khui_action_context * ctx);\r
-\r
-/*! \brief Find an action by id\r
-\r
-    \note This function should not be used by plugins.  It is there\r
-        for use by the NetIDMgr application.\r
-*/\r
-KHMEXP khui_action * KHMAPI khui_find_action(khm_int32 action);\r
-\r
-#ifdef NOEXPORT\r
-/*! \brief Get the length of the action list */\r
-KHMEXP size_t KHMAPI khui_action_list_length(khui_action_ref * ref);\r
-#endif\r
-\r
-/*! \brief Create a new action\r
-\r
-    Creates a new custom action.  The created custom action can be\r
-    added to menus, toolbars and can be triggered by\r
-    khui_action_trigger().\r
-\r
-    When the action is triggered as a result of the user selecting a\r
-    menu item, a toolbar item or as a result of calling\r
-    khui_action_trigger(), the subscription identified by \a hsub will\r
-    received a message of type ::KMSG_ACT, subtype\r
-    ::KMSG_ACT_ACTIVATE.  The \a uparam for the message will be the\r
-    action identifier that was returned by khui_action_create().  The\r
-    \a vparam of the message will currently be set to \a NULL.\r
-\r
-    Actions can optionally be named.  The name is not actively used by\r
-    the Network Identity Manager framework, but can be used to label\r
-    actions so that they can be looked up later using\r
-    khui_find_named_action().\r
-\r
-    \param[in] name Name for a named action.  The name must be unique\r
-        among all registered actions. (limited by KHUI_MAXCCH_NAME).\r
-        (Optional. Set to NULL if the action is not a named action.)\r
-        See \a note below for additional restrictions on the name of\r
-        the action.\r
-\r
-    \param[in] caption The localized caption for the action.  This\r
-        will be shown in menus, toolbars and buttons when the action\r
-        needs to be represented. (limited by KHUI_MAXCCH_SHORT_DESC)\r
-        (Required)\r
-\r
-    \param[in] tooltip The localized tooltip for the action. (limited\r
-        by KHUI_MAXCCH_SHORT_DESC) (Optional, set to NULL if there is\r
-        no tooltip associated with the action)\r
-\r
-    \param[in] userdata A custom value.\r
-\r
-    \param[in] type The type of the action.  Currently it should be\r
-        set to either ::KHUI_ACTIONTYPE_TRIGGER or\r
-        ::KHUI_ACTIONTYPE_TOGGLE.  For ::KHUI_ACTIONTYPE_TOGGLE, the\r
-        initial state will be unchecked.  Use khui_check_action()\r
-        function to change the checked state of the action.\r
-\r
-    \param[in] hsub The subscription that is notified when the action\r
-        is triggered. (Optional) The subscription must be created with\r
-        kmq_create_subscription().  The handle will be released when\r
-        it is no longer needed.  Hence, the caller should not release\r
-        it.\r
-\r
-    \return The identifier of the new action or zero if the action\r
-        could not be created.\r
-\r
-    \note For named custom actions, the name of the action can not be\r
-        the same as the name of a configuration node.  See\r
-        khui_cfg_register_node().\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_action_create(const wchar_t * name,\r
-                   const wchar_t * caption,\r
-                   const wchar_t * tooltip,\r
-                   void * userdata,\r
-                   khm_int32 type,\r
-                   khm_handle hsub);\r
-\r
-/* \brief Delete a custom action\r
-\r
-   Deletes a custom action created by a call to khui_action_create().\r
-   Custom actions should only be deleted when unloading a plugin.\r
- */\r
-KHMEXP void KHMAPI\r
-khui_action_delete(khm_int32 action);\r
-\r
-/*! \brief Get the user data associated with a custom action\r
-\r
-    This function returns the user data that was specified when the\r
-    custom action was created usng khui_action_create().  If the\r
-    custom action identifier is invalid or if the custom action does\r
-    not contain any user data, this function will return NULL.\r
- */\r
-KHMEXP void * KHMAPI\r
-khui_action_get_data(khm_int32 action);\r
-\r
-/*! \brief Find an action by name */\r
-KHMEXP khui_action * KHMAPI khui_find_named_action(const wchar_t * name);\r
-\r
-/*! \brief Enables or disables a group of actions\r
-\r
-    The group of actions are specified by the menu definition.  All\r
-    valid action entries in the menu are marked as enabled or disabled\r
-    according to the value of \a enable.\r
- */\r
-KHMEXP void KHMAPI khui_enable_actions(khui_menu_def * d, khm_boolean enable);\r
-\r
-/*! \brief Enables or disables an action\r
-\r
-    The action designated by the command \a action will either be enabled\r
-    or disabled depending on the \a enable parameter.  If \a enable is\r
-    TRUE then the action is enabled.\r
- */\r
-KHMEXP void KHMAPI khui_enable_action(khm_int32 action, khm_boolean enable);\r
-\r
-/*! \brief Check an action in an action group\r
-\r
-    Marks the action denoted by \a action as checked and resets the\r
-    checked bit in all other actions.\r
-\r
-    \param[in] d A menu definition.\r
-\r
-    \param[in] action A command identifier.  Setting this to -1 will\r
-        reset the checked bit in all the actions in the menu\r
-        definition.\r
- */\r
-KHMEXP void KHMAPI khui_check_radio_action(khui_menu_def * d, khm_int32 action);\r
-\r
-/*! \brief Check an action\r
-\r
-    For toggle typed actions, this sets or resets the check.\r
- */\r
-KHMEXP void KHMAPI khui_check_action(khm_int32 cmd, khm_boolean check);\r
-\r
-#ifdef NOEXPORT\r
-/*!\cond INTERNAL */\r
-\r
-/*! \brief Initialize actions\r
-\r
-    \note Only called by the NetIDMgr application\r
- */\r
-KHMEXP void KHMAPI khui_init_actions(void);\r
-\r
-/*! \brief Exit actions\r
-\r
-    \note Only called by the NetIDMgr application\r
- */\r
-KHMEXP void KHMAPI khui_exit_actions(void);\r
-\r
-/*! \endcond */\r
-#endif\r
-\r
-/*@}*/\r
-/*@}*/\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
+  @{*/
+
+struct tag_khui_action;
+typedef struct tag_khui_action khui_action;
+
+/*! \brief Unknown action type
+
+    Unknown action type.
+ */
+#define KHUI_ACTIONTYPE_NONE    0
+
+/*! \brief A trigger type action
+
+    A trigger action usually triggers some event, which is what pretty
+    much every action does.
+*/
+#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
+
+    This is the default if no other state is specified.  Just means
+    not-disabled.
+*/
+#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
+
+/*! \brief The action has been marked for deletion
+
+    For custom actions, this means that the custom action was deleted.
+    The contents of the custom action fields are no longer valid.
+ */
+#define KHUI_ACTIONSTATE_DELETED    8
+
+#ifdef NOEXPORT
+#define ACTION_SIMPLE(c,cap,des,top) \
+    {c,KHUI_ACTIONTYPE_TRIGGER,NULL,0,0,0,0,0,cap,des,top,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0}
+
+#define ACTION_FULL(cmd,type,name,inormal,ihot,idis,isml,ismld,capt,toolt,topic,state) \
+    {cmd,type,name,inormal,ihot,idis,isml,ismld,capt,toolt,topic,NULL,NULL,NULL,NULL,NULL,NULL,NULL,state}
+
+#define ACTION_SIMPLE_IMAGE(c,inormal, ihot, idis, isml, ismld,cap, des, top) \
+    {c,KHUI_ACTIONTYPE_TRIGGER,NULL,inormal,ihot,idis,isml,ismld,cap,des,top,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0}
+#endif
+
+/*! \brief A reference to an action
+
+    If the \a flags member has the KHUI_ACTIONREF_PACTION bit set,
+    then the action is referenced by the \a p_action member of the
+    union.  Otherwise the identifier for the action is specified by \a
+    action member.
+*/
+typedef struct tag_khui_action_ref {
+    int flags;                  /*!< A combination of KHUI_ACTIONREF_* */
+    union {
+        khm_int32     action;   /*!< The action identifier for the
+                                  action that is being referrred to.
+                                  Only valid if
+                                  ::KHUI_ACTIONREF_PACTION is not set
+                                  in \a flags. */
+        khui_action * p_action; /*!< A pointer to the ::khui_action
+                                  structure that describes the action
+                                  that is being referred to.  Only
+                                  valid if ::KHUI_ACTIONREF_PACTION is
+                                  set. */
+    };
+} khui_action_ref;
+
+/*! \brief A submenu
+
+    There should exist a menu associated with the action that is being
+    referred.  When displaying this action in a menu, the contents of
+    the associated menu will appear as a submenu.
+ */
+#define KHUI_ACTIONREF_SUBMENU      0x01
+
+/*! \brief Separator
+
+    This is not an actual action, but represents a separator between
+    actions.  When displaying this action in a menu or a toolbar, a
+    separating line will be drawn in place of this action.  The \a
+    action and \a p_action members of the structures are unused if
+    this flag is set.
+ */
+#define KHUI_ACTIONREF_SEP          0x02
+
+/*! \brief Action by reference
+
+    The \a p_action member of the structure points to the
+    ::khui_action structure that describes the action.
+ */
+#define KHUI_ACTIONREF_PACTION      0x04
+
+#ifdef NOEXPORT
+/*! \brief Action should be freed
+
+    \note This flag is reserved for internal use in the NetIDMgr
+    application.  Do not use.
+ */
+#define KHUI_ACTIONREF_FREE_PACTION 0x08
+
+/*! \brief Marks the end of an action sequence
+
+    \note THis flag is reserved for internal use in the NetIDMgr
+    application. Do not use.
+ */
+#define KHUI_ACTIONREF_END          0x10
+#endif
+
+/*! \brief The default action
+
+    When this bit is set in an action reference that describes a menu,
+    the menu item will be the default item and will be rendered
+    differently from other menu items.  Only useful when defining
+    context menus.  In general, it is good practice to place the
+    default item at the top of a menu, although the UI library does
+    not enforce this.  This is purely meant as a rendering hint.
+
+    Only one action is allowed to have this flag set.  When an action
+    is added to a menu using khui_menu_insert_action() or
+    khui_menu_insert_paction() and this flag is set, all other menu
+    items will be stripped of this flag.
+ */
+#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
+
+    Use the khui_menu_create(), khui_menu_insert_action(),
+    khui_menu_insert_paction(), khui_menu_get_size(),
+    khui_menu_get_action() functions to create and manipulate custom
+    menus.  Do not manipulate this structure directly as doing so may
+    cause inconsistencies in the UI library.
+*/
+typedef struct tag_khui_menu_def {
+    khm_int32 cmd;          /*!< Action associated with menu */
+    khm_int32 state;        /*!< combination of KHUI_MENUSTATE_* */
+    khm_size  n_items;      /*!< The number of actions in the \a items
+                              list.  If this is a custom menu, the
+                              ::KHUI_MENUSTATE_ALLOCD bit will be set,
+                              and the contents of this field will be
+                              valid.  Otherwise, the contents of this
+                              field is ignored and the list of actions
+                              must be terminated with a
+                              ACTION_LIST_END action. */
+    khm_size  nc_items;     /*!< max number of items in the buffer
+                             alocated for items.  Ignored if
+                             ::KHUI_MENUSTATE_ALLOCD is not 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.  When constructing a
+                             menu using khui_menu_* functions, they
+                             will set the size of this list in the \a
+                             n_items member, and there will be no
+                             ACTION_LIST_END action to terminate the
+                             list. */
+} khui_menu_def;
+
+#ifdef NOEXPORT
+#define CONSTMENU(c,s,i) {c,s,-1,-1,i}
+#endif
+
+/*! \brief Unspecified menu
+
+    Used when there is no single command associated with the entire
+    menu, such as for ad-hoc context menus.
+ */
+#define KHUI_MENU_NONE -3
+
+/*! \brief Menu end indicator
+
+    For static or constant menus this indicates that this action marks
+    the end of the list of actions which defined the menu.  This is
+    invalid if used in a dynamic menu (a menu with the
+    ::KHUI_MENUSTATE_ALLOCD bit set).
+ */
+#define KHUI_MENU_END -2
+
+/*! \brief Menu separator
+
+    A separator for actions.  When displaying a menu or showing a
+    toolbar based on a menu definition, a separator is rendered as a
+    bar separating the user interface elements for the actions on
+    either side of this.
+*/
+#define KHUI_MENU_SEP -1
+
+/*! \brief Constant menu
+
+    The contents of the menu cannot be modified (individual actions in
+    the menu may be modified, but the order and the contents of the
+    menu itself cannot be modified.
+
+    This is the default if ::KHUI_MENUSTATE_ALLOCD is not specified.
+ */
+#define KHUI_MENUSTATE_CONSTANT 0
+
+/*! \brief Variable menu
+
+    The menu is dnamically allocated.  The list of actions contained
+    in the menu can be modified.
+*/
+#define KHUI_MENUSTATE_ALLOCD   1
+
+#ifdef NOEXPORT
+/* predefined system menu */
+#define KHUI_MENUSTATE_SYSTEM   2
+#endif
+
+#ifdef NOEXPORT
+
+/*! \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
+
+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 */
+
+/*! \brief Refresh the global action table
+
+    Changes to system menus and toolbars may not be immediately
+    reflected in the user interface.  Calling this function forces the
+    UI to reparse the action tables and menus and refresh the
+    application menu bar and toolbars.
+
+ */
+KHMEXP void KHMAPI
+khui_refresh_actions(void);
+
+/*! \brief Lock the action and menu tables
+
+    This function, along with khui_action_unlock() is used to prevent
+    changes from being made to shared menus and actions while they are
+    being updated.  In particular, changes to shared menus usually
+    need to be done in a batch and may suffer corruption of other
+    threads access or modify the menu while one thread is updating it.
+    Operations on shared menus should always be done with the actions
+    locked.
+*/
+KHMEXP void KHMAPI
+khui_action_lock(void);
+
+/*! \brief Unlock the action and menu tables
+
+    Unlocks the action and menu tables after a call to
+    khui_action_lock().
+
+    \see khui_action_lock()
+ */
+KHMEXP void KHMAPI
+khui_action_unlock(void);
+
+/*! \brief Create a new menu
+
+    Creates a new menu.  The returned data structure must be freed by
+    a call to khui_menu_delete().  Custom menus that are created this
+    way are not reference counted or maintained by the UI library.
+    The caller is responsible for calling khui_menu_delete() when the
+    data is no longer needed.
+
+    Specifiying an action in the \a action parameter will associate
+    the menu with the specified action.  In this case, if the action
+    is added to another menu with the ::KHUI_ACTIONREF_SUBMENU flag,
+    this menu will appear as a submenu within that menu.  Only one
+    menu can be associated with any given action.  Custom menus can
+    not be associated with standard actions.
+ */
+KHMEXP khui_menu_def * KHMAPI
+khui_menu_create(khm_int32 action);
+
+/*! \brief Duplicate a menu
+
+    Creates a copy of the specified menu.  The returned data structure
+    must be freed by a call to khui_menu_delete().  Custom menus are
+    not reference counted or maintained by the UI library.  The caller
+    is responsible for calling khui_menu_delete() when the data is no
+    longer needed.
+
+    Note that even if the original menu was associated with an action,
+    the duplicate will not be.  Modifying the duplicate will not
+    modify the original menu.  Only one menu can be associated with an
+    action.
+ */
+KHMEXP khui_menu_def * KHMAPI
+khui_menu_dup(khui_menu_def * src);
+
+/*! \brief Delete a menu
+
+    Deletes a menu created by a call to khui_menu_create() or
+    khui_menu_dup().  This frees up the memory and associated
+    resources used by the menu definition.  The pointer that is passed
+    in will no longer be valid.
+ */
+KHMEXP void KHMAPI
+khui_menu_delete(khui_menu_def * d);
+
+/*! \brief Insert an action into a menu
+
+    The action specified by \a cmd will be inserted in to the menu \a
+    d at index \a idx.
+
+    \param[in] d The menu to insert the action into
+
+    \param[in] idx The index at which to insert the action.  The index
+        is zero based.  If \a idx is (-1) or larger than the largest
+        index in the menu, the item is appended to the menu.
+
+    \param[in] cmd The command representing the action to insert into
+        the menu.  This should be either a standard action, a user
+        action created with khui_action_create(), or certain pseudo
+        actions.  Not all pseudo actions can be placed on a menu.
+
+    \param[in] flags Flags for the action.  This is a combination of
+        KHUI_ACTIONREF_* constants.  Currently, the only constants
+        that are valid for this function are: ::KHUI_ACTIONREF_SEP,
+        ::KHUI_ACTIONREF_SUBMENU, ::KHUI_ACTIONREF_DEFAULT.
+        ::KHUI_ACTIONREF_SEP will be automatically added if the
+        command is ::KHUI_MENU_SEP.  If ::KHUI_ACTIONREF_DEFAULT is
+        specified, then all other items in the menu will be stripped
+        of that flag leaving this action as the only one with that
+        flag set.
+ */
+KHMEXP void KHMAPI
+khui_menu_insert_action(khui_menu_def * d, khm_size idx, khm_int32 cmd, khm_int32 flags);
+
+#define khui_menu_add_action(d,c) khui_menu_insert_action((d),-1,(c),0)
+#pragma deprecated(khui_menu_add_action)
+
+#ifdef NOEXPORT
+
+/*! \brief Insert an action by reference into a menu
+
+    The action specified by \a act will be inserted into the menu \a d
+    at index \a idx.
+
+    \param[in] d The menu to inser the action into.
+
+    \param[in] idx The index at which to insert the action.  The index
+        is zero based.  If the index is (-1) or is larger than the
+        largest index in the menu, then the action is appended to the
+        menu.
+
+    \param[in] act The action to insert.  This is added by reference.
+        It is the callers reponsibility to ensure that the structure
+        pointed to by \a act is available throughout the lifetime of
+        the menu.
+
+    \param[in] flags Flags for the action.  This is a combination of
+        KHUI_ACTIONREF_* constants.  Currently, the only constants
+        that are valid for this function are: ::KHUI_ACTIONREF_SEP,
+        ::KHUI_ACTIONREF_SUBMENU, ::KHUI_ACTIONREF_DEFAULT.  For this
+        function, ::KHUI_ACTIONREF_PACTION will automatically be aded
+        when adding the action.  ::KHUI_ACTIONREF_SEP will be
+        automatically added if the command is ::KHUI_MENU_SEP.  If
+        ::KHUI_ACTIONREF_DEFAULT is specified, then all other items in
+        the menu will be stripped of that flag leaving this action as
+        the only one with that flag set.
+*/
+KHMEXP void KHMAPI
+khui_menu_insert_paction(khui_menu_def * d, khm_size idx, khui_action * act, khm_int32 flags);
+
+#define khui_menu_add_paction(d,a,f) khui_menu_insert_paction((d),-1,(a),(f))
+#pragma deprecated(khui_menu_add_paction)
+
+#endif
+
+/*! \brief Remove an action from a menu
+
+    The action at the specified index will be removed from the menu.
+  */
+KHMEXP void KHMAPI
+khui_menu_remove_action(khui_menu_def * d, khm_size idx);
+
+/*! \brief Get the number of items in the menu
+
+    Note that the count includes menu separators.  The indices of the
+    menu items range from 0 to one less than the value returned by
+    this function.
+ */
+KHMEXP khm_size KHMAPI
+khui_menu_get_size(khui_menu_def * d);
+
+/*! \brief Get the menu item at a specified index
+
+    The returned reference is only valid while the ::khui_menu_def
+    structure is valid.  In addition, the reference becomes invalid if
+    the list of actions in the menu data structure is modified in any
+    way.
+
+    If the specified index is out of bounds, then the function returns
+    NULL.
+
+ */
+KHMEXP khui_action_ref *
+khui_menu_get_action(khui_menu_def * d, khm_size idx);
+
+/*! \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 Set the current UI context using an existing context
+
+    Copies the context specified in \a ctx into the active UI context.
+
+    \param[in] ctx A pointer to a ::khui_action_context structure that
+        specifies the new UI context.  Cannot be NULL.
+*/
+KHMEXP void KHMAPI
+khui_context_set_indirect(khui_action_context * ctx);
+
+/*! \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(khm_int32 cmd, wchar_t * buf, khm_size bufsiz);
+
+#ifdef NOEXPORT
+/*! \brief Initializes the global accelerator table
+ */
+KHMEXP HACCEL KHMAPI khui_create_global_accel_table(void);
+#endif
+
+/*! \brief Find a menu by id
+
+    Finds the menu that is associated with the specified action.
+ */
+KHMEXP khui_menu_def * KHMAPI khui_find_menu(khm_int32 action);
+
+#ifdef NOEXPORT
+
+/* internal */
+KHMEXP void KHMAPI
+khui_set_main_window(HWND hwnd);
+
+#endif
+
+/*! \brief Trigger an action
+
+    Triggers the specified action using the specified UI context.
+
+    This function does not return until the specified action has been
+    processed.  Many standard actions are asynchronous and they will
+    return before processing will complete.
+
+    Pseudo actions should not be triggered using khui_action_trigger()
+    as they only carry meaning when invoked from specific windows or
+    contexts.
+
+    \param[in] action Action.  Should be one of the standard actions
+        or an action created by khui_action_create()
+
+    \param[in] ctx The UI context to use for the action.  If this is
+        NULL, the action will be triggered under the current UI context.
+ */
+KHMEXP void KHMAPI
+khui_action_trigger(khm_int32 action, khui_action_context * ctx);
+
+/*! \brief Find an action by id
+
+    \note This function should not be used by plugins.  It is there
+        for use by the NetIDMgr application.
+*/
+KHMEXP khui_action * KHMAPI khui_find_action(khm_int32 action);
+
+#ifdef NOEXPORT
+/*! \brief Get the length of the action list */
+KHMEXP size_t KHMAPI khui_action_list_length(khui_action_ref * ref);
+#endif
+
+/*! \brief Create a new action
+
+    Creates a new custom action.  The created custom action can be
+    added to menus, toolbars and can be triggered by
+    khui_action_trigger().
+
+    When the action is triggered as a result of the user selecting a
+    menu item, a toolbar item or as a result of calling
+    khui_action_trigger(), the subscription identified by \a hsub will
+    received a message of type ::KMSG_ACT, subtype
+    ::KMSG_ACT_ACTIVATE.  The \a uparam for the message will be the
+    action identifier that was returned by khui_action_create().  The
+    \a vparam of the message will currently be set to \a NULL.
+
+    Actions can optionally be named.  The name is not actively used by
+    the Network Identity Manager framework, but can be used to label
+    actions so that they can be looked up later using
+    khui_find_named_action().
+
+    \param[in] name Name for a named action.  The name must be unique
+        among all registered actions. (limited by KHUI_MAXCCH_NAME).
+        (Optional. Set to NULL if the action is not a named action.)
+        See \a note below for additional restrictions on the name of
+        the action.
+
+    \param[in] caption The localized caption for the action.  This
+        will be shown in menus, toolbars and buttons when the action
+        needs to be represented. (limited by KHUI_MAXCCH_SHORT_DESC)
+        (Required)
+
+    \param[in] tooltip The localized tooltip for the action. (limited
+        by KHUI_MAXCCH_SHORT_DESC) (Optional, set to NULL if there is
+        no tooltip associated with the action)
+
+    \param[in] userdata A custom value.
+
+    \param[in] type The type of the action.  Currently it should be
+        set to either ::KHUI_ACTIONTYPE_TRIGGER or
+        ::KHUI_ACTIONTYPE_TOGGLE.  For ::KHUI_ACTIONTYPE_TOGGLE, the
+        initial state will be unchecked.  Use khui_check_action()
+        function to change the checked state of the action.
+
+    \param[in] hsub The subscription that is notified when the action
+        is triggered. (Optional) The subscription must be created with
+        kmq_create_subscription().  The handle will be released when
+        it is no longer needed.  Hence, the caller should not release
+        it.
+
+    \return The identifier of the new action or zero if the action
+        could not be created.
+
+    \note For named custom actions, the name of the action can not be
+        the same as the name of a configuration node.  See
+        khui_cfg_register_node().
+ */
+KHMEXP khm_int32 KHMAPI
+khui_action_create(const wchar_t * name,
+                   const wchar_t * caption,
+                   const wchar_t * tooltip,
+                   void * userdata,
+                   khm_int32 type,
+                   khm_handle hsub);
+
+/* \brief Delete a custom action
+
+   Deletes a custom action created by a call to khui_action_create().
+   Custom actions should only be deleted when unloading a plugin.
+ */
+KHMEXP void KHMAPI
+khui_action_delete(khm_int32 action);
+
+/*! \brief Get the user data associated with a custom action
+
+    This function returns the user data that was specified when the
+    custom action was created usng khui_action_create().  If the
+    custom action identifier is invalid or if the custom action does
+    not contain any user data, this function will return NULL.
+ */
+KHMEXP void * KHMAPI
+khui_action_get_data(khm_int32 action);
+
+/*! \brief Find an action by name */
+KHMEXP khui_action * KHMAPI khui_find_named_action(const 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 action 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(khm_int32 action, khm_boolean enable);
+
+/*! \brief Check an action in an action group
+
+    Marks the action denoted by \a action as checked and resets the
+    checked bit in all other actions.
+
+    \param[in] d A menu definition.
+
+    \param[in] action 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 action);
+
+/*! \brief Check an action
+
+    For toggle typed actions, this sets or resets the check.
+ */
+KHMEXP void KHMAPI khui_check_action(khm_int32 cmd, khm_boolean check);
+
+#ifdef NOEXPORT
+/*!\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
+
+/*@}*/
+/*@}*/
+#endif
index 87e2355f1e0aec73eae54005b32db3e470733123..08dbad2ad3f1c2485dbb52bd1b53074bee3d8550 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_ACTIONDEF_H\r
-#define __KHIMAIRA_ACTIONDEF_H\r
-\r
-/*! \ingroup khui_actions\r
-  @{*/\r
-/*! \defgroup khui_std_actions Standard Actions\r
-@{ */\r
-\r
-/*!\name Standard actions\r
-  @{*/\r
-#define KHUI_ACTION_BASE 50000\r
-\r
-#define KHUI_ACTION_PROPERTIES  (KHUI_ACTION_BASE + 0)\r
-#define KHUI_ACTION_EXIT        (KHUI_ACTION_BASE + 1)\r
-#define KHUI_ACTION_SET_DEF_ID  (KHUI_ACTION_BASE + 3)\r
-#define KHUI_ACTION_SET_SRCH_ID (KHUI_ACTION_BASE + 4)\r
-#define KHUI_ACTION_PASSWD_ID   (KHUI_ACTION_BASE + 7)\r
-#define KHUI_ACTION_NEW_CRED    (KHUI_ACTION_BASE + 8)\r
-#define KHUI_ACTION_DEBUG_WINDOW    (KHUI_ACTION_BASE + 10)\r
-#define KHUI_ACTION_VIEW_REFRESH    (KHUI_ACTION_BASE + 11)\r
-#define KHUI_ACTION_LAYOUT_ID   (KHUI_ACTION_BASE + 12)\r
-#define KHUI_ACTION_LAYOUT_TYPE (KHUI_ACTION_BASE + 13)\r
-#define KHUI_ACTION_LAYOUT_LOC  (KHUI_ACTION_BASE + 14)\r
-#define KHUI_ACTION_TB_STANDARD (KHUI_ACTION_BASE + 15)\r
-#define KHUI_ACTION_OPT_KHIM    (KHUI_ACTION_BASE + 16)\r
-#define KHUI_ACTION_OPT_IDENTS  (KHUI_ACTION_BASE + 17)\r
-#define KHUI_ACTION_OPT_NOTIF   (KHUI_ACTION_BASE + 18)\r
-#define KHUI_ACTION_HELP_CTX    (KHUI_ACTION_BASE + 19)\r
-#define KHUI_ACTION_HELP_CONTENTS   (KHUI_ACTION_BASE + 20)\r
-#define KHUI_ACTION_HELP_INDEX  (KHUI_ACTION_BASE + 21)\r
-#define KHUI_ACTION_HELP_ABOUT  (KHUI_ACTION_BASE + 22)\r
-#define KHUI_ACTION_DESTROY_CRED    (KHUI_ACTION_BASE + 23)\r
-#define KHUI_ACTION_RENEW_CRED  (KHUI_ACTION_BASE + 24)\r
-#define KHUI_ACTION_OPEN_APP    (KHUI_ACTION_BASE + 25)\r
-#define KHUI_ACTION_MENU_ACTIVATE   (KHUI_ACTION_BASE + 26)\r
-#define KHUI_ACTION_CLOSE_APP   (KHUI_ACTION_BASE + 27)\r
-#define KHUI_ACTION_IMPORT      (KHUI_ACTION_BASE + 28)\r
-#define KHUI_ACTION_OPT_PLUGINS (KHUI_ACTION_BASE + 29)\r
-#define KHUI_ACTION_LAYOUT_CUST (KHUI_ACTION_BASE + 30)\r
-#define KHUI_ACTION_OPT_APPEAR  (KHUI_ACTION_BASE + 31)\r
-#define KHUI_ACTION_LAYOUT_RELOAD (KHUI_ACTION_BASE + 32)\r
-#define KHUI_ACTION_RENEW_ALL   (KHUI_ACTION_BASE + 33)\r
-#define KHUI_ACTION_DESTROY_ALL (KHUI_ACTION_BASE + 34)\r
-#define KHUI_ACTION_UICB        (KHUI_ACTION_BASE + 35)\r
-#define KHUI_ACTION_LAYOUT_MINI (KHUI_ACTION_BASE + 36)\r
-/*@}*/\r
-\r
-/*! \name Pseudo actions \r
-\r
-Pseudo actions do not trigger any specific function, but acts as a\r
-signal of some generic event which will be interpreted based on\r
-context.\r
-\r
-@{*/\r
-#define KHUI_PACTION_BASE   (KHUI_ACTION_BASE + 500)\r
-\r
-#define KHUI_PACTION_MENU   (KHUI_PACTION_BASE + 0)\r
-#define KHUI_PACTION_UP     (KHUI_PACTION_BASE + 1)\r
-#define KHUI_PACTION_DOWN   (KHUI_PACTION_BASE + 2)\r
-#define KHUI_PACTION_LEFT   (KHUI_PACTION_BASE + 3)\r
-#define KHUI_PACTION_RIGHT  (KHUI_PACTION_BASE + 4)\r
-#define KHUI_PACTION_ENTER  (KHUI_PACTION_BASE + 5)\r
-#define KHUI_PACTION_ESC    (KHUI_PACTION_BASE + 6)\r
-#define KHUI_PACTION_OK     (KHUI_PACTION_BASE + 7)\r
-#define KHUI_PACTION_CANCEL (KHUI_PACTION_BASE + 8)\r
-#define KHUI_PACTION_CLOSE  (KHUI_PACTION_BASE + 9)\r
-#define KHUI_PACTION_DELETE (KHUI_PACTION_BASE + 10)\r
-#define KHUI_PACTION_UP_EXTEND (KHUI_PACTION_BASE + 11)\r
-#define KHUI_PACTION_UP_TOGGLE (KHUI_PACTION_BASE + 12)\r
-#define KHUI_PACTION_DOWN_EXTEND (KHUI_PACTION_BASE + 13)\r
-#define KHUI_PACTION_DOWN_TOGGLE (KHUI_PACTION_BASE + 14)\r
-#define KHUI_PACTION_BLANK  (KHUI_PACTION_BASE + 15)\r
-#define KHUI_PACTION_NEXT   (KHUI_PACTION_BASE + 16)\r
-#define KHUI_PACTION_SELALL (KHUI_PACTION_BASE + 17)\r
-#define KHUI_PACTION_YES    (KHUI_PACTION_BASE + 18)\r
-#define KHUI_PACTION_NO     (KHUI_PACTION_BASE + 19)\r
-#define KHUI_PACTION_YESALL (KHUI_PACTION_BASE + 20)\r
-#define KHUI_PACTION_NOALL  (KHUI_PACTION_BASE + 21)\r
-#define KHUI_PACTION_REMOVE (KHUI_PACTION_BASE + 22)\r
-#define KHUI_PACTION_KEEP   (KHUI_PACTION_BASE + 23)\r
-#define KHUI_PACTION_DISCARD (KHUI_PACTION_BASE + 24)\r
-#define KHUI_PACTION_PGDN   (KHUI_PACTION_BASE + 25)\r
-#define KHUI_PACTION_PGUP   (KHUI_PACTION_BASE + 26)\r
-#define KHUI_PACTION_PGUP_EXTEND (KHUI_PACTION_BASE + 27)\r
-#define KHUI_PACTION_PGDN_EXTEND (KHUI_PACTION_BASE + 28)\r
-\r
-/*@}*/\r
-\r
-/*! \name Menus\r
-\r
-Stock menus.\r
-\r
-@{*/\r
-#define KHUI_MENU_BASE      (KHUI_ACTION_BASE + 1000)\r
-\r
-#define KHUI_MENU_MAIN      (KHUI_MENU_BASE + 0)\r
-#define KHUI_MENU_FILE      (KHUI_MENU_BASE + 1)\r
-#define KHUI_MENU_CRED      (KHUI_MENU_BASE + 2)\r
-#define KHUI_MENU_VIEW      (KHUI_MENU_BASE + 3)\r
-#define KHUI_MENU_OPTIONS   (KHUI_MENU_BASE + 4)\r
-#define KHUI_MENU_HELP      (KHUI_MENU_BASE + 5)\r
-\r
-#define KHUI_MENU_LAYOUT    (KHUI_MENU_BASE + 6)\r
-#define KHUI_MENU_TOOLBARS  (KHUI_MENU_BASE + 7)\r
-\r
-#define KHUI_MENU_IDENT_CTX (KHUI_MENU_BASE + 8)\r
-#define KHUI_MENU_TOK_CTX   (KHUI_MENU_BASE + 9)\r
-#define KHUI_MENU_ICO_CTX_MIN    (KHUI_MENU_BASE + 12)\r
-#define KHUI_MENU_ICO_CTX_NORMAL (KHUI_MENU_BASE + 13)\r
-#define KHUI_MENU_CWHEADER_CTX   (KHUI_MENU_BASE + 14)\r
-\r
-#define KHUI_MENU_COLUMNS   (KHUI_MENU_BASE + 15)\r
-\r
-#define KHUI_PMENU_TOK_SEL  (KHUI_MENU_BASE + 10)\r
-#define KHUI_PMENU_ID_SEL   (KHUI_MENU_BASE + 11)\r
-\r
-#define KHUI_MENU_DESTROY_CRED (KHUI_MENU_BASE + 16)\r
-#define KHUI_MENU_RENEW_CRED (KHUI_MENU_BASE + 17)\r
-\r
-/*@}*/\r
-\r
-/*! \name Toolbars\r
-@{*/\r
-#define KHUI_TOOLBAR_BASE   (KHUI_ACTION_BASE + 2000)\r
-\r
-#define KHUI_TOOLBAR_STANDARD   (KHUI_TOOLBAR_BASE + 0)\r
-/*@}*/\r
-\r
-/*! \brief Base for user actions\r
-\r
-    When creating new actions, the UI library will allocate command\r
-    identifiers starting with this one.\r
-*/\r
-#define KHUI_USERACTION_BASE    (KHUI_ACTION_BASE + 10000)\r
-\r
-/*! \brief Does this command represent a user action? */\r
-#define IS_USERACTION(cmd) ((cmd) >= KHUI_USERACTION_BASE)\r
-/*@}*/\r
-/*@}*/\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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_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)
+#define KHUI_ACTION_OPT_PLUGINS (KHUI_ACTION_BASE + 29)
+#define KHUI_ACTION_LAYOUT_CUST (KHUI_ACTION_BASE + 30)
+#define KHUI_ACTION_OPT_APPEAR  (KHUI_ACTION_BASE + 31)
+#define KHUI_ACTION_LAYOUT_RELOAD (KHUI_ACTION_BASE + 32)
+#define KHUI_ACTION_RENEW_ALL   (KHUI_ACTION_BASE + 33)
+#define KHUI_ACTION_DESTROY_ALL (KHUI_ACTION_BASE + 34)
+#define KHUI_ACTION_UICB        (KHUI_ACTION_BASE + 35)
+#define KHUI_ACTION_LAYOUT_MINI (KHUI_ACTION_BASE + 36)
+/*@}*/
+
+/*! \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)
+#define KHUI_PACTION_NEXT   (KHUI_PACTION_BASE + 16)
+#define KHUI_PACTION_SELALL (KHUI_PACTION_BASE + 17)
+#define KHUI_PACTION_YES    (KHUI_PACTION_BASE + 18)
+#define KHUI_PACTION_NO     (KHUI_PACTION_BASE + 19)
+#define KHUI_PACTION_YESALL (KHUI_PACTION_BASE + 20)
+#define KHUI_PACTION_NOALL  (KHUI_PACTION_BASE + 21)
+#define KHUI_PACTION_REMOVE (KHUI_PACTION_BASE + 22)
+#define KHUI_PACTION_KEEP   (KHUI_PACTION_BASE + 23)
+#define KHUI_PACTION_DISCARD (KHUI_PACTION_BASE + 24)
+#define KHUI_PACTION_PGDN   (KHUI_PACTION_BASE + 25)
+#define KHUI_PACTION_PGUP   (KHUI_PACTION_BASE + 26)
+#define KHUI_PACTION_PGUP_EXTEND (KHUI_PACTION_BASE + 27)
+#define KHUI_PACTION_PGDN_EXTEND (KHUI_PACTION_BASE + 28)
+
+/*@}*/
+
+/*! \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_MENU_CWHEADER_CTX   (KHUI_MENU_BASE + 14)
+
+#define KHUI_MENU_COLUMNS   (KHUI_MENU_BASE + 15)
+
+#define KHUI_PMENU_TOK_SEL  (KHUI_MENU_BASE + 10)
+#define KHUI_PMENU_ID_SEL   (KHUI_MENU_BASE + 11)
+
+#define KHUI_MENU_DESTROY_CRED (KHUI_MENU_BASE + 16)
+#define KHUI_MENU_RENEW_CRED (KHUI_MENU_BASE + 17)
+
+/*@}*/
+
+/*! \name Toolbars
+@{*/
+#define KHUI_TOOLBAR_BASE   (KHUI_ACTION_BASE + 2000)
+
+#define KHUI_TOOLBAR_STANDARD   (KHUI_TOOLBAR_BASE + 0)
+/*@}*/
+
+/*! \brief Base for user actions
+
+    When creating new actions, the UI library will allocate command
+    identifiers starting with this one.
+*/
+#define KHUI_USERACTION_BASE    (KHUI_ACTION_BASE + 10000)
+
+/*! \brief Does this command represent a user action? */
+#define IS_USERACTION(cmd) ((cmd) >= KHUI_USERACTION_BASE)
+/*@}*/
+/*@}*/
+
+#endif
index 7f0e624b4494e48b076b35b92b6ccd15701aeb4b..751abce013f0db0728d6255993d12d4e5143488f 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KHALERTS_H\r
-#define __KHIMAIRA_KHALERTS_H\r
-\r
-/*********************************************************************\r
-  Alerter and error reporting\r
-**********************************************************************/\r
-\r
-/*! \addtogroup khui\r
-@{ */\r
-\r
-/*!\defgroup khui_alert Alerter and Error Reporting\r
-@{*/\r
-\r
-struct tag_khui_alert;\r
-typedef struct tag_khui_alert khui_alert;\r
-\r
-#define KHUI_MAX_ALERT_COMMANDS  4\r
-\r
-/*! \brief Maximum number of characters in title including terminating NULL\r
- */\r
-#define KHUI_MAXCCH_TITLE 256\r
-\r
-/*! \brief Maximum number of bytes in title including terminating NULL\r
- */\r
-#define KHUI_MAXCB_TITLE (KHUI_MAXCCH_TITLE * sizeof(wchar_t))\r
-\r
-/*! \brief Maximum number of characters in message including terminating NULL\r
- */\r
-#define KHUI_MAXCCH_MESSAGE 1024\r
-\r
-/*! \brief Maximum number of bytes in message including terminating NULL\r
- */\r
-#define KHUI_MAXCB_MESSAGE (KHUI_MAXCCH_MESSAGE * sizeof(wchar_t))\r
-\r
-/*! \brief Maxumum number of characters in a suggestion including terminating NULL */\r
-#define KHUI_MAXCCH_SUGGESTION 1024\r
-\r
-/*! \brief Maximum number of bytes in a suggestion, including terminating NULL */\r
-#define KHUI_MAXCB_SUGGESTION (KHUI_MAXCCH_SUGGESTION * sizeof(wchar_t))\r
-\r
-/*! \brief Flags for an alert */\r
-enum khui_alert_flags {\r
-    KHUI_ALERT_FLAG_FREE_STRUCT     =0x00000001, \r
-    /*!< Internal. Free the structure once the alert is done. */\r
-\r
-    KHUI_ALERT_FLAG_FREE_TITLE      =0x00000002,\r
-    /*!< Internal. Free the \a title field when the alert is done.*/\r
-\r
-    KHUI_ALERT_FLAG_FREE_MESSAGE    =0x00000004,\r
-    /*!< Internal. Free the \a message field when the alert is done. */\r
-\r
-    KHUI_ALERT_FLAG_FREE_SUGGEST    =0x00000008,\r
-    /*!< Internal. Free the \a suggest field when the alert is done */\r
-\r
-    KHUI_ALERT_FLAG_DEFACTION       =0x00000010,\r
-    /*!< If the message is displayed as a balloon prompt, then perform\r
-      the default action when it is clicked.  The default action is\r
-      the first action added to the alert.  Cannot be used if there\r
-      are no actions or if ::KHUI_ALERT_FLAG_REQUEST_WINDOW is\r
-      specified.*/\r
-\r
-    KHUI_ALERT_FLAG_DISPATCH_CMD    =0x00000020,\r
-    /*!< If the message has commands, when the user clicks on one of\r
-      the command buttons, the corresponding command will be\r
-      immediately dispatched as if khui_action_trigger() is called\r
-      with a NULL UI context.  Otherwise, the selected command will be\r
-      stored in the alert and can be retrieved via a call to\r
-      khui_alert_get_response(). */\r
-\r
-    KHUI_ALERT_FLAG_VALID_TARGET    =0x00010000,\r
-    /*!< Internal. There is a valid target for the alert */\r
-\r
-    KHUI_ALERT_FLAG_VALID_ERROR     =0x00020000,\r
-    /*!< Internal. There is a valid error context associated with the alert */\r
-\r
-    KHUI_ALERT_FLAG_DISPLAY_WINDOW  =0x01000000,\r
-    /*!< The alert has been displayed in a window */\r
-\r
-    KHUI_ALERT_FLAG_DISPLAY_BALLOON =0x02000000,\r
-    /*!< The alert has been displayed in a ballon */\r
-\r
-    KHUI_ALERT_FLAG_REQUEST_WINDOW  =0x04000000,\r
-    /*!< The alert should be displayed in a window */\r
-\r
-    KHUI_ALERT_FLAG_REQUEST_BALLOON =0x08000000,\r
-    /*!< The alert should be displayed in a balloon */\r
-\r
-    KHUI_ALERT_FLAG_MODAL           =0x10000000,\r
-    /*!< Internal. Modal alert.  Do not set direclty. */\r
-\r
-    KHUI_ALERT_FLAGMASK_RDWR        =0x0C000030,\r
-    /*!< Bit mask of flags that can be set by khui_alert_set_flags() */\r
-};\r
-\r
-/*! \brief Alert types\r
-\r
-    These types can be set with khui_alert_set_type() to indicate\r
-    which type of alert this is.  The types defined here are\r
-    identified by the Network Identity Manager and will receive\r
-    special handling whereever appropriate.\r
-\r
-    The type is a hint to the application and will not guarantee a\r
-    particular behavior.\r
- */\r
-typedef enum tag_khui_alert_types {\r
-    KHUI_ALERTTYPE_NONE = 0,    /*!< No specific alert type */\r
-    KHUI_ALERTTYPE_PLUGIN,      /*!< Plug-in or module load related\r
-                                  alert */\r
-    KHUI_ALERTTYPE_EXPIRE,      /*!< Credential or identity expiration\r
-                                  warning */\r
-    KHUI_ALERTTYPE_RENEWFAIL,   /*!< Failed to renew credentials */\r
-    KHUI_ALERTTYPE_ACQUIREFAIL, /*!< Failed to acquire credentials */\r
-    KHUI_ALERTTYPE_CHPW,        /*!< Failed to change password */\r
-} khui_alert_type;\r
-\r
-/*! \brief Create an empty alert object\r
-\r
-    The returned result is a held pointer to a ::khui_alert object.\r
-    Use khui_alert_release() to release the object.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_create_empty(khui_alert ** result);\r
-\r
-/*! \brief Create a simple alert object\r
-\r
-    The returned result is a held pointer to a ::khui_alert object.\r
-    Use khui_alert_release() to release the object.\r
-\r
-    \param[in] title The title of the alert. (Required, Localized)\r
-        Limited by ::KHUI_MAXCCH_TITLE.\r
-\r
-    \param[in] message The message.  (Required. Localized).  Limited\r
-        by ::KHUI_MAXCCH_MESSAGE.\r
-\r
-    \param[in] severity One of ::tag_kherr_severity\r
-\r
-    \param[out] result Receives a held pointer to a ::khui_alert\r
-        object upon successful completion.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_create_simple(const wchar_t * title, \r
-                         const wchar_t * message, \r
-                         khm_int32 severity, \r
-                         khui_alert ** result);\r
-\r
-/*! \brief Set the title of an alert object\r
-\r
-    The title is limited by ::KHUI_MAXCCH_TITLE.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_set_title(khui_alert * alert, \r
-                     const wchar_t * title);\r
-\r
-/*! \brief Set the message of an alert object\r
-\r
-    The message is limited by ::KHUI_MAXCCH_MESSAGE.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_set_message(khui_alert * alert, \r
-                       const wchar_t * message);\r
-\r
-/*! \brief Set the suggestion of an alert object \r
-\r
-    The suggestion is limited by ::KHUI_MAXCCH_SUGGESTION\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_set_suggestion(khui_alert * alert,\r
-                          const wchar_t * suggestion);\r
-\r
-/*! \brief Set the severity of the alert object\r
-\r
-    The severity value is one of ::tag_kherr_severity\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_set_severity(khui_alert * alert, \r
-                        khm_int32 severity);\r
-\r
-/*! \brief Sets the flags of the alert\r
-\r
-    The flags are as defined in ::khui_alert_flags.  The bits that are\r
-    on in \a mask will be set to the corresponding values in \a flags.\r
-    Only the bits specified in ::KHUI_ALERT_FLAGMASK_RDWR can be\r
-    specified in \a mask.\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_alert_set_flags(khui_alert * alert, khm_int32 mask, khm_int32 flags);\r
-\r
-/*! \brief Clear all the commands from an alert object\r
-\r
-    \see khui_alert_add_command()\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_clear_commands(khui_alert * alert);\r
-\r
-/*! \brief Add a command to an alert object\r
-\r
-    The command ID should be a valid registered action.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_add_command(khui_alert * alert, \r
-                       khm_int32 command_id);\r
-\r
-/*! \brief Set the type of alert\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_alert_set_type(khui_alert * alert,\r
-                    khui_alert_type type);\r
-\r
-/*! \brief Set the action context for the alert */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_alert_set_ctx(khui_alert * alert,\r
-                   khui_scope scope,\r
-                   khm_handle identity,\r
-                   khm_int32 cred_type,\r
-                   khm_handle cred);\r
-\r
-/*! \brief Get the response code from an alert\r
-\r
-    Once an alert has been displayed to the user, the user may choose\r
-    a command from the list of commands provided in the alert (see\r
-    khui_alert_add_command() ).  This function can retrieve the\r
-    selected command from the alert.\r
-\r
-    \return The selected command or \a 0 if no commands were selected.\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_alert_get_response(khui_alert * alert);\r
-\r
-\r
-/*! \brief Display an alert\r
-\r
-    The alert must have a valid \a severity, \a title and a \a message\r
-    to be displayed.  Otherwise the function immediately returns with\r
-    a failure code.\r
-\r
-    The method used to display the alert is as follows:\r
-\r
-    - A balloon alert will be shown if one of the following is true: \r
-      - The NetIDMgr application is minimized or in the background.  \r
-      - ::KHUI_ALERT_FLAG_REQUEST_BALLOON is specified in \a flags.  \r
-    - Otherwise an alert window will be shown.\r
-\r
-    If the message, title of the alert is too long to fit in a balloon\r
-    prompt, there's a suggestion or if there are custom commands then\r
-    a placeholder balloon prompt will be shown which when clicked on,\r
-    shows the actual alert in an alert window.  \r
-\r
-    An exception is when ::KHUI_ALERT_FLAG_DEFACTION is specified in\r
-    flags.  In this case instead of a placeholder balloon prompt, one\r
-    will be shown with the actual title and message (truncated if\r
-    necessary).  Clicking on the balloon will cause the first command\r
-    in the command list to be performed.\r
-\r
-    The placeholder balloon prompt will have a title derived from the\r
-    first 63 characters of the \a title field in the alert and a\r
-    message notifying the user that they should click the balloon\r
-    prompt for more information.\r
-\r
-    To this end, it is beneficial to limit the length of the title to\r
-    63 characters (64 counting the terminating NULL).  This limit is\r
-    enforced on Windows.  Also, try to make the title descriptive.\r
-\r
-    User interaction with the alert will be as follows:\r
-\r
-    - If the alert contains no commands, then the alert will be\r
-      displayed to the user as described above.  A 'close' button will\r
-      be added to the alert if the alert is being displayed in a\r
-      window.\r
-\r
-    - If the alert contains commands, has the\r
-      ::KHUI_ALERT_FLAG_DEFACTION flag set and is displayed in a\r
-      balloon and the user clicks on it, the first command in the\r
-      command list will be executed.\r
-\r
-    - If the alert contains commands and does not have the\r
-      ::KHUI_ALERT_FLAG_DEFACTION and has the\r
-      ::KHUI_ALERT_FLAG_DISPATCH_CMD flag set, then when the user\r
-      selects one of the command buttons, the corresponding command\r
-      will immediately be dispatched. (see\r
-      ::KHUI_ALERT_FLAG_DISPATCH_CMD).\r
-\r
-    - If the alert contains command and have neither\r
-      ::KHUI_ALERT_FLAG_DEFACTION nor ::KHUI_ALERT_FLAG_DISPATCH_CMD,\r
-      then when the user selects one of the command buttons, the\r
-      selected command will be stored along with the alert.  It can be\r
-      retrieved via a call to khui_alert_get_response().\r
-\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_show(khui_alert * alert);\r
-\r
-/*! \brief Display a modal alert\r
-\r
-    Similar to khui_alert_show(), but shows a modal alert dialog.  The\r
-    function does not return until the user has closed the alert.\r
-\r
-    This function always opens an alert window (never shows a\r
-    balloon).\r
-\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_alert_show_modal(khui_alert * alert);\r
-\r
-/*! \brief Queue an alert\r
-\r
-    Instead of displaying the alert immediately, the alert is queued\r
-    and the status bar updated to notify the user that there is a\r
-    pending alert.  Once the user activates the pending alert, it will\r
-    be displayed as if khui_alert_show() was called.\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_alert_queue(khui_alert * alert);\r
-\r
-/*! \brief Display a simple alert\r
-\r
-    \see khui_alert_show()\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_show_simple(const wchar_t * title, \r
-                       const wchar_t * message, \r
-                       khm_int32 severity);\r
-\r
-/*! \brief Obtain a hold on the alert\r
-\r
-    An alert structure is only considered valid for the duration that\r
-    there is a hold on it.\r
-\r
-    Use khui_alert_release() to release the hold.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_hold(khui_alert * alert);\r
-\r
-/*! \brief Release the hold on the alert\r
-\r
-    Holds obtained on an alert using any of the functions that either\r
-    return a held pointer to an alert or implicitly obtains a hold on\r
-    it need to be undone through a call to khui_alert_release().\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_alert_release(khui_alert * alert);\r
-\r
-/*! \brief Lock an alert \r
-\r
-    Locking an alert disallows any other thread from accessing the\r
-    alert at the same time.  NetIDMgr keeps a global list of all alert\r
-    objects and the user interface may access any of them at various\r
-    points in time.  Locking the alert allows a thread to modify an\r
-    alert without causing another thread to be exposed to an\r
-    inconsistent state.\r
-\r
-    Once a thread obtains a lock on the alert, it must call\r
-    khui_alert_unlock() to unlock it.  Otherwise no other thread will\r
-    be able to access the alert.\r
-\r
-    \note Currently the alert lock is global.  Locking one alert\r
-        disallows access to all other alerts as well.\r
-\r
-    \note Calling khui_alert_lock() is only necessary if you are\r
-        accessing the ::khui_alert structure directly.  Calling any of\r
-        the khui_alert_* functions to modify the alert does not\r
-        require obtaining a lock, as they perform synchronization\r
-        internally.\r
-*/\r
-KHMEXP void KHMAPI \r
-khui_alert_lock(khui_alert * alert);\r
-\r
-/*! \brief Unlock an alert \r
-\r
-    \see khui_alert_lock()\r
-*/\r
-KHMEXP void KHMAPI \r
-khui_alert_unlock(khui_alert * alert);\r
-\r
-/*!@}*/\r
-/*!@}*/\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
+@{*/
+
+struct tag_khui_alert;
+typedef struct tag_khui_alert khui_alert;
+
+#define KHUI_MAX_ALERT_COMMANDS  4
+
+/*! \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_DISPATCH_CMD    =0x00000020,
+    /*!< If the message has commands, when the user clicks on one of
+      the command buttons, the corresponding command will be
+      immediately dispatched as if khui_action_trigger() is called
+      with a NULL UI context.  Otherwise, the selected command will be
+      stored in the alert and can be retrieved via a call to
+      khui_alert_get_response(). */
+
+    KHUI_ALERT_FLAG_VALID_TARGET    =0x00010000,
+    /*!< Internal. There is a valid target for the alert */
+
+    KHUI_ALERT_FLAG_VALID_ERROR     =0x00020000,
+    /*!< Internal. 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_FLAG_MODAL           =0x10000000,
+    /*!< Internal. Modal alert.  Do not set direclty. */
+
+    KHUI_ALERT_FLAGMASK_RDWR        =0x0C000030,
+    /*!< Bit mask of flags that can be set by khui_alert_set_flags() */
+};
+
+/*! \brief Alert types
+
+    These types can be set with khui_alert_set_type() to indicate
+    which type of alert this is.  The types defined here are
+    identified by the Network Identity Manager and will receive
+    special handling whereever appropriate.
+
+    The type is a hint to the application and will not guarantee a
+    particular behavior.
+ */
+typedef enum tag_khui_alert_types {
+    KHUI_ALERTTYPE_NONE = 0,    /*!< No specific alert type */
+    KHUI_ALERTTYPE_PLUGIN,      /*!< Plug-in or module load related
+                                  alert */
+    KHUI_ALERTTYPE_EXPIRE,      /*!< Credential or identity expiration
+                                  warning */
+    KHUI_ALERTTYPE_RENEWFAIL,   /*!< Failed to renew credentials */
+    KHUI_ALERTTYPE_ACQUIREFAIL, /*!< Failed to acquire credentials */
+    KHUI_ALERTTYPE_CHPW,        /*!< Failed to change password */
+} khui_alert_type;
+
+/*! \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 action.
+ */
+KHMEXP khm_int32 KHMAPI 
+khui_alert_add_command(khui_alert * alert, 
+                       khm_int32 command_id);
+
+/*! \brief Set the type of alert
+ */
+KHMEXP khm_int32 KHMAPI
+khui_alert_set_type(khui_alert * alert,
+                    khui_alert_type type);
+
+/*! \brief Set the action context for the alert */
+KHMEXP khm_int32 KHMAPI
+khui_alert_set_ctx(khui_alert * alert,
+                   khui_scope scope,
+                   khm_handle identity,
+                   khm_int32 cred_type,
+                   khm_handle cred);
+
+/*! \brief Get the response code from an alert
+
+    Once an alert has been displayed to the user, the user may choose
+    a command from the list of commands provided in the alert (see
+    khui_alert_add_command() ).  This function can retrieve the
+    selected command from the alert.
+
+    \return The selected command or \a 0 if no commands were selected.
+ */
+KHMEXP khm_int32 KHMAPI
+khui_alert_get_response(khui_alert * alert);
+
+
+/*! \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 cause the first command
+    in the command list to be performed.
+
+    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.
+
+    User interaction with the alert will be as follows:
+
+    - If the alert contains no commands, then the alert will be
+      displayed to the user as described above.  A 'close' button will
+      be added to the alert if the alert is being displayed in a
+      window.
+
+    - If the alert contains commands, has the
+      ::KHUI_ALERT_FLAG_DEFACTION flag set and is displayed in a
+      balloon and the user clicks on it, the first command in the
+      command list will be executed.
+
+    - If the alert contains commands and does not have the
+      ::KHUI_ALERT_FLAG_DEFACTION and has the
+      ::KHUI_ALERT_FLAG_DISPATCH_CMD flag set, then when the user
+      selects one of the command buttons, the corresponding command
+      will immediately be dispatched. (see
+      ::KHUI_ALERT_FLAG_DISPATCH_CMD).
+
+    - If the alert contains command and have neither
+      ::KHUI_ALERT_FLAG_DEFACTION nor ::KHUI_ALERT_FLAG_DISPATCH_CMD,
+      then when the user selects one of the command buttons, the
+      selected command will be stored along with the alert.  It can be
+      retrieved via a call to khui_alert_get_response().
+
+ */
+KHMEXP khm_int32 KHMAPI 
+khui_alert_show(khui_alert * alert);
+
+/*! \brief Display a modal alert
+
+    Similar to khui_alert_show(), but shows a modal alert dialog.  The
+    function does not return until the user has closed the alert.
+
+    This function always opens an alert window (never shows a
+    balloon).
+
+ */
+KHMEXP khm_int32 KHMAPI
+khui_alert_show_modal(khui_alert * alert);
+
+/*! \brief Queue an alert
+
+    Instead of displaying the alert immediately, the alert is queued
+    and the status bar updated to notify the user that there is a
+    pending alert.  Once the user activates the pending alert, it will
+    be displayed as if khui_alert_show() was called.
+ */
+KHMEXP khm_int32 KHMAPI
+khui_alert_queue(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
+        accessing 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
index efe14789d3516250f05b0b24b34219453445f514..bbc712a367587e0424bf0303df7572becbfc8b08 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KHCONFIGUI_H\r
-#define __KHIMAIRA_KHCONFIGUI_H\r
-\r
-/*! \addtogroup khui\r
-@{ */\r
-\r
-/*! \defgroup khui_cfg Configuration Panels\r
-\r
-    Configuration panels are the primary means from which the user is\r
-    presented with an interface to change NetIDMgr and plugin\r
-    configuration.\r
-\r
-@{ */\r
-\r
-/*! \brief Configuration window notification message\r
-\r
-    This is the message that will be used to notify dialog panels.\r
-\r
-    The format of the message is :\r
-    - uMsg : KHUI_WM_CFG_NOTIFY\r
-    - HIWORD(wParam) : one of ::khui_wm_cfg_notifications\r
-\r
-    \note This is the same as ::KHUI_WM_NC_NOTIFY\r
- */\r
-#define KHUI_WM_CFG_NOTIFY (WM_APP + 0x101)\r
-\r
-/*! \brief Configuration notifications\r
-\r
-    These are sent thorugh a ::KHUI_WM_CFG_NOTIFY message.\r
-\r
-    The format of the message is :\r
-    - uMsg : KHUI_WM_CFG_NOTIFY\r
-    - HIWORD(wParam) : one of ::khui_wm_cfg_notifications\r
- */\r
-enum khui_wm_cfg_notifications {\r
-    WMCFG_SHOW_NODE = 1,\r
-    /*!< Sent to the configuration dialog to request that the panel\r
-      for the specified node be shown.  The \a lParam message\r
-      parameter will contain a held ::khui_config_node handle.  The\r
-      sender of the mssage is responsible for releasing the handle.*/\r
-\r
-    WMCFG_UPDATE_STATE = 2,\r
-    /*!< Sent to the configuration dialog to indicate that the state\r
-      flags for the specified configuration node have changed.\r
-\r
-      - LOWORD(wParam) : new flags\r
-      - lParam : ::khui_config_node for the node*/\r
-\r
-    WMCFG_APPLY = 3,\r
-    /*!< Sent to all the configuration panels when the user clicks the\r
-      'Apply' button or the 'Ok' button.  The panels are responsible\r
-      for applying the configuration changes and updating their flags\r
-      using khui_cfg_set_flags(). */\r
-\r
-    WMCFG_SYNC_NODE_LIST = 4,\r
-    /*!< Sent from the UI library to the configuration window to\r
-      notify the window that the node list has changed.  This message\r
-      is sent synchronously before the node is removed. */\r
-};\r
-\r
-/*! \brief Registration information for a configuration node\r
-\r
-    \see khui_cfg_register_node()\r
-*/\r
-typedef struct tag_khui_config_node_reg {\r
-    const wchar_t * name;       /*!< Internal identifier\r
-                                  (not-localized, required).  The name\r
-                                  is required to be unique among\r
-                                  sibling nodes.  However it is not\r
-                                  required to be unique globally.  The\r
-                                  size of the name is constrained by\r
-                                  ::KHUI_MAXCCH_NAME*/\r
-\r
-    const wchar_t * short_desc; /*!< Short description (Localized,\r
-                                  required).  This is the name which\r
-                                  identifies the node within a\r
-                                  collection of siblings.  The size of\r
-                                  the string is constrained by\r
-                                  ::KHUI_MAXCCH_SHORT_DESC*/\r
-\r
-    const wchar_t * long_desc;  /*!< Global name of the node.\r
-                                  (Localized, required).  This\r
-                                  uniquely identifies the node in the\r
-                                  collection of all configuration\r
-                                  nodes.  The size of the string is\r
-                                  constrained by\r
-                                  ::KHUI_MAXCCH_LONG_DESC.*/\r
-\r
-    HMODULE   h_module;         /*!< Module which contains the dialog\r
-                                  resource specified in \a\r
-                                  dlg_template */\r
-\r
-    LPWSTR    dlg_template;     /*!< Dialog template for the\r
-                                  configuration window */\r
-\r
-    DLGPROC   dlg_proc;         /*!< Dialog procedure */\r
-\r
-    khm_int32 flags;            /*!< Flags.  Can be a combination of\r
-                                  ::KHUI_CNFLAG_SORT_CHILDREN and\r
-                                  ::KHUI_CNFLAG_SUBPANEL*/\r
-\r
-} khui_config_node_reg;\r
-\r
-/*! \brief Sort the child nodes by short description */\r
-#define KHUI_CNFLAG_SORT_CHILDREN 0x0001\r
-\r
-/*! \brief Is a subpanel */\r
-#define KHUI_CNFLAG_SUBPANEL      0x0002\r
-\r
-/*! \brief Node represents a panel that is replicated for all child nodes */\r
-#define KHUI_CNFLAG_PLURAL        0x0004\r
-\r
-/*! \brief System node\r
-\r
-    \note For internal use by the NetIDMgr application.  Do not use.\r
-*/\r
-#define KHUI_CNFLAG_SYSTEM        0x0010\r
-\r
-/*! \brief Settings have been modified\r
-\r
-    Settings for this configuration panel have been modified.  This\r
-    flag should be cleared once the settings have been successfully\r
-    applied.\r
- */\r
-#define KHUI_CNFLAG_MODIFIED      0x0100\r
-\r
-/*! \brief Settings have been applied\r
-\r
-    Set once any modified settings were successfully applied.\r
- */\r
-#define KHUI_CNFLAG_APPLIED       0x0200\r
-\r
-#define KHUI_CNFLAGMASK_STATIC    0x00ff\r
-#define KHUI_CNFLAGMASK_DYNAMIC   0x0f00\r
-\r
-/*! \brief Maximum length of the name in characters\r
-\r
-    The length includes the terminating NULL\r
- */\r
-#define KHUI_MAXCCH_NAME 256\r
-\r
-/*! \brief Maximum length of the name in bytes\r
-\r
-    The length includes the terminating NULL\r
- */\r
-#define KHUI_MAXCB_NAME (KHUI_MAXCCH_NAME * sizeof(wchar_t))\r
-\r
-/*! \brief Maximum length of the long description in characters\r
-\r
-    The length includes the terminating NULL\r
- */\r
-#define KHUI_MAXCCH_LONG_DESC 1024\r
-\r
-/*! \brief Maximum length of the long description in bytes\r
-\r
-    The length includes the terminating NULL\r
- */\r
-#define KHUI_MAXCB_LONG_DESC (KHUI_MAXCCH_LONG_DESC * sizeof(wchar_t))\r
-\r
-/*! \brief Maximum length of the short description in chracters\r
-\r
-    The length includes the terminating NULL\r
- */\r
-#define KHUI_MAXCCH_SHORT_DESC 256\r
-\r
-/*! \brief Maximum length of the short description in bytes\r
-\r
-    The length includes the terminating NULL\r
- */\r
-#define KHUI_MAXCB_SHORT_DESC (KHUI_MAXCCH_SHORT_DESC * sizeof(wchar_t))\r
-\r
-/*! \brief Width of a configuration dialog in dialog units\r
-\r
-    ::CFGDLG_WIDTH and ::CFGDLG_HEIGHT specify the dimensions of a\r
-    configuration dialog width and height in dialog units.  The dialog\r
-    will be created as a child of the configuration dialog and placed\r
-    within it.\r
- */\r
-#define CFGDLG_WIDTH 255\r
-\r
-/*! \brief Height of a configuration dialog in dialog units \r
-\r
-    \see ::CFGDLG_WIDTH\r
-*/\r
-#define CFGDLG_HEIGHT 182\r
-\r
-/*! \brief Width of a configuration tab dialog in dialog units\r
-\r
-    ::CFGDLG_TAB_WIDTH and ::CFGDLG_TAB_HEIGHT specify the dimensions\r
-    (in dialog units) of a dialog that will be placed within a tab\r
-    control for dialogs where multiple display panels need to be\r
-    shown.\r
- */\r
-#define CFGDLG_TAB_WIDTH 235\r
-\r
-/*! \brief Height of configuration tab dialog in dialog units\r
-\r
-    \see ::CFGDLG_TAB_WIDTH\r
- */\r
-#define CFGDLG_TAB_HEIGHT 151\r
-\r
-/*! \brief A handle to a configuration node\r
-\r
-    \see khui_cfg_open_node(), khui_cfg_close_node()\r
-*/\r
-typedef khm_handle khui_config_node;\r
-\r
-/*! \brief Initialization data passed in to a subpanel \r
-\r
-    When creating a subpanel, a pointer to the following strucutred\r
-    will be passed in as the creation parameter for the dialog.\r
-*/\r
-typedef struct tag_khui_config_init_data {\r
-    khui_config_node ctx_node;  /*!< The node under which the current\r
-                                  dialog subpanel is being created. */\r
-\r
-    khui_config_node this_node; /*!< The node which provided the\r
-                                  registration information for the\r
-                                  creation of the subpanel. */\r
-\r
-    khui_config_node ref_node;  /*!< The parent node of the subpanel\r
-                                  node.  In nodes which have the\r
-                                  ::KHUI_CNFLAG_PLURAL, this would be\r
-                                  different from the \a node. This is\r
-                                  the node under which the subpanel\r
-                                  was registered. */\r
-} khui_config_init_data;\r
-\r
-/*! \brief Register a configuration node\r
-\r
-    The caller fills the registration information in the\r
-    ::khui_config_node_reg structre.  If the call succeeds, the\r
-    function will return KHM_ERROR_SUCCESS.\r
-\r
-    \param[in] parent Parent of the node to be registered.  Set to\r
-        NULL if the parent is the root node.\r
-\r
-    \param[in] reg Registration information\r
-\r
-    \param[out] new_id Receives the new unique identifier of the\r
-        configuration node.  Pass in NULL if the new identifier is not\r
-        required.\r
-\r
-    \retval KHM_ERROR_SUCCESS Success\r
-    \retval KHM_ERROR_INVALID_PARAM One or more parameters, or fields\r
-        of reg were invalid\r
-    \retval KHM_ERROR_DUPLICATE A node with the same name exists as a\r
-        child of the specified parent node.\r
-\r
-    \note The name (not the short or long description) of the node can\r
-        not be the same as the name of a custom action.  See\r
-        khui_action_create().\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_register(khui_config_node parent,\r
-                  const khui_config_node_reg * reg);\r
-\r
-/*!\brief Open a configuration node by name\r
-\r
-    If successful, the \a result parameter will receive a handle to\r
-    the configuration node.  Use khui_cfg_release() to release\r
-    the handle.\r
-\r
-    \param[in] parent Parent node.  Set to NULL to specify root node.\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_open(khui_config_node parent,\r
-              const wchar_t * name,\r
-              khui_config_node * result);\r
-\r
-/*! \brief Remove a configuration node\r
-\r
-    Marks a configuration node as deleted.  Once all the handles,\r
-    including the handle specified in \a node have been released, it\r
-    will be deleted.\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_remove(khui_config_node node);\r
-\r
-/*! \brief Hold a handle to a configuration node\r
-\r
-    Obtains an additional hold on the handle specified by \a node.\r
-    The hold must be released with a call to \a\r
-    khui_cfg_release()\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_hold(khui_config_node node);\r
-\r
-/*! \brief Release a handle to a configuration node\r
-\r
-    \see khui_cfg_hold()\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_release(khui_config_node node);\r
-\r
-/*! \brief Get the parent of a node\r
-\r
-    Returns a held handle to the parent of the node, or NULL if the\r
-    current node is a top level node.  The returned handle must be\r
-    released with khui_cfg_release().\r
-\r
-    \retval KHM_ERROR_SUCCESS The handle to the parent node is in \a result\r
-    \retval KHM_ERROR_NOT_FOUND The node is a top level node\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_get_parent(khui_config_node vnode,\r
-                    khui_config_node * result);\r
-\r
-/*! \brief Get a handle to the first child node\r
-\r
-    If the call is successful, \a result will receieve a handle to the\r
-    first child node of the specified node.  The returned handle must\r
-    be released with a call to khui_cfg_release()\r
-\r
-    If \a parent does not have any child nodes, the function will\r
-    return KHM_ERROR_NOT_FOUND and set \a result to NULL.\r
-\r
-    \param[in] parent Parent node.  Set to NULL to specify root node.\r
-    \param[out] result Receives a held handle to the first child node.\r
-\r
-    \see khui_cfg_get_next()\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_get_first_child(khui_config_node parent,\r
-                         khui_config_node * result);\r
-\r
-/*! \brief Get a handle to the first subpanel\r
-\r
-    If the call is successful, \a result will receieve a handle to the\r
-    first subpanel node of the specified node.  The returned handle\r
-    must be released with a call to khui_cfg_release()\r
-\r
-    If \a parent does not have any subpanels, the function will return\r
-    KHM_ERROR_NOT_FOUND and set \a result to NULL.\r
-\r
-    A subpanel node is a node which has the ::KHUI_CNFLAG_SUBPANEL\r
-    flag set.\r
-\r
-    \param[in] parent Parent node.  Set to NULL to specify root node.\r
-    \param[out] result Receives a held handle to the first subpanel node.\r
-\r
-    \see khui_cfg_get_next()\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_get_first_subpanel(khui_config_node vparent,\r
-                            khui_config_node * result);\r
-\r
-/*! \brief Get a handle to the next sibling node\r
-\r
-    If the call is successful, \a result will receive a held handle to\r
-    the next sibling node.  The returned handle must be released with\r
-    a call to khui_cfg_release().\r
-\r
-    If there are no more sibling nodes, then the function return\r
-    KHM_ERROR_NOT_FOUND and set \a result to NULL.\r
-\r
-    This function can be used to traverse a list of child nodes as\r
-    well as a list of subpanel nodes.\r
-\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_get_next(khui_config_node node,\r
-                  khui_config_node * result);\r
-\r
-/*! \brief Get a handle to the next sibling node\r
-\r
-    Similar to khui_cfg_get_next(), but implicitly releases the handle\r
-    that was supplied.  Equivalent to doing :\r
-\r
-    \code\r
-    khui_cfg_get_next(node, &next);\r
-    khui_cfg_release(node);\r
-    node = next;\r
-    \endcode\r
-\r
-    \param[in,out] node On entry, specifies the node whose sibling\r
-        needs to be fetched.  On exit, will have either NULL or a held\r
-        handle to the sibling node.  The handle which was supplied to\r
-        the function is released.\r
-\r
-    \retval KHM_ERROR_SUCCESS The next node is now in \a node\r
-    \retval KHM_ERROR_INVALID_PARAM \a node was not a valid handle\r
-    \retval KHM_ERROR_NOT_FOUND There are no more siblings.  \a node\r
-        is set to NULL.\r
-\r
-    \note Even if there are no more siblings, the handle specified in\r
-        \a node on entry is released.\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_get_next_release(khui_config_node * node);\r
-\r
-/*! \brief Get the name of a configuration node \r
-\r
-    Gets the name (not the short description or the long description)\r
-    of the given configuration node.\r
-*/\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_get_name(khui_config_node node,\r
-                  wchar_t * buf,\r
-                  khm_size * cb_buf);\r
-\r
-/*! \brief Get registration information for a node\r
-\r
-    The registration information that is returned is a shallow copy of\r
-    the data kept by NetIDMgr.  In particular, the strings that will\r
-    be returned actually point to internal buffers and should not be\r
-    modified.\r
-\r
-    No further action is necessary to release the information.\r
-    However, the returned data ceases to be valid when \a node is\r
-    released with a call to khui_cfg_release().\r
-\r
-    \param[in] node Node for which information is requested.  Can be NULL if requesting information about the root node.\r
-    \param[out] reg Pointer to a ::khui_config_node_reg structure.\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_get_reg(khui_config_node node,\r
-                 khui_config_node_reg * reg);\r
-\r
-/*! \brief Internal use\r
-\r
-    This function is used internally by NetIDMgr.  Do not use.\r
-*/\r
-KHMEXP HWND KHMAPI\r
-khui_cfg_get_hwnd_inst(khui_config_node node,\r
-                       khui_config_node noderef);\r
-\r
-/*! \brief Internal use\r
-\r
-    This function is used internally by NetIDMgr.  Do not use.\r
-*/\r
-KHMEXP LPARAM KHMAPI\r
-khui_cfg_get_param_inst(khui_config_node node,\r
-                        khui_config_node noderef);\r
-\r
-/*! \brief Internal use\r
-\r
-    This function is used internally by NetIDMgr.  Do not use.\r
-*/\r
-KHMEXP void KHMAPI\r
-khui_cfg_set_hwnd_inst(khui_config_node node, \r
-                       khui_config_node noderef,\r
-                       HWND hwnd);\r
-\r
-/*! \brief Internal use\r
-\r
-    This function is used internally by NetIDMgr.  Do not use.\r
-*/\r
-KHMEXP void KHMAPI\r
-khui_cfg_set_param_inst(khui_config_node node, \r
-                        khui_config_node noderef,\r
-                        LPARAM param);\r
-\r
-/*! \brief Internal use\r
-\r
-    This function is used internally by NetIDMgr.  Do not use.\r
-*/\r
-KHMEXP HWND KHMAPI\r
-khui_cfg_get_hwnd(khui_config_node node);\r
-\r
-/*! \brief Internal use\r
-\r
-    This function is used internally by NetIDMgr.  Do not use.\r
-*/\r
-KHMEXP LPARAM KHMAPI\r
-khui_cfg_get_param(khui_config_node node);\r
-\r
-/*! \brief Internal use\r
-\r
-    This function is used internally by NetIDMgr.  Do not use.\r
-*/\r
-KHMEXP void KHMAPI\r
-khui_cfg_set_hwnd(khui_config_node node, HWND hwnd);\r
-\r
-/*! \brief Internal use\r
-\r
-    This function is used internally by NetIDMgr.  Do not use.\r
-*/\r
-KHMEXP void KHMAPI\r
-khui_cfg_set_param(khui_config_node node, LPARAM param);\r
-\r
-/*! \brief Internal use\r
-\r
-    This function is used internally by NetIDMgr.  Do not use.\r
-*/\r
-KHMEXP void KHMAPI\r
-khui_cfg_clear_params(void);\r
-\r
-/*! \brief Internal use\r
-\r
-    This function is used internally by NetIDMgr.  Do not use.\r
-*/\r
-KHMEXP void KHMAPI\r
-khui_cfg_set_configui_handle(HWND hwnd);\r
-\r
-/*! \brief Update the state for the specified node\r
-\r
-    \param[in] node ::khui_config_node handle for the configuration node.\r
-\r
-    \param[in] flags New flags.  Combination of ::KHUI_CNFLAG_APPLIED and ::KHUI_CNFLAG_MODIFIED\r
-\r
-    \param[in] mask Valid bits in \a flags\r
-\r
-    \note Should only be called from within the dialog procedure for\r
-        the configuration node.\r
- */\r
-KHMEXP void KHMAPI\r
-khui_cfg_set_flags(khui_config_node vnode, khm_int32 flags, khm_int32 mask);\r
-\r
-/*! \brief Retrieve the state flags for the configuration node\r
-\r
-    \see khui_cfg_set_flags()\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_get_flags(khui_config_node vnode);\r
-\r
-/*! \brief Utility function: Initialize dialog box window data\r
-\r
-    This function initializes the dialog box window data using the\r
-    ::khui_config_init_data that was passed into the WM_INITDIALOG\r
-    message.\r
-\r
-    A new block of memory will be alocated to store the dialog data as\r
-    well as any extra space specified.  A pointer to this memory block\r
-    will be stored in the \a DWLP_USER slot in the dialog box.\r
-\r
-    The allocated block of memory must be freed by a call to\r
-    khui_cfg_free_dialog_data().  While handling other messages, the\r
-    dialog data can be retrieved using khui_cfg_get_dialog_data().\r
-\r
-    \param[in] hwnd_dlg Handle to the dialog box\r
-\r
-    \param[in] data Pointer to the ::khui_config_init_data that was\r
-        passed in to WM_INITDIALOG (this is the value of \a lParam)\r
-\r
-    \param[in] cb_extra Number of extra bytes to allocate, along with\r
-        the space required to store the contents of\r
-        ::khui_config_init_data.  The extra space will be initialized\r
-        to zero.\r
-\r
-    \param[out] new_data Receives a pointer to the copy of the\r
-        initialization data that was allocated.  Optional.  Pass in\r
-        NULL if this value is not required.\r
-\r
-    \param[out] extra Receives a pointer to the block of extra memory\r
-        allocated as specified in \a cb_extra.  If \a cb_extra is 0,\r
-        then this receives a NULL.\r
-\r
-    \see khui_cfg_get_dialog_data(), khui_cfg_free_dialog_data()\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_init_dialog_data(HWND hwnd_dlg,\r
-                          const khui_config_init_data * data,\r
-                          khm_size cb_extra,\r
-                          khui_config_init_data ** new_data,\r
-                          void ** extra);\r
-\r
-/*! \brief Utility function: Retrieves dialog data \r
-\r
-    Retrieves the dialog data previoulsy stored using\r
-    khui_cfg_init_dialog_data().\r
-\r
-    \param[in] hwnd_dlg Handle to the dialog box \r
-\r
-    \param[out] data Receives a pointer to the ::khui_config_init_data\r
-        block.\r
-    \r
-    \param[out] extra Receives a pointer to the extra memory\r
-        allocated. Optional (set to NULL if this value is not needed).\r
-*/\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_get_dialog_data(HWND hwnd_dlg,\r
-                         khui_config_init_data ** data,\r
-                         void ** extra);\r
-\r
-/*! \brief Utility function: Free dialog data\r
-\r
-    Deallocates the memory allcated in a previous call to\r
-    khui_cfg_init_dialog_data()\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cfg_free_dialog_data(HWND hwnd_dlg);\r
-\r
-/*! \brief Sets the instance flags for a subpanel\r
-\r
-    Since there can be more than one subpanel in a configuration\r
-    panel, they shouldn't modify the flags of the configuration node\r
-    directly.  Instead, they should call this function to set the\r
-    instance flags.\r
-\r
-    The instance flags will be merged with the flags for the\r
-    configuration node automatically.\r
- */\r
-KHMEXP void KHMAPI\r
-khui_cfg_set_flags_inst(khui_config_init_data * d,\r
-                        khm_int32 flags,\r
-                        khm_int32 mask);\r
-\r
-/*!@} */\r
-/*!@} */\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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(). */
+
+    WMCFG_SYNC_NODE_LIST = 4,
+    /*!< Sent from the UI library to the configuration window to
+      notify the window that the node list has changed.  This message
+      is sent synchronously before the node is removed. */
+};
+
+/*! \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
+
+/*! \brief System node
+
+    \note For internal use by the NetIDMgr application.  Do not use.
+*/
+#define KHUI_CNFLAG_SYSTEM        0x0010
+
+/*! \brief Settings have been modified
+
+    Settings for this configuration panel have been modified.  This
+    flag should be cleared once the settings have been successfully
+    applied.
+ */
+#define KHUI_CNFLAG_MODIFIED      0x0100
+
+/*! \brief Settings have been applied
+
+    Set once any modified settings were successfully applied.
+ */
+#define KHUI_CNFLAG_APPLIED       0x0200
+
+#define KHUI_CNFLAGMASK_STATIC    0x00ff
+#define KHUI_CNFLAGMASK_DYNAMIC   0x0f00
+
+/*! \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_PARAM 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.
+
+    \note The name (not the short or long description) of the node can
+        not be the same as the name of a custom action.  See
+        khui_action_create().
+ */
+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 the parent of a node
+
+    Returns a held handle to the parent of the node, or NULL if the
+    current node is a top level node.  The returned handle must be
+    released with khui_cfg_release().
+
+    \retval KHM_ERROR_SUCCESS The handle to the parent node is in \a result
+    \retval KHM_ERROR_NOT_FOUND The node is a top level node
+ */
+KHMEXP khm_int32 KHMAPI
+khui_cfg_get_parent(khui_config_node vnode,
+                    khui_config_node * result);
+
+/*! \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_PARAM \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
index 1246923e23422b4d6c4e75b5add88deb662e84b3..be6abb21c0a9b7835a96cc1fddd408fb561fac35 100644 (file)
@@ -1,77 +1,77 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KHHTLINK_H\r
-#define __KHIMAIRA_KHHTLINK_H\r
-\r
-/*! \addtogroup khui \r
-@{ */\r
-\r
-/*! \defgroup khui_hyperlink Hyperlink \r
-@{*/\r
-\r
-/*! \brief A hyperlink\r
-\r
-    When a link in a hypertext window is clicked, this structure is\r
-    passed along with the message.\r
-\r
-    The link text fields do to point to NULL terminated strings.\r
-    Instead, the length fields should be used to extract the string.\r
- */\r
-typedef struct tag_khui_htwnd_link {\r
-    RECT r;                     /*!< The enclosing rectangle of the\r
-                                  hyperlink.  Units are screen units\r
-                                  and the coordinates are relative to\r
-                                  the top left hand corner of the\r
-                                  hypertext area.  */\r
-    wchar_t * id;               /*!< The value of the \a id attribute\r
-                                  of the link or \a NULL if there was\r
-                                  no \a id attribute.  This does not\r
-                                  point to a \a NULL terminated\r
-                                  string.  The length of the string is\r
-                                  given by the \a id_len field. */\r
-    int id_len;                 /*!< The length of the string pointed\r
-                                  to by \a id in characters.\r
-                                  Undefined if \a id is \a NULL. */\r
-    wchar_t * param;            /*!< The value of the \a param\r
-                                  attribute of the link or \a NULL if\r
-                                  there was no \a param attribute.\r
-                                  This does not point to a \a NULL\r
-                                  terminated string.  The length of\r
-                                  the string is given by the \a\r
-                                  param_len field.*/\r
-    int param_len;              /*!< Length of the string pointed to\r
-                                  by \a param in characters.\r
-                                  Undefined if \a param is \a NULL. */\r
-} khui_htwnd_link;\r
-\r
-#define KHUI_MAXCCH_HTLINK_FIELD 256\r
-#define KHUI_MAXCB_HTLINK_FIELD (KHUI_MAXCCH_HTLINK_FIELD * sizeof(wchar_t))\r
-\r
-/*!@}*/\r
-/*!@}*/\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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;                     /*!< The enclosing rectangle of the
+                                  hyperlink.  Units are screen units
+                                  and the coordinates are relative to
+                                  the top left hand corner of the
+                                  hypertext area.  */
+    wchar_t * id;               /*!< The value of the \a id attribute
+                                  of the link or \a NULL if there was
+                                  no \a id attribute.  This does not
+                                  point to a \a NULL terminated
+                                  string.  The length of the string is
+                                  given by the \a id_len field. */
+    int id_len;                 /*!< The length of the string pointed
+                                  to by \a id in characters.
+                                  Undefined if \a id is \a NULL. */
+    wchar_t * param;            /*!< The value of the \a param
+                                  attribute of the link or \a NULL if
+                                  there was no \a param attribute.
+                                  This does not point to a \a NULL
+                                  terminated string.  The length of
+                                  the string is given by the \a
+                                  param_len field.*/
+    int param_len;              /*!< Length of the string pointed to
+                                  by \a param in characters.
+                                  Undefined if \a param is \a NULL. */
+} khui_htwnd_link;
+
+#define KHUI_MAXCCH_HTLINK_FIELD 256
+#define KHUI_MAXCB_HTLINK_FIELD (KHUI_MAXCCH_HTLINK_FIELD * sizeof(wchar_t))
+
+/*!@}*/
+/*!@}*/
+
+#endif
index e22e1f77f25e188d4df73a7183f57781cc084a57..c3ef5dabf21110cf089474c2716a42c56099b999 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KHNEWCRED_H\r
-#define __KHIMAIRA_KHNEWCRED_H\r
-\r
-/********************************************************************\r
-  New credentials windows\r
-*********************************************************************/\r
-\r
-/*! \addtogroup khui\r
-@{ */\r
-\r
-/*! \defgroup khui_cred Credentials acquisition \r
-\r
-    Declarations associated with credentials acquisition.\r
-\r
-@{ */\r
-\r
-/*! \brief Window message sent to credentials type panels\r
-\r
-    This message is sent to the child windows.\r
-\r
-    The format of the message is :\r
-    - uMsg : KHUI_WM_NC_NOTIFY\r
-    - HIWORD(wParam) : one of ::khui_wm_nc_notifications\r
-    - LPARAM : pointer to the ::khui_new_creds structure\r
-*/\r
-#define KHUI_WM_NC_NOTIFY (WM_APP + 0x101)\r
-\r
-/*! \brief The first control ID that may be used by an identity provider */\r
-#define KHUI_CW_ID_MIN 8016\r
-\r
-/*! \brief The maximum number of controls that may be created by an identity provider*/\r
-#define KHUI_CW_MAX_CTRLS 8\r
-\r
-/*! \brief The maximum control ID that may be used by an identity provider */\r
-#define KHUI_CW_ID_MAX (KHUI_CW_ID_MIN + KHUI_CW_MAX_CTRLS - 1)\r
-\r
-\r
-/*! \brief Credentials dialog notifications\r
-\r
-    These notifications will be sent to the individual dialog\r
-    procedures of the credential type panels as a ::KHUI_WM_NC_NOTIFY\r
-    message.\r
-*/\r
-enum khui_wm_nc_notifications {\r
-    WMNC_DIALOG_EXPAND = 1, \r
-    /*!< The dialog is switching from basic to advanced mode or vice\r
-      versa.\r
-\r
-      This message is sent to the new creds dialog to set the dialog\r
-      to expanded mode.  In expanded mode, all credentials type panels\r
-      are visible as opposed to the compressed mode where they are not\r
-      visible.  The message is not sent to credentials type panels.*/\r
-\r
-    WMNC_DIALOG_SETUP,      \r
-    /*!< Sent by NetIDMgr to the new creds window to notify it that\r
-      the dialog should create all the type configuration panels.\r
-        \r
-      Until this message is issued, none of the credentials type\r
-      panels exist.  The credentials type panels will receive\r
-      WM_INITDIALOG etc as per the normal dialog creation process. */\r
-\r
-    WMNC_DIALOG_ACTIVATE,   \r
-    /*!< Sent by NetIDMgr to the new creds window to notify it that\r
-      the dialog should do final initialization work and activate. */\r
-\r
-    WMNC_DIALOG_MOVE,       \r
-    /*!< Sent by the new creds widnow to all the panels notifying them\r
-      that the NC window is moving. */\r
-\r
-    WMNC_DIALOG_SWITCH_PANEL, \r
-    /*!< Sent to the new creds window to cause it to switch to the\r
-      panel identified by LOWORD(wParam).\r
-\r
-      Does nothing if the specified panel is already the current\r
-      panel.  If the dialog is in compact mode and making the\r
-      specified panel visible requires switching to expanded mode, the\r
-      dialog will do so. */\r
-\r
-    WMNC_UPDATE_CREDTEXT,   \r
-    /*!< Sent to all the credential type panels for a credentials\r
-      window to request them to update the credential text.\r
-\r
-      When sent to the new credentials window, causes it to send the\r
-      WMNC_UPDATE_CREDTEXT message to all the credential type panels\r
-      and update the cred text window.*/\r
-\r
-    WMNC_CREDTEXT_LINK,    \r
-    /*!< Sent to a panel dialog proc when a user clicks a credtext\r
-      embedded link that belongs to that panel.  The \a lParam\r
-      parameter of the message is a pointer to a ::khui_htwnd_link\r
-      structure describing the link. */\r
-\r
-    WMNC_IDENTITY_CHANGE,   \r
-    /*!< The primary identity has changed */\r
-\r
-    WMNC_CLEAR_PROMPTS,     \r
-    /*!< Sent to the new creds window to clear any custom prompts */\r
-\r
-    WMNC_SET_PROMPTS,       \r
-    /*!< Sent to the new creds window to set custom prompts */\r
-    \r
-    WMNC_DIALOG_PREPROCESS, \r
-    /*!< Sent to all the credentials type panels to notify them that\r
-      the dialog is about to be processed */\r
-\r
-    WMNC_DIALOG_PROCESS,    \r
-    /*!< Process the dialog and signal whether to exit the dialog or\r
-      not */\r
-\r
-    WMNC_DIALOG_PROCESS_COMPLETE, \r
-    /*!< Sent to the new creds window to indicate that the all the\r
-      threads have completed processing.*/\r
-\r
-    WMNC_TYPE_STATE,        \r
-    /*!< Sent to the new creds window as notification that a\r
-      particular credentials type has changed state from enabled to\r
-      disabled or vice versa.  The LPARAM member of the message\r
-      specifies the credentials type identifier for the changed\r
-      type */\r
-\r
-    WMNC_ADD_CONTROL_ROW,\r
-    /*!< Add a row of controls to a new cred dialog.  This is an\r
-      internal message. */\r
-\r
-    WMNC_UPDATE_LAYOUT,\r
-    /*!< Update the layout of a dialog or window.  This is an internal\r
-      message. */\r
-};\r
-\r
-/*! \brief Plugins can use WMNC_NOTIFY message codes from here on up\r
-\r
-    \see ::KHUI_WM_NC_NOTIFY\r
- */\r
-#define WMNC_USER 2048\r
-\r
-/*! \brief Notifications to the identity provider\r
-\r
-    These notifications are sent through to the identity provider's UI\r
-    callback that was obtained using a ::KMSG_IDENT_GET_UI_CB message.\r
-\r
-    The callback routine is called from the context of the UI thread\r
-    and is expected to not make any blocking calls.  One of the\r
-    following commands will be passed in as the \a cmd parameter to\r
-    the callback.\r
- */\r
-enum khui_wm_nc_ident_notify {\r
-    WMNC_IDENT_INIT,            \r
-    /*!< Initialize an identity selector for a new credentials\r
-         dialog. The \a lParam parameter contains a handle to the\r
-         dialog window which will contain the identity selector\r
-         controls.  The identity provider may make use of the \a\r
-         ident_aux field of the ::khui_new_creds structure to hold any\r
-         data pertaining to the credentials acquisition dialog.*/\r
-\r
-    WMNC_IDENT_WMSG,\r
-    /*!< Windows message.  Presumably sent from one of the controls\r
-         that was created by the identity provider.  The callback is\r
-         expected to return TRUE if it processed the message or FALSE\r
-         if it did not.  The \a uMsg, \a wParam and \a lParam\r
-         parameters are set to the values passed in by Windows. */\r
-\r
-    WMNC_IDENT_EXIT,\r
-    /*!< Terminate a credentials acquisition dialog. Sent just before\r
-      the dialog is terminated. */\r
-};\r
-\r
-/*! \name Standard credtext link IDs\r
-@{*/\r
-\r
-/*! \brief Switch the panel\r
-    \r
-    The \a id attribute of the link specifies the ordinal of the panel\r
-    to switch to.\r
-*/\r
-#define CTLINKID_SWITCH_PANEL L"SwitchPanel"\r
-\r
-/*@}*/\r
-\r
-/*forward dcl*/\r
-struct tag_khui_new_creds_by_type;\r
-typedef struct tag_khui_new_creds_by_type khui_new_creds_by_type;\r
-struct tag_khui_new_creds_prompt;\r
-typedef struct tag_khui_new_creds_prompt khui_new_creds_prompt;\r
-struct tag_khui_new_creds;\r
-typedef struct tag_khui_new_creds khui_new_creds;\r
-\r
-typedef LRESULT\r
-(KHMAPI *khui_ident_new_creds_cb)(khui_new_creds * nc,\r
-                                  UINT cmd,\r
-                                  HWND hwnd,\r
-                                  UINT uMsg,\r
-                                  WPARAM wParam,\r
-                                  LPARAM lParam);\r
-\r
-/*! \brief New credentials acquisition blob\r
-\r
-    A pointer to an object of this type is passed in along with the\r
-    credentials acquisition messages.\r
-\r
-    \see \ref cred_acq for more information\r
-*/\r
-typedef struct tag_khui_new_creds {\r
-    khm_int32   magic;          /*!< Internal use */\r
-\r
-    khm_int32   subtype;        /*!< Subtype of the request that is\r
-                                  being handled through this object.\r
-                                  One of ::KMSG_CRED_NEW_CREDS,\r
-                                  ::KMSG_CRED_RENEW_CREDS or\r
-                                  ::KMSG_CRED_PASSWORD */\r
-\r
-    CRITICAL_SECTION cs;        /*!< Internal use */\r
-\r
-    khm_boolean set_default;    /*!< After a successfull credentials\r
-                                  acquisition, set the primary\r
-                                  identity as the default. */\r
-\r
-    khm_handle  *identities;    /*!< The list of identities associated\r
-                                  with this request.  The first\r
-                                  identity in this list (\a\r
-                                  identities[0]) is the primary\r
-                                  identity. */\r
-\r
-    khm_size    n_identities;   /*!< Number of identities in the list\r
-                                  \a identities */\r
-\r
-    khm_size    nc_identities;  /*!< Internal use */\r
-\r
-    khui_action_context ctx;    /*!< An action context specifying the\r
-                                  context in which the credentials\r
-                                  acquisition operation was\r
-                                  launced. */\r
-\r
-    khm_int32   mode;           /*!< The mode of the user interface.\r
-                                  One of ::KHUI_NC_MODE_MINI or\r
-                                  ::KHUI_NC_MODE_EXPANDED. */\r
-\r
-    HWND        hwnd;           /*!< Handle to the new credentials\r
-                                  window. */\r
-\r
-    struct tag_khui_new_creds_by_type **types;\r
-                                /*!< Internal use */\r
-    khm_handle  *type_subs;     /*!< Internal use */\r
-    khm_size    n_types;        /*!< Internal use */\r
-    khm_size    nc_types;       /*!< Internal use */\r
-\r
-    khm_int32   result;     /*!< One of ::KHUI_NC_RESULT_CANCEL or\r
-                                ::KHUI_NC_RESULT_PROCESS indicating\r
-                                the result of the dialog with the\r
-                                user */\r
-\r
-    khm_int32   response;   /*!< Response.  See individual message\r
-                                documentation for info on what to do\r
-                                with this field */\r
-\r
-    wchar_t     *password;  /*!< Not used. */\r
-\r
-    /* UI stuff */\r
-\r
-    wchar_t     *banner;        /*!< Internal use */\r
-    wchar_t     *pname;         /*!< Internal use */\r
-    khm_size    n_prompts;      /*!< Internal use */\r
-    khm_size    nc_prompts;     /*!< Internal use */\r
-    struct tag_khui_new_creds_prompt ** prompts; /*!< Internal use */\r
-\r
-    khui_ident_new_creds_cb ident_cb; /*!< Internal use */\r
-\r
-    wchar_t     *window_title;  /*!< Internal use */\r
-\r
-    LPARAM      ident_aux;      /*!< Auxilliary field which is\r
-                                  reserved for use by the identity\r
-                                  provider during the course of\r
-                                  conducting this dialog. */\r
-\r
-} khui_new_creds;\r
-\r
-#define KHUI_NC_MAGIC 0x84270427\r
-\r
-/*!\name Result values for khui_new_creds_t::result\r
-  @{*/\r
-#define KHUI_NC_RESULT_PROCESS    0\r
-#define KHUI_NC_RESULT_CANCEL       1\r
-/*@}*/\r
-\r
-/*!\name Mode values for khui_new_creds_t::mode\r
-  @{*/\r
-#define KHUI_NC_MODE_MINI       0\r
-#define KHUI_NC_MODE_EXPANDED   1\r
-/*@}*/\r
-\r
-/*!\name Response values for khui_new_creds_t::response\r
-  @{*/\r
-/*!\brief No known response */\r
-#define KHUI_NC_RESPONSE_NONE     0\r
-\r
-/*!\brief It is okay to exit the dialog now \r
-\r
-    This is the default, which is why it has a value of zero.  In\r
-    order to prevent the dialog from exiting, set the\r
-    KHUI_NC_RESPONSE_NOEXIT response bit. */\r
-#define KHUI_NC_RESPONSE_EXIT     0\r
-\r
-/*!\brief It is NOT okay to exit the dialog now\r
-\r
-    Used to indicate that further user-interaction is necessary to\r
-    process the dialog.  Usually this is accompanied by setting\r
-    necessary custom prompts and notifications so the user knows why\r
-    the dialog is prompting for more information.\r
- */\r
-#define KHUI_NC_RESPONSE_NOEXIT    0x00000002\r
-\r
-/*!\brief The dialog was processed successfully\r
-\r
-    Since this is the default response, the value is zero.  Use one of\r
-    KHUI_NC_RESPONSE_FAILED or KHUI_NC_RESPONSE_PENDING to indicate an\r
-    error or pending status.\r
- */\r
-#define KHUI_NC_RESPONSE_SUCCESS  0\r
-\r
-/*!\brief The processing of the dialog failed\r
-\r
-    Self explanatory.  More information about the failure should have\r
-    been reported using the khlog API, however, this response value\r
-    indicates to other credential types that depend on this credential\r
-    type that whatever it was that this credential type was supposed\r
-    to do didn't happen.\r
-*/\r
-#define KHUI_NC_RESPONSE_FAILED    0x00000008\r
-\r
-/*!\brief Further interaction required\r
-\r
-    Set along with KHUI_NC_RESPONSE_NOEXIT although it is not\r
-    required.  Setting this bit will automatically add the\r
-    KHUI_NC_RESPONSE_NOEXIT.\r
-\r
-    If this bit is set, all dependent plugins will be set on hold\r
-    until another round of processing clears the pending bit.\r
- */\r
-#define KHUI_NC_RESPONSE_PENDING   0x00000010\r
-\r
-/*! \brief Completed\r
-\r
-    This is automatically set if the plugin sets a response which does\r
-    not indicate either KHUI_NC_RESPONSE_NOEXIT or\r
-    KHUI_NC_RESPONSE_PENDING, which is considered to mean that the\r
-    plugin is completed processing.\r
-\r
-    This flag cannot be explicitly specified in a response.\r
- */\r
-#define KHUI_NC_RESPONSE_COMPLETED 0x00000020\r
-\r
-/*! \brief Processing\r
-\r
-    This is an internal flag set while the credentials acquisition\r
-    process is executing.\r
- */\r
-#define KHUI_NC_RESPONSE_PROCESSING 0x00010000\r
-\r
-#define KHUI_NCMASK_RESPONSE (KHUI_NC_RESPONSE_EXIT|KHUI_NC_RESPONSE_NOEXIT)\r
-#define KHUI_NCMASK_RESULT  (KHUI_NC_RESPONSE_SUCCESS|KHUI_NC_RESPONSE_FAILED|KHUI_NC_RESPONSE_PENDING)\r
-/*@}*/\r
-\r
-/*!\brief Maximum number of dependencies for a credentials type */\r
-#define KHUI_MAX_TYPE_DEPS 8\r
-\r
-/*!\brief Maximum number of credential types for a new creds window */\r
-#define KHUI_MAX_NCTYPES 16\r
-\r
-/*!\brief Maximum number of characters in a password\r
-\r
-  Length includes the termininating NULL\r
-*/\r
-#define KHUI_MAXCCH_PASSWORD 512\r
-\r
-/*! \brief Maximum number of bytes in a password\r
-\r
-  Includes terminating NULL\r
-*/\r
-#define KHUI_MAXCB_PASSWORD (KHUI_MAXCCH_PASSWORD * sizeof(wchar_t))\r
-\r
-/*! \brief Maximum number of characters in a custom banner\r
-\r
-    Length includes terminating NULL\r
-*/\r
-#define KHUI_MAXCCH_BANNER 256\r
-\r
-\r
-/*! \brief Maximum number of bytes in a custom banner\r
-\r
-    Length includes terminating NULL\r
-*/\r
-#define KHUI_MAXCB_BANNER (KHUI_MAXCCH_BANNER * sizeof(wchar_t))\r
-\r
-/*! \brief Maximum number of characters in a panel name\r
-\r
-    Length includes terminating NULL\r
-*/\r
-#define KHUI_MAXCCH_PNAME 256\r
-\r
-/*! \brief Maximum number of bytes in a panel name\r
-\r
-    Length includes terminating NULL\r
-*/\r
-#define KHUI_MAXCB_PNAME (KHUI_MAXCCH_PNAME * sizeof(wchar_t))\r
-\r
-/*! \brief A descriptor of a panel in the new credentials acquisition tab\r
-\r
-    When processing certain credentials messages such as\r
-    ::KMSG_CRED_PASSWORD, ::KMSG_CRED_NEW_CREDS,\r
-    ::KMSG_CRED_RENEW_CREDS, a pointer to a ::khui_new_creds structure\r
-    will be passed in to the message handler.  If the handler of the\r
-    message needs to add one or more credentials types as participants\r
-    of the operation, the handler will need to call khui_cw_add_type()\r
-    and specify a ::khui_new_creds_by_type structure.\r
-\r
-    Note that the memory address passed in to the call to\r
-    khui_cw_add_type() will not be copied.  Therefore, the block of\r
-    memory should remain as-is for the lifetime of the\r
-    ::khui_new_creds structure or until it is removed with a call to\r
-    khui_cw_del_type().\r
-\r
-    Some of the credentials messages that require specifying a\r
-    ::khui_new_creds_by_type structure require providing a\r
-    user-interface.  In these cases, the fields marked for providing a\r
-    UI may be required to hold valid values.  If the message does not\r
-    require providing a UI, these fields will be ignored.\r
-*/\r
-typedef struct tag_khui_new_creds_by_type {\r
-    khui_new_creds * nc;        /*!< Internal use.  Do not set */\r
-    khm_int32   flags;          /*!< Internal use.  Do not set */\r
-\r
-    khm_int32   type;           /*!< The identifier of the credentials\r
-                                  type.  This is a credentials type\r
-                                  identifier allocated with a call to\r
-                                  kcdb_credtype_register(). */\r
-\r
-    khm_int32   type_deps[KHUI_MAX_TYPE_DEPS];\r
-                                /*!< credentials types that this\r
-                                    credential type depends on.  Each\r
-                                    element defines a credentials type\r
-                                    identifier that this type depends\r
-                                    on for this operation.  The number\r
-                                    of valid values in this array\r
-                                    should be specified in the \a\r
-                                    n_type_deps field. */\r
-\r
-    khm_size    n_type_deps;    /*!< Number of dependencies listed\r
-                                  above.  Should be between 0 and\r
-                                  ::KHUI_MAX_TYPE_DEPS.  Specify 0 if\r
-                                  there are no dependencies. */\r
-\r
-    khm_size    ordinal;        /*!< The requested ordinal.  The UI\r
-                                  would attempt to place this panel at\r
-                                  the reqested order in the list of\r
-                                  panels.  Set to -1 if the order does\r
-                                  not matter.  Once the dialog is\r
-                                  activated this field will be updated\r
-                                  to reflect the actual ordinal of the\r
-                                  panel. */\r
-\r
-    wchar_t    *name;           /*!< Name of the panel (localized,\r
-                                  optional).  If NULL, the localized\r
-                                  name of the credentials type is\r
-                                  used. Only used if providing a\r
-                                  user-interface. */\r
-\r
-    HICON       icon;           /*!< Icon for the panel (optional).\r
-                                  Only used if providing a\r
-                                  user-interface. */\r
-\r
-    wchar_t    *tooltip;        /*!< Tooltip for the panel (localized,\r
-                                  optional).  If NULL, no tooltip will\r
-                                  be assigned for the panel.  Only\r
-                                  used if providing a\r
-                                  user-interface.  */\r
-\r
-    HMODULE     h_module;       /*!< Handle to the module containing\r
-                                  the dialog resource.  Only used if\r
-                                  providing a user-interface. */\r
-\r
-    LPWSTR      dlg_template;   /*!< The dialog resource.  Only used\r
-                                  if providing a user-interface. */\r
-    DLGPROC     dlg_proc;       /*!< The dialog procedure. Only used\r
-                                  if providing a user-interface. */\r
-\r
-    HWND        hwnd_panel;     /*!< The dialog window.  Once the\r
-                                  dialog panel is created, a handle to\r
-                                  the panel will be assigned here.\r
-                                  Note that the handle is assigned\r
-                                  after a successful call to\r
-                                  CreateDialogParam and hence would\r
-                                  not be available when handling the\r
-                                  WM_INITDIALOG message from the\r
-                                  dialog procedure.  Only used of\r
-                                  providing a user-interface. */\r
-\r
-    HWND        hwnd_tc;        /*!< Internal use. Do not set */\r
-\r
-    wchar_t    *credtext;       /*!< A brief description of the\r
-                                  current state of this cred\r
-                                  type. (localized, optional).  Only\r
-                                  used if providing a\r
-                                  user-interface. If this field is\r
-                                  non-NULL, then it should point to a\r
-                                  NULL terminated string that does not\r
-                                  exceed ::KHUI_MAXCCH_LONG_DESC\r
-                                  characters in length including the\r
-                                  terminating NULL.\r
-\r
-                                  \see \ref khui_htwnd for information\r
-                                  on how to format the string for this\r
-                                  field.\r
-                                */\r
-\r
-    LPARAM      aux;            /*!< auxilliary field.  For use by the\r
-                                  plug-in. */\r
-} khui_new_creds_by_type;\r
-\r
-/*!\name Flags for khui_new_creds_by_type\r
-\r
-    Note that KHUI_NC_RESPONSE_SUCCESS, KHUI_NC_RESPONSE_FAILED,\r
-    KHUI_NC_RESPONSE_PENDING are also stored in the flags. \r
-\r
-@{*/\r
-#define KHUI_NCT_FLAG_PROCESSED 1024\r
-#define KHUI_NCT_FLAG_DISABLED  2048\r
-/*@}*/\r
-\r
-/*! \brief Width of a new creds dialog panel in dialog units*/\r
-#define NCDLG_WIDTH     300\r
-/*! \brief Height of a new creds dialog panel in dialog units*/\r
-#define NCDLG_HEIGHT    166\r
-\r
-/*! \brief A custom prompt */\r
-typedef struct tag_khui_new_creds_prompt {\r
-    khm_size    index;          /*!< Set to the zero based index\r
-                                  of this prompt. */\r
-\r
-    khm_int32   type;           /*!< one of KHUI_NCPROMPT_TYPE_* */\r
-    wchar_t *   prompt;         /*!< prompt string. Cannot exceed\r
-                                  KHUI_MAXCCH_PROMPT */\r
-    wchar_t *   def;            /*!< default value. Cannot exceed\r
-                                  KHUI_MAXCCH_PROMPT_VALUE */\r
-    wchar_t *   value;          /*!< On completion, this is set to the\r
-                                  value that the user entered. Will\r
-                                  not exceed\r
-                                  KHUI_MAXCCH_PROMPT_VALUE */\r
-\r
-    khm_int32   flags;          /*!< Combination of\r
-                                  KHUI_NCPROMPT_FLAG_* */\r
-\r
-    HWND        hwnd_static;    /* internal use */\r
-    HWND        hwnd_edit;      /* internal use */\r
-} khui_new_creds_prompt;\r
-\r
-/*! \brief The prompt input is hidden\r
-\r
-    The input is hidden for prompts which accept passwords.  The\r
-    control which represents the input will display an asterisk or a\r
-    small circle corresponding to each character typed in, but will\r
-    not show the actual character.\r
- */\r
-#define KHUI_NCPROMPT_FLAG_HIDDEN   1\r
-\r
-/*! \brief Internal use */\r
-#define KHUI_NCPROMPT_FLAG_STOCK    2\r
-\r
-/*! \brief Maximum number of characters in a prompt\r
-\r
-    Refers to the prompt text that accompanies an input control.  THe\r
-    length includes the terminating NULL.\r
- */\r
-#define KHUI_MAXCCH_PROMPT 256\r
-\r
-/*! \brief Maximum number of bytes in a prompt\r
-\r
-    Refers to the prompt text that accompanies an input control.  THe\r
-    length includes the terminating NULL.\r
- */\r
-#define KHUI_MAXCB_PROMPT (KHUI_MAXCCH_PROMPT * sizeof(wchar_t))\r
-\r
-/*! \brief Maximum number of characters that can be entered in an input control\r
-\r
-    Refers to the input control of a prompt.  The length includes the\r
-    terminating NULL.\r
- */\r
-#define KHUI_MAXCCH_PROMPT_VALUE 256\r
-\r
-/*! \brief Maximum number of bytes that can be entered in an input control\r
-\r
-    Refers to the input control of a prompt.  The length includes the\r
-    terminating NULL.\r
- */\r
-#define KHUI_MAXCB_PROMPT_VALUE (KHUI_MAXCCH_PROMPT_VALUE * sizeof(wchar_t))\r
-\r
-/* from krb5.h.  Redefining here because we don't want to depend on\r
-   krb5.h for all credential types */\r
-\r
-/*! \brief A password control */\r
-#define KHUI_NCPROMPT_TYPE_PASSWORD             1\r
-\r
-/*! \brief New password control\r
-\r
-    Used when changing the password\r
- */\r
-#define KHUI_NCPROMPT_TYPE_NEW_PASSWORD         2\r
-\r
-/*! \brief New password again control\r
-\r
-    Used when changing the password\r
- */\r
-#define KHUI_NCPROMPT_TYPE_NEW_PASSWORD_AGAIN   3\r
-\r
-/*! \brief Preauthentication (reserved) */\r
-#define KHUI_NCPROMPT_TYPE_PREAUTH              4\r
-\r
-/*! \brief Control sizes */\r
-typedef enum tag_khui_control_size {\r
-    KHUI_CTRLSIZE_SMALL,\r
-    /*!< A small control fits in about 1/5 the width of the new\r
-      credentials panel */\r
-    KHUI_CTRLSIZE_HALF,\r
-    /*!< Half size controls fit in 1/2 the width of the new\r
-      credentials panel */\r
-    KHUI_CTRLSIZE_FULL,\r
-    /*!< Takes up the whole width of the crednetials panel */\r
-} khui_control_size;\r
-\r
-/*! \brief Internal use */\r
-typedef struct tag_khui_control_row {\r
-    HWND label;\r
-    HWND input;\r
-    khui_control_size size;\r
-} khui_control_row;\r
-\r
-/*! \brief Create a ::khui_new_creds object\r
-\r
-    Creates and initializes a ::khui_new_creds object.  The created\r
-    object must be destroyed using the khui_cw_destroy_cred_blob()\r
-    function.\r
-\r
-    \note Plugins should not call this function directly.  The\r
-         necessary ::khui_new_creds objects will be created by\r
-         NetIDMgr.\r
-\r
-    \see khui_cw_destroy_cred_blob()\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_create_cred_blob(khui_new_creds ** c);\r
-\r
-/*! \brief Destroy a ::khui_new_creds object\r
-\r
-    Destroys a ::khui_new_creds object that was fomerly created using\r
-    a call to khui_cw_create_cred_blob().\r
-\r
-    \note Plugins should not call this function directly.  The\r
-         necessary ::khui_new_creds objects will be created by\r
-         NetIDMgr.\r
-\r
-    \see khui_cw_create_cred_blob()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_destroy_cred_blob(khui_new_creds *c);\r
-\r
-/*! \brief Lock the new_creds object\r
-\r
-    When a plugin is accessing the fields of a ::khui_new_creds\r
-    object, it must first obtain a lock on the object so that other\r
-    threads will not modify the fields at the same time.  Locking the\r
-    object ensures that the fields of the object will be consistent.\r
-\r
-    Use khui_cw_unlock_nc() to undo the lock obtained through a call\r
-    to khui_cw_lock_nc().\r
-\r
-    It is not necessary to lock a new credentials object when\r
-    modifying it using the NetIDMgr API.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_lock_nc(khui_new_creds * c);\r
-\r
-/*! \brief Unlock a new_creds object\r
-\r
-    \see khui_cw_lock_nc()\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_unlock_nc(khui_new_creds * c);\r
-\r
-/*! \brief Add a new panel to a new credentials acquisition window \r
-\r
-    See the description of ::khui_new_cred_panel for information on\r
-    how to populate it to describe a credentials type panel.\r
-\r
-    Note that the structure pointed to by \a t is added by reference.\r
-    The memory pointed to by \a t is not copied.  Hence, the block of\r
-    memory and any other blocks pointed to by the\r
-    ::khui_new_creds_by_type structure located there should remain\r
-    intact for the lifetime of the ::khui_new_creds structure pointed\r
-    to by \a c or until the credentials type panel is removed from the\r
-    ::khui_new_creds structure with a call to khui_cw_del_type().\r
-\r
-    Generally, a plug-in that calls this function should allocate a\r
-    block of memory to contain the ::khui_new_creds_by_type structure,\r
-    fill it in and then pass in the address in a call to\r
-    khui_cw_add_type() while handling a ::KMSG_CRED_PASSWORD,\r
-    ::KMSG_CRED_NEW_CREDS or ::KMSG_CRED_RENEW_CREDS message.  Then\r
-    the plug-in should remove the reference with a call to\r
-    khui_cw_del_type() while processing ::KMSG_CRED_END.\r
-\r
-    \see khui_cw_del_type()\r
-    \see \ref cred_acq_panel_spec\r
-    \see ::khui_new_cred_panel\r
-    \see ::khui_new_creds\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_add_type(khui_new_creds * c, \r
-                 khui_new_creds_by_type * t);\r
-\r
-/*! \brief Remove a panel from a new credentials acquisition window\r
-\r
-    \see khui_cw_add_type()\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_del_type(khui_new_creds * c, \r
-                 khm_int32 type);\r
-\r
-/*! \brief Find the panel belonging to a particular credentials type\r
-\r
-    This panel would have been added to the new credentials window\r
-    using khui_cw_add_type().\r
-\r
-    \see khui_cw_add_type()\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_find_type(khui_new_creds * c, \r
-                  khm_int32 type, \r
-                  khui_new_creds_by_type **t);\r
-\r
-/*! \brief Enable/disable a particular credentials type\r
-\r
-    Enables or disables the panel associated with a particular\r
-    credentials type.  Does not preclude the credentials type from\r
-    participating in the new credentials acquisition.  However, the\r
-    user will be prevented from interacting with the specific panel.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_enable_type(khui_new_creds * c,\r
-                    khm_int32 type,\r
-                    khm_boolean enable);\r
-\r
-/*! \brief Set the primary identity in a new credentials acuisition\r
-\r
-    The primary identity dictates many of the defaults and the\r
-    semantics associated with the credentials acquision process.\r
-    Setting the primary identity also triggers the\r
-    ::WMNC_IDENTITY_CHANGE notification which will be sent to all the\r
-    credentials type panels.\r
-\r
-    Has no effect if the primary identity is already the same as the\r
-    one specified in \a id.  Specify NULL for \a id if the current\r
-    primary identity is to be cleared.\r
-\r
-    If the primary identity is changed, then all the additional\r
-    identities associated with the new credentials acquisition dialog\r
-    will also be discarded.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_set_primary_id(khui_new_creds * c, \r
-                       khm_handle id);\r
-\r
-/*! \brief Add an additional identity to the new credentials acquisition\r
-\r
-    Individual plugins are free to decide how to handle additional\r
-    identities.  Generally, they would attempt to obtain credentials\r
-    for the primary and additional identities, but would not consider\r
-    it an error if an additional identity failed to obtain\r
-    credentials.\r
-\r
-    Calling this function with \a id of NULL does nothing.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_add_identity(khui_new_creds * c, \r
-                     khm_handle id);\r
-\r
-/*! \brief Clear all custom prompts\r
-\r
-    Removes all the custom prompts from the new credentials dialog.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_clear_prompts(khui_new_creds * c);\r
-\r
-/*! \brief Synchronize custom prompt values\r
-\r
-    It is important to synchronize the values before accessing their\r
-    values.  The controls associated with custom prompts update the\r
-    values in the ::khui_new_creds object periodically.  However, the\r
-    values may lose sync intermittently.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_sync_prompt_values(khui_new_creds * c);\r
-\r
-/*! \brief Begin custom prompting\r
-\r
-    Begins the process of defining custom prompts.  Implicity removes\r
-    all the custom prompts that are currently being displayed.  The \a\r
-    banner and \a name will be displayed in separate controls above\r
-    the set of new custom prompts.\r
-\r
-    The controls associated with the prompts will not actually be\r
-    created until all the prompts have been added using\r
-    khui_cw_add_prompt().  The number of promtps that can be added\r
-    will be exactly \a n_prompts.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_begin_custom_prompts(khui_new_creds * c, \r
-                             khm_size n_prompts, \r
-                             wchar_t * banner, \r
-                             wchar_t * name);\r
-\r
-/*! \brief Add a custom prompt\r
-\r
-    After khui_cw_begin_custom_prompts() is called, the plugin should\r
-    call khui_cw_add_prompt() to add the actual prompts.  The number\r
-    of prompts that can be added is the \a n_prompts value specified\r
-    in the earlier call to \a khui_cw_begin_custom_prompts().\r
-\r
-    Once \a n_prompts prompts have been added, the new prompts will\r
-    automatically be created and shown in the user interface.\r
-    However, if less than that prompts are added, nothing is displayed\r
-    to the user.\r
-\r
-    \param[in] c Pointer to ::khui_new_creds structure\r
-\r
-    \param[in] type Type of prompt.  One of\r
-        ::KHUI_NCPROMPT_TYPE_PREAUTH, ::KHUI_NCPROMPT_TYPE_PASSWORD,\r
-        ::KHUI_NCPROMPT_TYPE_NEW_PASSWORD,\r
-        ::KHUI_NCPROMPT_TYPE_NEW_PASSWORD_AGAIN\r
-\r
-    \param[in] prompt Text of the prompt.  Constrained by\r
-        ::KHUI_MAXCCH_PROMPT. (Localized, required)\r
-\r
-    \param[in] def Default value.  (optional).  Constrained by\r
-        ::KHUI_MAXCCH_PROMPT_VALUE.  Set to NULL if not provided.\r
-\r
-    \param[in] flags Flags.  Combination of\r
-        ::KHUI_NCPROMPT_FLAG_HIDDEN\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_add_prompt(khui_new_creds * c, \r
-                   khm_int32 type, \r
-                   wchar_t * prompt, \r
-                   wchar_t * def, \r
-                   khm_int32 flags);\r
-\r
-/*! \brief Retrieve a custom prompt\r
-\r
-    Retrieves an individual prompt.  The \a idx parameter is a\r
-    zero-based index of the prompt to retrieve.  The ordering is the\r
-    same as the order in which khui_cw_add_prompt() was called.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_get_prompt(khui_new_creds * c, \r
-                   khm_size idx, \r
-                   khui_new_creds_prompt ** prompt);\r
-\r
-/*! \brief Get the number of custom prompts\r
-\r
-    Retrieves the number of custom prompts currently displayed.  If\r
-    this function is called between calling\r
-    khui_cw_begin_custom_prompts() and adding all the prompts, the\r
-    number returned will be the number of prompts that is expected to\r
-    be registered (i.e. the \a n_prompts parameter passed to\r
-    khui_cw_begin_custom_prompts()).\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_get_prompt_count(khui_new_creds * c,\r
-                         khm_size * np);\r
-\r
-\r
-/*! \brief Get the value of a custom prompt\r
-\r
-    Retrieve the value of a specific prompt.  The value is the string\r
-    that was typed into the input control associated with a custom\r
-    prompt.  The \a idx parameter is the zero-based index of the\r
-    prompt from which to retrieve the value from.  The ordering is the\r
-    same as the order in which khui_cw_add_prompt() was called.\r
-\r
-    It is important to call khui_cw_sync_prompt_values() before\r
-    starting to call khui_cw_get_prompt_value() so that the values\r
-    returned are up-to-date.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_get_prompt_value(khui_new_creds * c, \r
-                         khm_size idx, \r
-                         wchar_t * buf, \r
-                         khm_size *cbbuf);\r
-\r
-/*! \brief Set the response for a plugin\r
-\r
-    When handling ::KMSG_CRED_DIALOG_PROCESS from within the plugin\r
-    thread, it is important to set the response by calling this\r
-    function.  The response can be used to signal whether the plugin\r
-    successfully obtained credentials or whether further interaction\r
-    is required, or the credentials acquisition failed.\r
-\r
-    The response is a combination of :\r
-    - ::KHUI_NC_RESPONSE_PENDING\r
-    - ::KHUI_NC_RESPONSE_FAILED\r
-    - ::KHUI_NC_RESPONSE_PENDING\r
-    - ::KHUI_NC_RESPONSE_SUCCESS\r
-    - ::KHUI_NC_RESPONSE_NOEXIT\r
-    - ::KHUI_NC_RESPONSE_EXIT\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_cw_set_response(khui_new_creds * c,\r
-                     khm_int32 type,\r
-                     khm_int32 response);\r
-\r
-/*! \brief Check whether a specified credential type panel succeeded\r
-\r
-    This is called during the processing of ::KMSG_CRED_DIALOG_PROCESS\r
-    to determine whether a specified credential type succeeded in\r
-    obtaining credentials.  The credential type that is being queried\r
-    should have also been listed as a dependency when adding the\r
-    current credentials type, otherwise the type queried may not have\r
-    been invoked yet.\r
-\r
-    \return TRUE iff the queried type has reported that it successfully\r
-        completed the credentials acquision operation.\r
- */\r
-KHMEXP khm_boolean KHMAPI \r
-khui_cw_type_succeeded(khui_new_creds * c,\r
-                       khm_int32 type);\r
-\r
-/*! \brief Add a row of controls to the identity specifier area\r
-\r
-    Only for use by identity provider callbacks that wish to add an\r
-    identity selector control.  A row of controls consist of a label\r
-    control and some input control.\r
-\r
-    When the ::WMNC_IDENT_INIT message is sent to the identity\r
-    provider, it receives a handle to the dialog panel in the \a\r
-    lParam parameter which should be the parent window of both the\r
-    windows specified here.  The control ID for any controls created\r
-    must fall within the ::KHUI_CW_ID_MIN and ::KHUI_CW_ID_MAX range.\r
-\r
-    Both controls will be resized to fit in the row.\r
-\r
-    If \a long_label is TRUE then the size of the label will be larger\r
-    than normal and will accomodate more text.\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_cw_add_control_row(khui_new_creds * c,\r
-                        HWND label,\r
-                        HWND input,\r
-                        khui_control_size size);\r
-\r
-/*!@}*/ /* Credentials acquisition */\r
-/*!@}*/\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 switching from basic to advanced mode or vice
+      versa.
+
+      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.  The \a lParam
+      parameter of the message is a pointer to a ::khui_htwnd_link
+      structure describing the link. */
+
+    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. */
+
+    WMNC_UPDATE_LAYOUT,
+    /*!< Update the layout of a dialog or window.  This is an internal
+      message. */
+};
+
+/*! \brief Plugins can use WMNC_NOTIFY message codes from here on up
+
+    \see ::KHUI_WM_NC_NOTIFY
+ */
+#define WMNC_USER 2048
+
+/*! \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;          /*!< Internal use */
+
+    khm_int32   subtype;        /*!< Subtype of the request that is
+                                  being handled through this object.
+                                  One of ::KMSG_CRED_NEW_CREDS,
+                                  ::KMSG_CRED_RENEW_CREDS or
+                                  ::KMSG_CRED_PASSWORD */
+
+    CRITICAL_SECTION cs;        /*!< Internal use */
+
+    khm_boolean set_default;    /*!< After a successfull credentials
+                                  acquisition, set the primary
+                                  identity as the default. */
+
+    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_PROCESS 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 used. */
+
+    /* 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_PROCESS    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
+
+/*! \brief Processing
+
+    This is an internal flag set while the credentials acquisition
+    process is executing.
+ */
+#define KHUI_NC_RESPONSE_PROCESSING 0x00010000
+
+#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
+
+    When processing certain credentials messages such as
+    ::KMSG_CRED_PASSWORD, ::KMSG_CRED_NEW_CREDS,
+    ::KMSG_CRED_RENEW_CREDS, a pointer to a ::khui_new_creds structure
+    will be passed in to the message handler.  If the handler of the
+    message needs to add one or more credentials types as participants
+    of the operation, the handler will need to call khui_cw_add_type()
+    and specify a ::khui_new_creds_by_type structure.
+
+    Note that the memory address passed in to the call to
+    khui_cw_add_type() will not be copied.  Therefore, the block of
+    memory should remain as-is for the lifetime of the
+    ::khui_new_creds structure or until it is removed with a call to
+    khui_cw_del_type().
+
+    Some of the credentials messages that require specifying a
+    ::khui_new_creds_by_type structure require providing a
+    user-interface.  In these cases, the fields marked for providing a
+    UI may be required to hold valid values.  If the message does not
+    require providing a UI, these fields will be ignored.
+*/
+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.  This is a credentials type
+                                  identifier allocated with a call to
+                                  kcdb_credtype_register(). */
+
+    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.  The number
+                                    of valid values in this array
+                                    should be specified in the \a
+                                    n_type_deps field. */
+
+    khm_size    n_type_deps;    /*!< Number of dependencies listed
+                                  above.  Should be between 0 and
+                                  ::KHUI_MAX_TYPE_DEPS.  Specify 0 if
+                                  there are no dependencies. */
+
+    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. Only used if providing a
+                                  user-interface. */
+
+    HICON       icon;           /*!< Icon for the panel (optional).
+                                  Only used if providing a
+                                  user-interface. */
+
+    wchar_t    *tooltip;        /*!< Tooltip for the panel (localized,
+                                  optional).  If NULL, no tooltip will
+                                  be assigned for the panel.  Only
+                                  used if providing a
+                                  user-interface.  */
+
+    HMODULE     h_module;       /*!< Handle to the module containing
+                                  the dialog resource.  Only used if
+                                  providing a user-interface. */
+
+    LPWSTR      dlg_template;   /*!< The dialog resource.  Only used
+                                  if providing a user-interface. */
+    DLGPROC     dlg_proc;       /*!< The dialog procedure. Only used
+                                  if providing a user-interface. */
+
+    HWND        hwnd_panel;     /*!< The dialog window.  Once the
+                                  dialog panel is created, a handle to
+                                  the panel will be assigned here.
+                                  Note that the handle is assigned
+                                  after a successful call to
+                                  CreateDialogParam and hence would
+                                  not be available when handling the
+                                  WM_INITDIALOG message from the
+                                  dialog procedure.  Only used of
+                                  providing a user-interface. */
+
+    HWND        hwnd_tc;        /*!< Internal use. Do not set */
+
+    wchar_t    *credtext;       /*!< A brief description of the
+                                  current state of this cred
+                                  type. (localized, optional).  Only
+                                  used if providing a
+                                  user-interface. If this field is
+                                  non-NULL, then it should point to a
+                                  NULL terminated string that does not
+                                  exceed ::KHUI_MAXCCH_LONG_DESC
+                                  characters in length including the
+                                  terminating NULL.
+
+                                  \see \ref khui_htwnd for information
+                                  on how to format the string for this
+                                  field.
+                                */
+
+    LPARAM      aux;            /*!< auxilliary field.  For use by the
+                                  plug-in. */
+} 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 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.
+
+    Note that the structure pointed to by \a t is added by reference.
+    The memory pointed to by \a t is not copied.  Hence, the block of
+    memory and any other blocks pointed to by the
+    ::khui_new_creds_by_type structure located there should remain
+    intact for the lifetime of the ::khui_new_creds structure pointed
+    to by \a c or until the credentials type panel is removed from the
+    ::khui_new_creds structure with a call to khui_cw_del_type().
+
+    Generally, a plug-in that calls this function should allocate a
+    block of memory to contain the ::khui_new_creds_by_type structure,
+    fill it in and then pass in the address in a call to
+    khui_cw_add_type() while handling a ::KMSG_CRED_PASSWORD,
+    ::KMSG_CRED_NEW_CREDS or ::KMSG_CRED_RENEW_CREDS message.  Then
+    the plug-in should remove the reference with a call to
+    khui_cw_del_type() while processing ::KMSG_CRED_END.
+
+    \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
index c0977b80cb685a635488c3e3cb2e1650d0665f21..a00c65f7fc810109042c3a832846f4d2a5b81b83 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KHPROPS_H\r
-#define __KHIMAIRA_KHPROPS_H\r
-\r
-#include<prsht.h>\r
-\r
-/*********************************************************************\r
-  Property sheets\r
-**********************************************************************/\r
-\r
-/*! \addtogroup khui \r
-\r
-@{*/\r
-\r
-/*!\defgroup khui_pp Property sheets\r
-@{*/\r
-\r
-/* forward dcl */\r
-struct tag_khui_property_page;\r
-\r
-/*! \brief A property sheet\r
- */\r
-typedef struct tag_khui_property_sheet {\r
-  PROPSHEETHEADER header;    /*!< property sheet header */\r
-  khm_int32       status;    /*!< status of property sheet.  One of\r
-                              ::KHUI_PS_STATUS_NONE,\r
-                              ::KHUI_PS_STATUS_RUNNING or\r
-                              ::KHUI_PS_STATUS_DONE */\r
-\r
-  HWND            hwnd;      /*!< handle to the property sheet window.\r
-                              Only valid when \a status is NOT\r
-                              ::KHUI_PS_STATUS_NONE */\r
-\r
-  HWND            hwnd_page; /*!< handle to the current page in the\r
-                              property sheet.  Only valid when \a\r
-                              status is ::KHUI_PS_STATUS_RUNNING */\r
-\r
-  khui_action_context ctx;   /*!< Context for the property sheet.  See\r
-                              documentation for\r
-                              ::khui_action_context */\r
-\r
-  khm_handle      identity;  /*!< Handle to the associated identity,\r
-                               if applicable */\r
-  khm_int32       credtype;  /*!< Type ID of the credentials type, if\r
-                               applicable */\r
-  khm_handle      cred;      /*!< Handle to the associated credential,\r
-                               if applicable */\r
-\r
-  khm_int32       n_pages;   /*!< Number of property pages.\r
-                              Upperbound of ::KHUI_PS_MAX_PSP */\r
-\r
-  QDCL(struct tag_khui_property_page);\r
-} khui_property_sheet;\r
-\r
-/*! \brief The property sheet hasn't been created yet */\r
-#define KHUI_PS_STATUS_NONE 0\r
-\r
-/*! \brief The property sheet is visible and running */\r
-#define KHUI_PS_STATUS_RUNNING 1\r
-\r
-/*! \brief The property sheet has completed running.\r
-\r
-    At this point, it is safe to call khui_ps_destroy_sheet() to\r
-    destroy the property sheet.\r
-*/\r
-#define KHUI_PS_STATUS_DONE 2\r
-\r
-/*! \brief The property sheet is in the process of being destroyed\r
- */\r
-#define KHUI_PS_STATUS_DESTROY 3\r
-\r
-/*! \brief Maximum number of property sheet pages in a property sheet */\r
-#define KHUI_PS_MAX_PSP 16\r
-\r
-\r
-/*! \brief A property sheet page\r
- */\r
-typedef struct tag_khui_property_page {\r
-  HPROPSHEETPAGE h_page;\r
-  LPPROPSHEETPAGE p_page;\r
-  HWND           hwnd;\r
-  khm_int32      credtype;\r
-  khm_int32      ordinal;\r
-\r
-  LDCL(struct tag_khui_property_page);\r
-} khui_property_page;\r
-\r
-/*! \brief Special pseudo credtype for identity page\r
- */\r
-#define KHUI_PPCT_IDENTITY (-8)\r
-\r
-/*! \brief Special pseudo credtype for credential page\r
- */\r
-#define KHUI_PPCT_CREDENTIAL (-9)\r
-\r
-/*! \brief Create a property sheet\r
-\r
-    \note Only called by the NetIDMgr application.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_ps_create_sheet(khui_property_sheet ** sheet);\r
-\r
-/*! \brief Add a page to a property sheet\r
-\r
-    Called by a plugin or the NetIDMgr application to add a page to a\r
-    property sheet.\r
-\r
-    Pages can only be added before the property sheet is made visible\r
-    to the user.\r
-\r
-    \param[in] sheet The property sheet to add the page to\r
-\r
-    \param[in] credtype The credentials type ID of the owner of the\r
-        property page.  This should be set to ::KCDB_CREDTYPE_INVALID\r
-        if the type is not relevant.\r
-\r
-    \param[in] ordinal Requested ordinal.  A positive integer which is\r
-        used to order the pages in a property sheet.  The pages are\r
-        ordered based on ordinal first and then alphabetically by\r
-        credentials type name.  If the type is unavailable, then the\r
-        ordering is undefined.  Ordinals for credential type property\r
-        pages can be in the range from 0 to 127.  Ordinals 128 and\r
-        above are reserved.  Passing in 0 will work for credentials\r
-        providers unless they provide more than one property page per\r
-        credential, in which case the ordinal should be used to\r
-        enforce an order.\r
-\r
-    \param[in] ppage Pointer to structure that will be passed to\r
-        CreatePropertySheetPage() to create the property page.  The\r
-        structure is not managed by NetIDMgr at all, and must exist\r
-        until the status of the property sheet changes to\r
-        ::KHUI_PS_STATUS_RUNNING.  The same pointer will be found in\r
-        the \a p_page member of the ::khui_property_page structure.\r
-\r
-    \param[out] page A pointer will be returned here that will point\r
-        to the newly created khui_property_page structure.  Specify\r
-        NULL if this value is not required.  You can use\r
-        khui_ps_find_page() to retrieve a pointer to the structure\r
-        later.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-khui_ps_add_page(khui_property_sheet * sheet,\r
-                 khm_int32 credtype,\r
-                 khm_int32 ordinal,\r
-                 LPPROPSHEETPAGE ppage,\r
-                 khui_property_page ** page);\r
-\r
-/*! \brief Retrieve a property page structure from a property sheet\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_ps_find_page(khui_property_sheet * sheet,\r
-                  khm_int32 credtype,\r
-                  khui_property_page ** page);\r
-\r
-/*! \brief Display the property sheet\r
-\r
-    \note Only called by the NetIDMgr application\r
- */\r
-KHMEXP HWND KHMAPI \r
-khui_ps_show_sheet(HWND parent, \r
-                   khui_property_sheet * sheet);\r
-\r
-/*! \brief Check if the given message belongs to the property sheet\r
-\r
-    \note Only called by the NetIDMgr application\r
- */\r
-KHMEXP LRESULT KHMAPI \r
-khui_ps_check_message(khui_property_sheet * sheet, \r
-                      PMSG msg);\r
-\r
-/*! \brief Destroy a property sheet and all associated data structures.\r
-\r
-    \note Only called by the NetIDMgr application.\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-khui_ps_destroy_sheet(khui_property_sheet * sheet);\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_property_wnd_set_record(HWND hwnd_pwnd, khm_handle record);\r
-\r
-/*!@}*/\r
-/*!@}*/\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
+
+#include<prsht.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.  Ordinals for credential type property
+        pages can be in the range from 0 to 127.  Ordinals 128 and
+        above are reserved.  Passing in 0 will work for credentials
+        providers unless they provide more than one property page per
+        credential, in which case the ordinal should be used to
+        enforce an order.
+
+    \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
index 5de877da611c1659c3d4e7b85188e39edc8654fc..3a79d65552b7b87d88a11b193e461b42273b4f3d 100644 (file)
@@ -1,84 +1,84 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_REMOTE_H\r
-#define __KHIMAIRA_REMOTE_H\r
-\r
-/*! \addtogroup khui\r
-  @{*/\r
-/*! \defgroup khui_remote Connecting to NetIDMgr from another process\r
-  @{*/\r
-\r
-/* Leash compatibility */\r
-#define ID_OBTAIN_TGT_WITH_LPARAM       32810\r
-\r
-#define KHUI_REQDAEMONWND_CLASS L"IDMgrRequestDaemonCls"\r
-#define KHUI_REQDAEMONWND_NAME  L"IDMgrRequestDaemon"\r
-\r
-#define KHUI_REQD_MAPPING_FORMAT L"Local\\NetIDMgr_DlgInfo_%lu"\r
-\r
-#define NETID_USERNAME_SZ       128\r
-#define NETID_REALM_SZ          192\r
-#define NETID_TITLE_SZ          256\r
-#define NETID_CCACHE_NAME_SZ    264\r
-  \r
-#define NETID_DLGTYPE_TGT      0\r
-#define NETID_DLGTYPE_CHPASSWD 1\r
-typedef struct {\r
-    DWORD size;\r
-    DWORD dlgtype;\r
-    // Tells whether dialog box is in change pwd mode or init ticket mode\r
-    struct {\r
-        WCHAR title[NETID_TITLE_SZ];\r
-        WCHAR username[NETID_USERNAME_SZ];\r
-        WCHAR realm[NETID_REALM_SZ];\r
-        WCHAR ccache[NETID_CCACHE_NAME_SZ];\r
-        DWORD use_defaults;\r
-        DWORD forwardable;\r
-        DWORD noaddresses;\r
-        DWORD lifetime;\r
-        DWORD renew_till;\r
-        DWORD proxiable;\r
-        DWORD publicip;\r
-        DWORD must_use_specified_principal;\r
-    } in;\r
-    struct {\r
-        WCHAR username[NETID_USERNAME_SZ];\r
-        WCHAR realm[NETID_REALM_SZ];\r
-        WCHAR ccache[NETID_CCACHE_NAME_SZ];\r
-    } out;\r
-    // Version 1 of this structure ends here\r
-} NETID_DLGINFO, *LPNETID_DLGINFO;\r
-  \r
-#define NETID_DLGINFO_V1_SZ (10 * sizeof(DWORD) \\r
-         + sizeof(WCHAR) * (NETID_TITLE_SZ + \\r
-         2 * NETID_USERNAME_SZ + 2 * NETID_REALM_SZ + \\r
-         2 * NETID_CCACHE_NAME_SZ))\r
-\r
-/*!@} */\r
-/*!@} */\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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       32810
+
+#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
index eeb4f6585c0bc93f4d9a8d33d44f783a38bec5bf..63baa1f722bebbc8ec61e7e20d3c5f77ef5a5296 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_RESCACHE_H\r
-#define __KHIMAIRA_RESCACHE_H\r
-\r
-#include<khdefs.h>\r
-\r
-KHMEXP void KHMAPI \r
-khui_init_rescache(void);\r
-\r
-KHMEXP void KHMAPI \r
-khui_exit_rescache(void);\r
-\r
-KHMEXP void KHMAPI \r
-khui_cache_bitmap(UINT id, HBITMAP hbm);\r
-\r
-KHMEXP HBITMAP KHMAPI \r
-khui_get_cached_bitmap(UINT id);\r
-\r
-typedef struct khui_ilist_t {\r
-    int cx;\r
-    int cy;\r
-    int n;\r
-    int ng;\r
-    int nused;\r
-    HBITMAP img;\r
-    HBITMAP mask;\r
-    int *idlist;\r
-} khui_ilist;\r
-\r
-typedef struct khui_bitmap_t {\r
-    HBITMAP hbmp;\r
-    int cx;\r
-    int cy;\r
-} khui_bitmap;\r
-\r
-KHMEXP void KHMAPI  \r
-khui_bitmap_from_hbmp(khui_bitmap * kbm, HBITMAP hbm);\r
-\r
-KHMEXP void KHMAPI\r
-khui_delete_bitmap(khui_bitmap * kbm);\r
-\r
-KHMEXP void KHMAPI\r
-khui_draw_bitmap(HDC hdc, int x, int y, khui_bitmap * kbm);\r
-\r
-/* image lists */\r
-KHMEXP khui_ilist * KHMAPI  \r
-khui_create_ilist(int cx, int cy, int n, int ng, int opt);\r
-\r
-KHMEXP BOOL KHMAPI          \r
-khui_delete_ilist(khui_ilist * il);\r
-\r
-KHMEXP int KHMAPI           \r
-khui_ilist_add_masked(khui_ilist * il, HBITMAP hbm, COLORREF cbkg);\r
-\r
-KHMEXP int KHMAPI           \r
-khui_ilist_add_masked_id(khui_ilist *il, HBITMAP hbm, \r
-                         COLORREF cbkg, int id);\r
-\r
-KHMEXP int KHMAPI           \r
-khui_ilist_lookup_id(khui_ilist *il, int id);\r
-\r
-KHMEXP void KHMAPI          \r
-khui_ilist_draw(khui_ilist * il, int idx, HDC dc, int x, int y, int opt);\r
-\r
-KHMEXP void KHMAPI          \r
-khui_ilist_draw_bg(khui_ilist * il, int idx, HDC dc, int x, int y, \r
-                   int opt, COLORREF bgcolor);\r
-\r
-#define khui_ilist_draw_id(il, id, dc, x, y, opt) \\r
-    khui_ilist_draw((il),khui_ilist_lookup_id((il),(id)),(dc),(x),(y),(opt))\r
-\r
-#define KHUI_SMICON_CX 16\r
-#define KHUI_SMICON_CY 16\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
index c12cd4fac6b4196ee23328e5980c8c4b970a8a9e..38be29a134905fe66363058f0ca3300624257a0d 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_TRACKERWND_H\r
-#define __KHIMAIRA_TRACKERWND_H\r
-\r
-#include<time.h>\r
-\r
-/*! \addtogroup khui \r
-\r
-@{ */\r
-\r
-/*!\defgroup khui_trk Duration sliders\r
-\r
-The duration sliders in the UI are pseudo-log-scaled.  This is based\r
-on the assumption that people don't really need 1 minute accuracy when\r
-setting a duration that's several hours long.  As a result, it is\r
-easier to hone in on the duration that you want without having\r
-wizardly mouse maneuvering skillz.\r
-\r
-Following are the duration ranges and the granularity that is offered\r
-in each range:\r
-\r
-<table>\r
-<tr><td> Range     </td><td> Increment</td></tr>\r
-<tr><td> 0..5m     </td><td> 1 min    </td></tr>\r
-<tr><td> 5m..1hr   </td><td> 5 min    </td></tr>\r
-<tr><td> 1hr..4hr  </td><td> 15 min   </td></tr>\r
-<tr><td> 4hr..10hr </td><td> 30 min   </td></tr>\r
-<tr><td> 10hr..24hr</td><td> 1 hr     </td></tr>\r
-<tr><td> 24hr..4d  </td><td> 6 hr     </td></tr>\r
-<tr><td> 4d..      </td><td> 1 day    </td></tr>\r
-</table>\r
-\r
-We don't really adjust for durations over 4 days.  The ranges we are\r
-concerned with don't get much larger.\r
-\r
-For the purpose of writing this piece of code, I have chosen the term\r
-"tick" to refer to a period of granularity.  The number of periods of\r
-granularity (inclusive) within a certain duration interval is referred\r
-to as the number of ticks in the interval.  For example, there are 4\r
-ticks between the interval of 3 minutes to 10 minutes.  Each occuring\r
-at the start of 3min, 4, 5 and 10mins.  And thusly the slider control\r
-will display 4 ticks if it is displaying the interval 3-10mins.\r
-\r
-@{*/\r
-\r
-/*! \brief Tracker data */\r
-typedef struct tag_khui_tracker {\r
-    WNDPROC fn_edit;\r
-    WNDPROC fn_tracker;\r
-    HWND hw_slider;\r
-    HWND hw_edit;\r
-    int lbl_y;\r
-    int lbl_lx;\r
-    int lbl_rx;\r
-    DWORD act_time;\r
-\r
-    time_t current;             /*!< Current selection */\r
-    time_t min;                 /*!< Minimum (inclusive)  */\r
-    time_t max;                 /*!< Maximum (inclusive) */\r
-} khui_tracker;\r
-\r
-/*! \brief Install a tracker into an edit control\r
-\r
-    Once installed, the edit control becomes a duration editor.  The\r
-    tracker data structure that is supplied should remain as is for\r
-    the lifetime of the edit control.\r
-\r
-    The tracker strucutre should have been initialized with a call to\r
-    khui_tracker_initialize() and should have valid values in the \a\r
-    min, \a max and \a current fields.\r
- */\r
-KHMEXP void KHMAPI\r
-khui_tracker_install(HWND hwnd_edit, khui_tracker * tc);\r
-\r
-KHMEXP void KHMAPI\r
-khui_tracker_reposition(khui_tracker * tc);\r
-\r
-KHMEXP void KHMAPI\r
-khui_tracker_initialize(khui_tracker * tc);\r
-\r
-KHMEXP void KHMAPI\r
-khui_tracker_refresh(khui_tracker * tc);\r
-\r
-KHMEXP void KHMAPI\r
-khui_tracker_kill_controls(khui_tracker * tc);\r
-/*!@}*/\r
-/*!@}*/\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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;
+    DWORD act_time;
+
+    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
index 9b3855614c7a945389664759e65ed1de254d191e..16c4474f7afb903abe01286d11759cf768cebb27 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_KHUIDEFS_H\r
-#define __KHIMAIRA_KHUIDEFS_H\r
-\r
-#include<windows.h>\r
-#include<kmq.h>\r
-#include<kcreddb.h>\r
-#include<kherror.h>\r
-#include<kherr.h>\r
-#include<khmsgtypes.h>\r
-\r
-#include<khaction.h>\r
-#include<khactiondef.h>\r
-#include<khhtlink.h>\r
-#include<khnewcred.h>\r
-#include<khprops.h>\r
-#include<khalerts.h>\r
-#include<khconfigui.h>\r
-#include<khtracker.h>\r
-\r
-#ifdef NOEXPORT\r
-#include<khrescache.h>\r
-#endif\r
-\r
-#include<khremote.h>\r
-\r
-/*! \internal */\r
-KHMEXP void KHMAPI\r
-khm_version_init(void);\r
-\r
-/*! \defgroup khui User Interface\r
-\r
-    Functions and data structures for interacting with the user\r
-    interface.\r
-\r
-@{*/\r
-\r
-/*! \brief Get the version of the NetIDMgr library\r
-\r
-    \param[out] libver Receives the version of the library.\r
-\r
-    \param[out] apiver Receives the API version of the library.\r
-        Optional.  Set to NULL if this value is not required.\r
-\r
-    \note When the NetIDMgr framework loads a plugin, it checks the\r
-        version information of the plugin against the version of the\r
-        library to determine if the plugin is compatible.\r
- */\r
-KHMEXP void KHMAPI\r
-khm_get_lib_version(khm_version * libver, khm_ui_4 * apiver);\r
-\r
-/*! \brief Return the version of Common Control library\r
-\r
-    Can be used to check the version of the Windows Common Control\r
-    library that is currently loaded.  The return value of the\r
-    function is the packed version value obatained by the macro :\r
-\r
-    \code\r
-    MAKELONG(vesion->dwMinorVersion, version->dwMajorVersion);\r
-    \endcode\r
-\r
-    The \a pdvi parameter is optional.  Specify NULL if this is not\r
-    required.\r
- */\r
-KHMEXP khm_ui_4 KHMAPI\r
-khm_get_commctl_version(khm_version * pdvi);\r
-\r
-/*! \brief UI callback function\r
-\r
-    Used with khui_request_UI_callback().\r
-\r
-    \see khui_request_UI_callback()\r
- */\r
-typedef khm_int32\r
-(KHMAPI *khm_ui_callback)(HWND hwnd_main_wnd, void * rock);\r
-\r
-/*! \brief Request a UI callback\r
-\r
-    In general, plug-ins in Network Identity Manager run in their own\r
-    thread and will not be able to interact with the user directly by\r
-    creating windows of its own.  There are exceptions to this, such\r
-    as when the plug-in is responding to a new credentials request or\r
-    if the plug-in provides configuration panels.  However, if a\r
-    plug-in needs to provide a user interface to the user outside of\r
-    the provisions already provided by Network Identity Manager, it\r
-    needs to do so from within the user interface thread.\r
-\r
-    To do so, a plug-in would provide a callback function of the type\r
-    ::khm_ui_callback to this function.  The Network Identity Manager\r
-    will then call the callback function from within the user\r
-    interface thread.  At this point, the callback function can create\r
-    any windows it wishes to create and interact with the user\r
-    directly.\r
-\r
-    The callback function would be called synchronously.\r
-    khui_request_UI_callback() will not return until the user\r
-    interface processes the request and calls the callback function.\r
-    The return code of khui_request_UI_callback() will be the return\r
-    code of the callback.\r
-\r
-    \param[in] cb The callback function which will be called from the\r
-        user interface thread.\r
-\r
-    \param[in] rock An arbitrary parameter which will be passed into\r
-        the callback function.\r
-\r
-    \return The return value of \a cb.\r
-\r
-    \note When the plug-in creates any windows, it should specify the\r
-        window handle provided via the \a hwnd_main_wnd parameter as\r
-        the parent window.\r
-\r
-    \see ::khm_ui_callback\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-khui_request_UI_callback(khm_ui_callback cb, void * rock);\r
-\r
-/*!@}*/\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<khhtlink.h>
+#include<khnewcred.h>
+#include<khprops.h>
+#include<khalerts.h>
+#include<khconfigui.h>
+#include<khtracker.h>
+
+#ifdef NOEXPORT
+#include<khrescache.h>
+#endif
+
+#include<khremote.h>
+
+/*! \internal */
+KHMEXP void KHMAPI
+khm_version_init(void);
+
+/*! \defgroup khui User Interface
+
+    Functions and data structures for interacting with the user
+    interface.
+
+@{*/
+
+/*! \brief Get the version of the NetIDMgr library
+
+    \param[out] libver Receives the version of the library.
+
+    \param[out] apiver Receives the API version of the library.
+        Optional.  Set to NULL if this value is not required.
+
+    \note When the NetIDMgr framework loads a plugin, it checks the
+        version information of the plugin against the version of the
+        library to determine if the plugin is compatible.
+ */
+KHMEXP void KHMAPI
+khm_get_lib_version(khm_version * libver, khm_ui_4 * apiver);
+
+/*! \brief Return the version of Common Control library
+
+    Can be used to check the version of the Windows Common Control
+    library that is currently loaded.  The return value of the
+    function is the packed version value obatained by the macro :
+
+    \code
+    MAKELONG(vesion->dwMinorVersion, version->dwMajorVersion);
+    \endcode
+
+    The \a pdvi parameter is optional.  Specify NULL if this is not
+    required.
+ */
+KHMEXP khm_ui_4 KHMAPI
+khm_get_commctl_version(khm_version * pdvi);
+
+/*! \brief UI callback function
+
+    Used with khui_request_UI_callback().
+
+    \see khui_request_UI_callback()
+ */
+typedef khm_int32
+(KHMAPI *khm_ui_callback)(HWND hwnd_main_wnd, void * rock);
+
+/*! \brief Request a UI callback
+
+    In general, plug-ins in Network Identity Manager run in their own
+    thread and will not be able to interact with the user directly by
+    creating windows of its own.  There are exceptions to this, such
+    as when the plug-in is responding to a new credentials request or
+    if the plug-in provides configuration panels.  However, if a
+    plug-in needs to provide a user interface to the user outside of
+    the provisions already provided by Network Identity Manager, it
+    needs to do so from within the user interface thread.
+
+    To do so, a plug-in would provide a callback function of the type
+    ::khm_ui_callback to this function.  The Network Identity Manager
+    will then call the callback function from within the user
+    interface thread.  At this point, the callback function can create
+    any windows it wishes to create and interact with the user
+    directly.
+
+    The callback function would be called synchronously.
+    khui_request_UI_callback() will not return until the user
+    interface processes the request and calls the callback function.
+    The return code of khui_request_UI_callback() will be the return
+    code of the callback.
+
+    \param[in] cb The callback function which will be called from the
+        user interface thread.
+
+    \param[in] rock An arbitrary parameter which will be passed into
+        the callback function.
+
+    \return The return value of \a cb.
+
+    \note When the plug-in creates any windows, it should specify the
+        window handle provided via the \a hwnd_main_wnd parameter as
+        the parent window.
+
+    \see ::khm_ui_callback
+ */
+KHMEXP khm_int32 KHMAPI
+khui_request_UI_callback(khm_ui_callback cb, void * rock);
+
+/*!@}*/
+
+#endif
index 705dd96a391ab6f78b5fcb2136e94b6ad088e89d..55c6cbb1b7e0f1e87adf1e1906aebff0191969d8 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khuidefs.h>\r
-#include<utils.h>\r
-#ifdef DEBUG\r
-#include<assert.h>\r
-#endif\r
-\r
-CRITICAL_SECTION cs_props;\r
-\r
-void\r
-ps_init(void) {\r
-    InitializeCriticalSection(&cs_props);\r
-}\r
-\r
-void\r
-ps_exit(void) {\r
-    DeleteCriticalSection(&cs_props);\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_ps_create_sheet(khui_property_sheet ** sheet)\r
-{\r
-    khui_property_sheet * ps;\r
-\r
-    ps = PMALLOC(sizeof(*ps));\r
-    ZeroMemory(ps, sizeof(*ps));\r
-\r
-    ps->header.dwSize = sizeof(ps->header);\r
-    ps->header.dwFlags = PSH_MODELESS | PSH_PROPTITLE;\r
-    ps->status = KHUI_PS_STATUS_NONE;\r
-\r
-    *sheet = ps;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_ps_add_page(khui_property_sheet * sheet,\r
-                 khm_int32 credtype,\r
-                 khm_int32 ordinal,\r
-                 LPPROPSHEETPAGE ppage,\r
-                 khui_property_page ** page)\r
-{\r
-    khui_property_page * p;\r
-\r
-    p = PMALLOC(sizeof(*p));\r
-    ZeroMemory(p, sizeof(*p));\r
-\r
-    p->credtype = credtype;\r
-    p->ordinal = ordinal;\r
-    p->p_page = ppage;\r
-\r
-    EnterCriticalSection(&cs_props);    \r
-    QPUT(sheet, p);\r
-    sheet->n_pages++;\r
-    LeaveCriticalSection(&cs_props);\r
-\r
-    if(page)\r
-        *page = p;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_ps_find_page(khui_property_sheet * sheet,\r
-                  khm_int32 credtype,\r
-                  khui_property_page ** page)\r
-{\r
-    khui_property_page * p;\r
-\r
-    EnterCriticalSection(&cs_props);\r
-    p = QTOP(sheet);\r
-\r
-    while(p) {\r
-        if(p->credtype == credtype)\r
-            break;\r
-        p = QNEXT(p);\r
-    }\r
-    LeaveCriticalSection(&cs_props);\r
-\r
-    if(p) {\r
-        *page = p;\r
-        return KHM_ERROR_SUCCESS;\r
-    } else {\r
-        *page = NULL;\r
-        return KHM_ERROR_NOT_FOUND;\r
-    }\r
-}\r
-\r
-int __cdecl \r
-ps_order_func(const void *l, const void * r) {\r
-    khui_property_page * lp;\r
-    khui_property_page * rp;\r
-\r
-    lp = *(khui_property_page **)l;\r
-    rp = *(khui_property_page **)r;\r
-\r
-    if (lp->ordinal == rp->ordinal)\r
-        return lp->credtype - rp->credtype;\r
-    else\r
-        return lp->ordinal - rp->ordinal;\r
-}\r
-\r
-KHMEXP HWND KHMAPI \r
-khui_ps_show_sheet(HWND parent, khui_property_sheet * s)\r
-{\r
-    khui_property_page * p;\r
-    HPROPSHEETPAGE phpsp[KHUI_PS_MAX_PSP];\r
-    khui_property_page * ppgs[KHUI_PS_MAX_PSP];\r
-    int i;\r
-    INT_PTR prv;\r
-    HWND hw;\r
-\r
-    EnterCriticalSection(&cs_props);\r
-\r
-    s->header.hwndParent = parent;\r
-    s->header.nPages = s->n_pages;\r
-\r
-    p = QTOP(s);\r
-    i = 0;\r
-    while(p) {\r
-        p->h_page = CreatePropertySheetPage(p->p_page);\r
-#ifdef DEBUG\r
-        assert(p->h_page);\r
-#endif\r
-        ppgs[i++] = p;\r
-        p = QNEXT(p);\r
-    }\r
-\r
-#ifdef DEBUG\r
-    assert(i == s->n_pages);\r
-#endif\r
-\r
-    qsort(ppgs, s->n_pages, sizeof(ppgs[0]), ps_order_func);\r
-\r
-    for (i=0; i < s->n_pages; i++) {\r
-        phpsp[i] = ppgs[i]->h_page;\r
-    }\r
-\r
-    s->header.phpage = phpsp;\r
-\r
-    prv = PropertySheet(&s->header);\r
-\r
-    s->header.phpage = NULL;\r
-\r
-    if(prv <= 0) {\r
-#ifdef DEBUG\r
-        assert(FALSE);\r
-#endif\r
-        /*TODO: better handling for this */\r
-        hw = NULL;\r
-    } else {\r
-        s->status = KHUI_PS_STATUS_RUNNING;\r
-\r
-        hw = (HWND) prv;\r
-        s->hwnd = hw;\r
-        s->hwnd_page = PropSheet_GetCurrentPageHwnd(hw);\r
-    }\r
-    LeaveCriticalSection(&cs_props);\r
-\r
-    return hw;\r
-}\r
-\r
-KHMEXP LRESULT KHMAPI \r
-khui_ps_check_message(khui_property_sheet * sheet, \r
-                      PMSG pmsg)\r
-{\r
-    LRESULT lr;\r
-\r
-    if(sheet->hwnd == NULL)\r
-        return FALSE;\r
-\r
-    lr = PropSheet_IsDialogMessage(sheet->hwnd, pmsg);\r
-    if(lr) {\r
-        sheet->hwnd_page = PropSheet_GetCurrentPageHwnd(sheet->hwnd);\r
-        if(sheet->hwnd_page == NULL && \r
-           sheet->status == KHUI_PS_STATUS_RUNNING)\r
-\r
-            sheet->status = KHUI_PS_STATUS_DONE;\r
-    }\r
-\r
-    return lr;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-khui_ps_destroy_sheet(khui_property_sheet * sheet)\r
-{\r
-    khui_property_page * p;\r
-\r
-    EnterCriticalSection(&cs_props);\r
-\r
-    DestroyWindow(sheet->hwnd);\r
-    sheet->hwnd = NULL;\r
-\r
-    QGET(sheet, &p);\r
-    while(p) {\r
-        PFREE(p);\r
-        QGET(sheet, &p);\r
-    }\r
-    PFREE(sheet);\r
-\r
-    LeaveCriticalSection(&cs_props);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<utils.h>
+#ifdef DEBUG
+#include<assert.h>
+#endif
+
+CRITICAL_SECTION cs_props;
+
+void
+ps_init(void) {
+    InitializeCriticalSection(&cs_props);
+}
+
+void
+ps_exit(void) {
+    DeleteCriticalSection(&cs_props);
+}
+
+KHMEXP khm_int32 KHMAPI 
+khui_ps_create_sheet(khui_property_sheet ** sheet)
+{
+    khui_property_sheet * ps;
+
+    ps = PMALLOC(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 = PMALLOC(sizeof(*p));
+    ZeroMemory(p, sizeof(*p));
+
+    p->credtype = credtype;
+    p->ordinal = ordinal;
+    p->p_page = ppage;
+
+    EnterCriticalSection(&cs_props);    
+    QPUT(sheet, p);
+    sheet->n_pages++;
+    LeaveCriticalSection(&cs_props);
+
+    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;
+
+    EnterCriticalSection(&cs_props);
+    p = QTOP(sheet);
+
+    while(p) {
+        if(p->credtype == credtype)
+            break;
+        p = QNEXT(p);
+    }
+    LeaveCriticalSection(&cs_props);
+
+    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) {
+    khui_property_page * lp;
+    khui_property_page * rp;
+
+    lp = *(khui_property_page **)l;
+    rp = *(khui_property_page **)r;
+
+    if (lp->ordinal == rp->ordinal)
+        return lp->credtype - rp->credtype;
+    else
+        return lp->ordinal - rp->ordinal;
+}
+
+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;
+
+    EnterCriticalSection(&cs_props);
+
+    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;
+        p = QNEXT(p);
+    }
+
+#ifdef DEBUG
+    assert(i == s->n_pages);
+#endif
+
+    qsort(ppgs, s->n_pages, sizeof(ppgs[0]), ps_order_func);
+
+    for (i=0; i < s->n_pages; i++) {
+        phpsp[i] = ppgs[i]->h_page;
+    }
+
+    s->header.phpage = phpsp;
+
+    prv = PropertySheet(&s->header);
+
+    s->header.phpage = NULL;
+
+    if(prv <= 0) {
+#ifdef DEBUG
+        assert(FALSE);
+#endif
+        /*TODO: better handling for this */
+        hw = NULL;
+    } else {
+        s->status = KHUI_PS_STATUS_RUNNING;
+
+        hw = (HWND) prv;
+        s->hwnd = hw;
+        s->hwnd_page = PropSheet_GetCurrentPageHwnd(hw);
+    }
+    LeaveCriticalSection(&cs_props);
+
+    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;
+
+    EnterCriticalSection(&cs_props);
+
+    DestroyWindow(sheet->hwnd);
+    sheet->hwnd = NULL;
+
+    QGET(sheet, &p);
+    while(p) {
+        PFREE(p);
+        QGET(sheet, &p);
+    }
+    PFREE(sheet);
+
+    LeaveCriticalSection(&cs_props);
+
+    return KHM_ERROR_SUCCESS;
+}
index 5ae93a7a5b6b3579f1867808b723bc1c03960960..2116f5ed4df2d2af76b7639b04c55dcefa9b6722 100644 (file)
@@ -1,37 +1,37 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khuidefs.h>\r
-\r
-\r
-#define PW_WM_SET_RECORD WM_USER\r
-\r
-KHMEXP khm_int32 KHMAPI khui_property_wnd_set_record(HWND hwnd_pwnd, khm_handle record)\r
-{\r
-    SendMessage(hwnd_pwnd, PW_WM_SET_RECORD, 0, (LPARAM) record);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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;
+}
index b84608a8e45b2a12d4075cd6bf363fee48dc9df0..3134df825ec350937361f7412f688e5f8f15ebdf 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#define NOEXPORT\r
-\r
-#include<khuidefs.h>\r
-#include<utils.h>\r
-\r
-hashtable * h_bitmaps;\r
-\r
-khm_int32 \r
-hash_id(const void *p) {\r
-#pragma warning(push)\r
-#pragma warning(disable: 4311)\r
-    return (khm_int32) p;\r
-#pragma warning(pop)\r
-}\r
-\r
-khm_int32 \r
-comp_id(const void *p1, const void *p2) {\r
-#pragma warning(push)\r
-#pragma warning(disable: 4311)\r
-    return ((khm_int32)p1) - ((khm_int32)p2);\r
-#pragma warning(pop)\r
-}\r
-\r
-void \r
-del_ref_object(const void *k, void * data) {\r
-    DeleteObject((HGDIOBJ) data);\r
-}\r
-\r
-KHMEXP void KHMAPI \r
-khui_init_rescache(void) {\r
-    h_bitmaps = hash_new_hashtable(127, hash_id, comp_id, NULL, \r
-                                   del_ref_object);\r
-}\r
-\r
-KHMEXP void KHMAPI \r
-khui_exit_rescache(void) {\r
-    hash_del_hashtable(h_bitmaps);\r
-}\r
-\r
-KHMEXP void KHMAPI \r
-khui_cache_bitmap(UINT id, HBITMAP hbm) {\r
-    hash_add(h_bitmaps, (void *)(size_t) id, (void *) hbm);\r
-}\r
-\r
-KHMEXP HBITMAP KHMAPI \r
-khui_get_cached_bitmap(UINT id) {\r
-    return (HBITMAP) hash_lookup(h_bitmaps, (void *)(size_t) id);\r
-}\r
-\r
-KHMEXP khui_ilist * KHMAPI \r
-khui_create_ilist(int cx, int cy, int n, int ng, int opt) {\r
-    BITMAPV5HEADER head;\r
-    HDC hdc;\r
-\r
-    khui_ilist * il = PMALLOC(sizeof(khui_ilist));\r
-    il->cx = cx;\r
-    il->cy = cy;\r
-    il->n = n;\r
-    il->ng = ng;\r
-    il->nused = 0;\r
-    hdc = GetDC(NULL);\r
-    head.bV5Size = sizeof(head);\r
-    head.bV5Width = cx * n;\r
-    head.bV5Height = cy;\r
-    head.bV5Planes = 1;\r
-    head.bV5BitCount = 24;\r
-    head.bV5Compression = BI_RGB;\r
-    head.bV5SizeImage = 0;\r
-    head.bV5XPelsPerMeter = 2835;\r
-    head.bV5YPelsPerMeter = 2835;\r
-    head.bV5ClrUsed = 0;\r
-    head.bV5ClrImportant = 0;\r
-    head.bV5AlphaMask = 0;\r
-    head.bV5CSType = LCS_WINDOWS_COLOR_SPACE;\r
-    head.bV5Intent = LCS_GM_GRAPHICS;\r
-    head.bV5ProfileData = 0;\r
-    head.bV5ProfileSize = 0;\r
-    head.bV5Reserved = 0;\r
-    il->img = CreateDIBitmap(hdc, (BITMAPINFOHEADER *) &head, 0, NULL, NULL, DIB_RGB_COLORS);\r
-    il->mask = CreateBitmap(cx * n, cy, 1, 1, NULL);\r
-    il->idlist = PMALLOC(sizeof(int) * n);\r
-\r
-    return il;\r
-}\r
-\r
-KHMEXP BOOL KHMAPI \r
-khui_delete_ilist(khui_ilist * il) {\r
-    DeleteObject(il->img);\r
-    DeleteObject(il->mask);\r
-    PFREE(il->idlist);\r
-    PFREE(il);\r
-\r
-    return TRUE;\r
-}\r
-\r
-KHMEXP int KHMAPI \r
-khui_ilist_add_masked_id(khui_ilist *il, \r
-                         HBITMAP hbm, \r
-                         COLORREF cbkg, \r
-                         int id) {\r
-    int idx;\r
-\r
-    idx = khui_ilist_add_masked(il,hbm,cbkg);\r
-    if(idx >= 0) {\r
-        il->idlist[idx] = id;\r
-    }\r
-\r
-    return idx;\r
-}\r
-\r
-KHMEXP int KHMAPI \r
-khui_ilist_lookup_id(khui_ilist *il, int id) {\r
-    int i;\r
-\r
-    for(i=0;i<il->nused;i++) {\r
-        if(il->idlist[i] == id)\r
-            return i;\r
-    }\r
-\r
-    return -1;\r
-}\r
-\r
-KHMEXP int KHMAPI \r
-khui_ilist_add_masked(khui_ilist * il, HBITMAP hbm, COLORREF cbkg) {\r
-    HDC dcr,dci,dct,dcb;\r
-    HBITMAP hb_oldb, hb_oldi, hb_oldt;\r
-    int sx, i;\r
-    int x,y;\r
-\r
-    dcr = GetDC(NULL);\r
-    dci = CreateCompatibleDC(dcr);\r
-    dct = CreateCompatibleDC(dcr);\r
-    dcb = CreateCompatibleDC(dcr);\r
-    ReleaseDC(NULL,dcr);\r
-\r
-    i = il->nused++;\r
-    il->idlist[i] = -1;\r
-    sx = i * il->cx;\r
-\r
-    hb_oldb = SelectObject(dcb, hbm);\r
-    hb_oldi = SelectObject(dci, il->img);\r
-    hb_oldt = SelectObject(dct, il->mask);\r
-\r
-    SetBkColor(dct, RGB(0,0,0));\r
-    SetTextColor(dct, RGB(255,255,255));\r
-\r
-    BitBlt(dci, sx, 0, il->cx, il->cy, dcb, 0, 0, SRCCOPY);\r
-    for(y=0;y < il->cy; y++)\r
-        for(x=0; x<il->cx; x++) {\r
-            COLORREF c = GetPixel(dcb, x, y);\r
-            if(c==cbkg) {\r
-                SetPixel(dct, sx + x, y, RGB(255,255,255));\r
-                SetPixel(dci, sx + x, y, RGB(0,0,0));\r
-            } else {\r
-                SetPixel(dct, sx + x, y, RGB(0,0,0));\r
-            }\r
-        }\r
-\r
-    SelectObject(dct, hb_oldt);\r
-    SelectObject(dci, hb_oldi);\r
-    SelectObject(dcb, hb_oldb);\r
-\r
-    DeleteDC(dcb);\r
-    DeleteDC(dct);\r
-    DeleteDC(dci);\r
-\r
-    return i;\r
-}\r
-\r
-KHMEXP void KHMAPI \r
-khui_ilist_draw(khui_ilist * il, \r
-                int idx, \r
-                HDC dc, \r
-                int x, \r
-                int y, \r
-                int opt) {\r
-    HDC dci;\r
-    HBITMAP hb_oldi;\r
-\r
-    if(idx < 0)\r
-        return;\r
-\r
-    dci = CreateCompatibleDC(dc);\r
-\r
-    hb_oldi = SelectObject(dci, il->img);\r
-\r
-    /*BitBlt(dc, x, y, il->cx, il->cy, dci, idx*il->cx, 0, SRCCOPY); */\r
-    MaskBlt(dc, x, y, il->cx, il->cy, dci, idx * il->cx, 0, il->mask, idx * il->cx, 0, MAKEROP4(SRCPAINT, SRCCOPY));\r
-/*    MaskBlt(dc, x, y, il->cx, il->cy, dci, idx * il->cx, 0, il->mask, idx * il->cx, 0, MAKEROP4(SRCINVERT, SRCCOPY)); */\r
-\r
-    SelectObject(dci, hb_oldi);\r
-\r
-    DeleteDC(dci);\r
-}\r
-\r
-KHMEXP void KHMAPI \r
-khui_ilist_draw_bg(khui_ilist * il, \r
-                   int idx, \r
-                   HDC dc, \r
-                   int x, \r
-                   int y, \r
-                   int opt, \r
-                   COLORREF bgcolor) {\r
-    HDC dcm;\r
-    HBITMAP hb_oldm, hb_mem;\r
-    HBRUSH hbr;\r
-    RECT r;\r
-\r
-    dcm = CreateCompatibleDC(dc);\r
-\r
-    hb_mem = CreateCompatibleBitmap(dc, il->cx, il->cy);\r
-\r
-    hb_oldm = SelectObject(dcm, hb_mem);\r
-\r
-    hbr = CreateSolidBrush(bgcolor);\r
-\r
-    r.left = 0;\r
-    r.top = 0;\r
-    r.right = il->cx;\r
-    r.bottom = il->cy;\r
-\r
-    FillRect(dcm, &r, hbr);\r
-\r
-    khui_ilist_draw(il,idx,dcm,0,0,opt);\r
-\r
-    BitBlt(dc,x,y,il->cx,il->cy,dcm,0,0,SRCCOPY);\r
-\r
-    SelectObject(dcm, hb_oldm);\r
-    \r
-    DeleteObject(hb_mem);\r
-    DeleteObject(hbr);\r
-\r
-    DeleteDC(dcm);\r
-}\r
-\r
-\r
-KHMEXP void KHMAPI \r
-khui_bitmap_from_hbmp(khui_bitmap * kbm, HBITMAP hbm)\r
-{\r
-    HDC hdc;\r
-    BITMAPINFO bmi;\r
-\r
-    hdc = CreateCompatibleDC(NULL);\r
-\r
-    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);\r
-\r
-    kbm->hbmp = hbm;\r
-\r
-    if(GetDIBits(hdc, hbm, 0, 0, NULL, &bmi, DIB_RGB_COLORS)) {\r
-        kbm->cx = bmi.bmiHeader.biWidth;\r
-        kbm->cy = bmi.bmiHeader.biHeight;\r
-    } else {\r
-        kbm->cx = -1;\r
-        kbm->cy = -1;\r
-    }\r
-\r
-    DeleteDC(hdc);\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_delete_bitmap(khui_bitmap * kbm) {\r
-    if (kbm->hbmp)\r
-        DeleteObject(kbm->hbmp);\r
-    kbm->hbmp = NULL;\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_draw_bitmap(HDC hdc, int x, int y, khui_bitmap * kbm) {\r
-    HDC hdcb = CreateCompatibleDC(hdc);\r
-    HBITMAP hbmold = SelectObject(hdcb, kbm->hbmp);\r
-\r
-    BitBlt(hdc, x, y, kbm->cx, kbm->cy,\r
-           hdcb, 0, 0, SRCCOPY);\r
-\r
-    SelectObject(hdcb, hbmold);\r
-    DeleteDC(hdcb);\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<utils.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 = PMALLOC(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 = PMALLOC(sizeof(int) * n);
+
+    return il;
+}
+
+KHMEXP BOOL KHMAPI 
+khui_delete_ilist(khui_ilist * il) {
+    DeleteObject(il->img);
+    DeleteObject(il->mask);
+    PFREE(il->idlist);
+    PFREE(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);
+}
index b0da7d15d668f439ed13837ea5361a093c99405e..1ec2fda28e3e732fca9dc2c7d0c06bd29c4e835d 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khuidefs.h>\r
-#include<commctrl.h>\r
-#include<assert.h>\r
-\r
-#define K5_SLIDER_WIDTH 208\r
-#define K5_SLIDER_HEIGHT 40\r
-\r
-#define K5_SLIDER_LBL_HPAD   5\r
-#define K5_SLIDER_LBL_VPAD  22\r
-\r
-#define KHUI_TRACKER_PROP L"KhmTrackerData"\r
-\r
-\r
-/* Count the number of ticks between tmin and tmax, inclusive\r
-*/\r
-int time_t_to_ticks(time_t tmin, time_t tmax)\r
-{\r
-    int c = 0;\r
-    time_t lo, hi;\r
-\r
-    tmin -= tmin % 60; /* our smallest gran is 1 min */\r
-    if(tmax % 60)\r
-        tmax += 60 - (tmax % 60);\r
-\r
-    lo = tmin;\r
-\r
-#define TFORWARD(limit,gran) \\r
-    if(lo < limit && lo < tmax) { \\r
-        hi = min(tmax, limit); \\r
-        c += (int)((hi - lo) / (gran)); \\r
-        lo = hi; \\r
-    }\r
-\r
-    TFORWARD(300,60);\r
-    TFORWARD(3600,300);\r
-    TFORWARD(3600*4, 60*15);\r
-    TFORWARD(3600*10,60*30);\r
-    TFORWARD(3600*24,3600);\r
-    TFORWARD(3600*24*4,3600*6);\r
-    TFORWARD(((time_t)(INFINITE & INT_MAX)),3600*24);\r
-\r
-#undef TFORWARD\r
-\r
-    return c;\r
-}\r
-\r
-/* Compute tmax given tmin and ticks such that there are ticks ticks\r
-   between tmin and tmax\r
-   */\r
-time_t ticks_to_time_t(int ticks, time_t tmin)\r
-{\r
-    int c = 0;\r
-    tmin -= tmin % 60; /* our smallest gran is 1 min */\r
-\r
-#define SFORWARD(limit,gran) \\r
-    if(tmin < limit && ticks > 0) { \\r
-        c = (int) min(ticks, (limit - tmin) / (gran)); \\r
-        tmin += c * gran; \\r
-        ticks -= c; \\r
-    }\r
-\r
-    SFORWARD(300,60);\r
-    SFORWARD(3600,300);\r
-    SFORWARD(3600*4,60*15);\r
-    SFORWARD(3600*10,60*30);\r
-    SFORWARD(3600*24,3600);\r
-    SFORWARD(3600*24*4,3600*6);\r
-    SFORWARD(((time_t)(INFINITE & INT_MAX)),3600*24);\r
-\r
-#undef SFORWARD\r
-\r
-    return tmin;\r
-}\r
-\r
-/*  Prep a tracker control which works in conjunction with the\r
-    duration edit control.\r
-\r
-    NOTE: Runs in the context of the UI thread\r
-*/\r
-void \r
-initialize_tracker(HWND hwnd, \r
-                   khui_tracker * tc)\r
-{\r
-    RECT r;\r
-    FILETIME ft;\r
-    wchar_t wbuf[256];\r
-    khm_size cbbuf;\r
-\r
-    SendMessage(tc->hw_slider, TBM_SETRANGE, 0, MAKELONG(0, time_t_to_ticks(tc->min, tc->max)));\r
-    SendMessage(tc->hw_slider, TBM_SETPOS, TRUE, (LPARAM) time_t_to_ticks(tc->min, tc->current));\r
-\r
-    r.left = K5_SLIDER_LBL_HPAD;\r
-    r.top = K5_SLIDER_LBL_VPAD;\r
-    r.right = K5_SLIDER_WIDTH - K5_SLIDER_LBL_HPAD;\r
-    r.bottom = r.top;\r
-\r
-    MapDialogRect(hwnd, &r);\r
-\r
-    tc->lbl_y = r.top;\r
-    tc->lbl_lx = r.left;\r
-    tc->lbl_rx = r.right;\r
-\r
-    TimetToFileTimeInterval(tc->current, &ft);\r
-    cbbuf = sizeof(wbuf);\r
-    FtIntervalToString(&ft, wbuf, &cbbuf);\r
-\r
-    SendMessage(tc->hw_edit, WM_SETTEXT, 0, (LPARAM)wbuf);\r
-}\r
-\r
-\r
-/* We instance-subclass each tracker control to provide the\r
-   functionality that we need.  This is the replacement window\r
-   procedure\r
-\r
-   NOTE: Runs in the context of the UI thread\r
-   */\r
-LRESULT CALLBACK \r
-duration_tracker_proc(HWND hwnd,\r
-                      UINT uMsg,\r
-                      WPARAM wParam,\r
-                      LPARAM lParam)\r
-{\r
-    khui_tracker * tc;\r
-\r
-    tc = (khui_tracker *) GetProp(hwnd, KHUI_TRACKER_PROP);\r
-#ifdef DEBUG\r
-    assert(tc != NULL);\r
-#endif\r
-\r
-    switch(uMsg) {\r
-    case WM_PAINT:\r
-        {\r
-            HDC hdc;\r
-            HFONT hf, hfold;\r
-            LRESULT lr;\r
-            FILETIME ft;\r
-            wchar_t buf[256];\r
-            khm_size cbbuf;\r
-\r
-            lr = CallWindowProc(tc->fn_tracker, hwnd, uMsg, wParam, lParam);\r
-\r
-            /* Can't use BeginPaint here, since we already called the\r
-               window proc */\r
-            hdc = GetWindowDC(hwnd);\r
-\r
-            hf = (HFONT) SendMessage(tc->hw_edit, WM_GETFONT, 0, 0);\r
-\r
-            hfold = ((HFONT) SelectObject((hdc), (HGDIOBJ)(HFONT)(hf)));\r
-\r
-            TimetToFileTimeInterval(tc->min, &ft);\r
-            cbbuf = sizeof(buf);\r
-            FtIntervalToString(&ft, buf, &cbbuf);\r
-\r
-            SetTextColor(hdc, RGB(0,0,0));\r
-            SetBkMode(hdc, TRANSPARENT);\r
-\r
-            SetTextAlign(hdc, TA_LEFT | TA_TOP | TA_NOUPDATECP);\r
-            TextOut(hdc, tc->lbl_lx, tc->lbl_y, buf, (int) wcslen(buf));\r
-                \r
-            TimetToFileTimeInterval(tc->max, &ft);\r
-            cbbuf = sizeof(buf);\r
-            FtIntervalToString(&ft, buf, &cbbuf);\r
-\r
-            SetTextAlign(hdc, TA_RIGHT | TA_TOP | TA_NOUPDATECP);\r
-            TextOut(hdc, tc->lbl_rx, tc->lbl_y, buf, (int) wcslen(buf));\r
-\r
-            ((HFONT) SelectObject((hdc), (HGDIOBJ)(HFONT)(hfold)));\r
-\r
-            ReleaseDC(hwnd, hdc);\r
-                \r
-            return lr;\r
-        }\r
-        break;\r
-\r
-    case WM_KILLFOCUS:\r
-        {\r
-            if((HWND)wParam != tc->hw_edit)\r
-                ShowWindow(hwnd, SW_HIDE);\r
-        }\r
-        break;\r
-\r
-    case WM_LBUTTONUP:\r
-    case WM_MOUSEMOVE:\r
-        {\r
-            LRESULT lr;\r
-\r
-            lr = CallWindowProc(tc->fn_tracker, hwnd, uMsg, wParam, lParam);\r
-\r
-            if(wParam & MK_LBUTTON) {\r
-                int c = (int) SendMessage(hwnd, TBM_GETPOS, 0, 0);\r
-                time_t t = ticks_to_time_t(c, tc->min);\r
-\r
-                if(t != tc->current) {\r
-                    wchar_t buf[256];\r
-                    FILETIME ft;\r
-                    khm_size cbbuf;\r
-\r
-                    tc->current = t;\r
-                    //d->dirty = TRUE;\r
-                    cbbuf = sizeof(buf);\r
-                    TimetToFileTimeInterval(t, &ft);\r
-                    FtIntervalToString(&ft, buf, &cbbuf);\r
-                    SendMessage(tc->hw_edit, WM_SETTEXT, 0, (LPARAM) buf);\r
-                }\r
-            }\r
-            return lr;\r
-        }\r
-    }\r
-\r
-    return CallWindowProc(tc->fn_tracker, hwnd, uMsg, wParam, lParam);\r
-}\r
-\r
-\r
-/* Create the subclassed duration slider on behalf of an edit control */\r
-void \r
-create_edit_sliders(HWND hwnd, \r
-                       HWND hwnd_dlg, \r
-                       khui_tracker * tc)\r
-{\r
-    RECT r;\r
-    RECT rs;\r
-\r
-    GetWindowRect(hwnd, &r);\r
-\r
-    rs.top = 0;\r
-    rs.left = 0;\r
-    rs.right = K5_SLIDER_WIDTH;\r
-    rs.bottom = K5_SLIDER_HEIGHT;\r
-    MapDialogRect(hwnd_dlg, &rs);\r
-    rs.right -= rs.left;\r
-    rs.bottom -= rs.top;\r
-\r
-    tc->hw_slider = \r
-        CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,\r
-                       TRACKBAR_CLASS,\r
-                       L"NetIDMgrTimeTickerTrackbar",\r
-                       WS_POPUP | TBS_AUTOTICKS | TBS_BOTTOM |\r
-#if (_WIN32_IE >= 0x0501)\r
-                       TBS_DOWNISLEFT | \r
-#endif\r
-                       TBS_HORZ | WS_CLIPCHILDREN,\r
-                       r.left,r.bottom,rs.right,rs.bottom,\r
-                       hwnd,\r
-                       NULL,\r
-                       (HINSTANCE)(DWORD_PTR) \r
-                       GetWindowLongPtr(hwnd, GWLP_HINSTANCE),\r
-                       NULL);\r
-\r
-    SetProp(tc->hw_slider, KHUI_TRACKER_PROP,\r
-            (HANDLE) tc);\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-    tc->fn_tracker = (WNDPROC)(LONG_PTR) SetWindowLongPtr(tc->hw_slider, GWLP_WNDPROC, (LONG_PTR) duration_tracker_proc);\r
-#pragma warning(pop)\r
-}\r
-\r
-/*  An edit control is instance-subclassed to create an edit control\r
-    that holds a duration.  Welcome to the window procedure.\r
-\r
-    NOTE: Runs in the context of the UI thread\r
-    */\r
-LRESULT CALLBACK \r
-duration_edit_proc(HWND hwnd,\r
-                   UINT uMsg,\r
-                   WPARAM wParam,\r
-                   LPARAM lParam)\r
-{\r
-    khui_tracker * tc;\r
-\r
-    tc = (khui_tracker *) GetProp(hwnd, KHUI_TRACKER_PROP);\r
-\r
-#ifdef DEBUG\r
-    assert(tc != NULL);\r
-#endif\r
-\r
-    switch(uMsg) {\r
-    case WM_SETFOCUS:\r
-        {\r
-            HWND p;\r
-\r
-            p = GetParent(hwnd);\r
-\r
-            /* we are being activated. */\r
-            if(tc->hw_slider == NULL) {\r
-                create_edit_sliders(hwnd, p, tc);\r
-                initialize_tracker(p, tc);\r
-            }\r
-\r
-            khui_tracker_reposition(tc);\r
-\r
-#ifdef SHOW_PANEL_ON_FIRST_ACTIVATE\r
-            ShowWindow(tc->hw_slider, SW_SHOWNOACTIVATE);\r
-#endif\r
-\r
-            tc->act_time = GetTickCount();\r
-        }\r
-        break;\r
-\r
-    case WM_KILLFOCUS:\r
-        {\r
-            wchar_t wbuf[256];\r
-            FILETIME ft;\r
-            khm_size cbbuf;\r
-\r
-            if((HWND) wParam != tc->hw_slider)\r
-                ShowWindow(tc->hw_slider, SW_HIDE);\r
-\r
-            TimetToFileTimeInterval(tc->current, &ft);\r
-            cbbuf = sizeof(wbuf);\r
-            FtIntervalToString(&ft, wbuf, &cbbuf);\r
-\r
-            SendMessage(tc->hw_edit, WM_SETTEXT, 0, (LPARAM)wbuf);\r
-        }\r
-        break;\r
-\r
-    case KHUI_WM_NC_NOTIFY:\r
-        if(HIWORD(wParam) == WMNC_DIALOG_SETUP) {\r
-            HWND p;\r
-\r
-            p = GetParent(hwnd);\r
-\r
-            if(tc->hw_slider == NULL) {\r
-                create_edit_sliders(hwnd,p,tc);\r
-            }\r
-\r
-            initialize_tracker(p, tc);\r
-        }\r
-        return TRUE;\r
-\r
-    case WM_LBUTTONUP:\r
-        if (IsWindowVisible(tc->hw_slider)) {\r
-            DWORD tm;\r
-\r
-            tm = GetTickCount();\r
-            if (tm - tc->act_time > 000)\r
-                ShowWindow(tc->hw_slider, SW_HIDE);\r
-        } else {\r
-            ShowWindow(tc->hw_slider, SW_SHOWNOACTIVATE);\r
-        }\r
-        break;\r
-\r
-        /*  these messages can potentially change the text in the edit\r
-            control.  We intercept them and see what changed.  We may\r
-            need to grab and handle them */\r
-    case EM_REPLACESEL:\r
-    case EM_UNDO:\r
-    case WM_UNDO:\r
-    case WM_CHAR:\r
-#if (_WIN32_WINNT >= 0x0501)\r
-    case WM_UNICHAR:\r
-#endif\r
-        {\r
-            wchar_t buf[256];\r
-            size_t nchars;\r
-            time_t ts;\r
-            FILETIME ft;\r
-            BOOL modified;\r
-            LRESULT lr = CallWindowProc(tc->fn_edit, hwnd, uMsg, wParam, lParam);\r
-\r
-            modified = (BOOL) SendMessage(hwnd, EM_GETMODIFY, 0, 0);\r
-            if(modified) {\r
-                /* parse the string */\r
-                if(nchars = (size_t) SendMessage(hwnd, WM_GETTEXT, ARRAYLENGTH(buf), (LPARAM) buf)) {\r
-                    buf[nchars] = 0;\r
-\r
-                    if(KHM_SUCCEEDED(IntervalStringToFt(&ft, buf))) {\r
-                        ts = FtIntervalToSeconds(&ft);\r
-                        if(ts >= tc->min && ts <= tc->max) {\r
-                            tc->current = ts;\r
-                            //d->dirty = TRUE;\r
-                            if(tc->hw_slider != NULL)\r
-                                SendMessage(tc->hw_slider, TBM_SETPOS, TRUE, (LPARAM) time_t_to_ticks(tc->min, tc->current));\r
-                        }\r
-                    }\r
-                }\r
-                SendMessage(hwnd, EM_SETMODIFY, FALSE, 0);\r
-            }\r
-\r
-            return lr;\r
-        }\r
-    }\r
-\r
-    return CallWindowProc(tc->fn_edit, hwnd, uMsg, wParam, lParam);\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_tracker_install(HWND hwnd_edit, khui_tracker * tc) {\r
-#ifdef DEBUG\r
-    assert(hwnd_edit);\r
-    assert(tc);\r
-#endif\r
-\r
-    tc->hw_edit = hwnd_edit;\r
-\r
-    SetProp(hwnd_edit, KHUI_TRACKER_PROP, (HANDLE) tc);\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable: 4244)\r
-    tc->fn_edit = (WNDPROC)(LONG_PTR) \r
-        SetWindowLongPtr(hwnd_edit, GWLP_WNDPROC, \r
-                         (LONG_PTR) duration_edit_proc);\r
-#pragma warning(pop)\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_tracker_reposition(khui_tracker * tc) {\r
-    RECT r;\r
-\r
-    if(tc->hw_slider && tc->hw_edit) {\r
-        GetWindowRect(tc->hw_edit, &r);\r
-        SetWindowPos(tc->hw_slider,\r
-                     NULL,\r
-                     r.left, r.bottom, \r
-                     0, 0, \r
-                     SWP_NOOWNERZORDER | SWP_NOSIZE | \r
-                     SWP_NOZORDER | SWP_NOACTIVATE);\r
-    }\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_tracker_initialize(khui_tracker * tc) {\r
-    ZeroMemory(tc, sizeof(*tc));\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_tracker_refresh(khui_tracker * tc) {\r
-    if (!tc->hw_edit)\r
-        return;\r
-\r
-    SendMessage(tc->hw_edit,\r
-                KHUI_WM_NC_NOTIFY, \r
-                MAKEWPARAM(0,WMNC_DIALOG_SETUP), 0);\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khui_tracker_kill_controls(khui_tracker * tc) {\r
-    if (tc->hw_slider)\r
-        DestroyWindow(tc->hw_slider);\r
-    if (tc->hw_edit)\r
-        DestroyWindow(tc->hw_edit);\r
-    tc->hw_slider = NULL;\r
-    tc->hw_edit = NULL;\r
-    tc->fn_edit = NULL;\r
-    tc->fn_tracker = NULL;\r
-}\r
-\r
-\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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 |
+#if (_WIN32_IE >= 0x0501)
+                       TBS_DOWNISLEFT | 
+#endif
+                       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. */
+            if(tc->hw_slider == NULL) {
+                create_edit_sliders(hwnd, p, tc);
+                initialize_tracker(p, tc);
+            }
+
+            khui_tracker_reposition(tc);
+
+#ifdef SHOW_PANEL_ON_FIRST_ACTIVATE
+            ShowWindow(tc->hw_slider, SW_SHOWNOACTIVATE);
+#endif
+
+            tc->act_time = GetTickCount();
+        }
+        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;
+
+    case WM_LBUTTONUP:
+        if (IsWindowVisible(tc->hw_slider)) {
+            DWORD tm;
+
+            tm = GetTickCount();
+            if (tm - tc->act_time > 000)
+                ShowWindow(tc->hw_slider, SW_HIDE);
+        } else {
+            ShowWindow(tc->hw_slider, SW_SHOWNOACTIVATE);
+        }
+        break;
+
+        /*  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:
+#if (_WIN32_WINNT >= 0x0501)
+    case WM_UNICHAR:
+#endif
+        {
+            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;
+}
+
+
index 11da4e4bfa8368af47ec9f87b5f334d821063f24..be183b68f836bb0f5c9388861817c90c68a68e00 100644 (file)
@@ -1,59 +1,59 @@
-/*\r
- * Copyright (c) 2007 Secure Endpoints Inc.\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khuidefs.h>\r
-#include<intaction.h>\r
-\r
-#ifdef DEBUG\r
-#include <assert.h>\r
-#endif\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-khui_request_UI_callback(khm_ui_callback cb, void * rock) {\r
-\r
-    khui_ui_callback_data cbdata;\r
-\r
-#ifdef DEBUG\r
-    assert(khui_hwnd_main);\r
-#endif\r
-\r
-    if (khui_hwnd_main == NULL)\r
-        return KHM_ERROR_NOT_READY;\r
-\r
-    ZeroMemory(&cbdata, sizeof(cbdata));\r
-    cbdata.magic = KHUI_UICBDATA_MAGIC;\r
-    cbdata.cb = cb;\r
-    cbdata.rock = rock;\r
-    cbdata.rv = KHM_ERROR_NOT_IMPLEMENTED;\r
-\r
-    SendMessage(khui_hwnd_main, WM_COMMAND,\r
-                MAKEWPARAM(KHUI_ACTION_UICB, 0),\r
-                (LPARAM) &cbdata);\r
-\r
-    return cbdata.rv;\r
-}\r
-\r
-\r
+/*
+ * Copyright (c) 2007 Secure Endpoints Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<intaction.h>
+
+#ifdef DEBUG
+#include <assert.h>
+#endif
+
+KHMEXP khm_int32 KHMAPI
+khui_request_UI_callback(khm_ui_callback cb, void * rock) {
+
+    khui_ui_callback_data cbdata;
+
+#ifdef DEBUG
+    assert(khui_hwnd_main);
+#endif
+
+    if (khui_hwnd_main == NULL)
+        return KHM_ERROR_NOT_READY;
+
+    ZeroMemory(&cbdata, sizeof(cbdata));
+    cbdata.magic = KHUI_UICBDATA_MAGIC;
+    cbdata.cb = cb;
+    cbdata.rock = rock;
+    cbdata.rv = KHM_ERROR_NOT_IMPLEMENTED;
+
+    SendMessage(khui_hwnd_main, WM_COMMAND,
+                MAKEWPARAM(KHUI_ACTION_UICB, 0),
+                (LPARAM) &cbdata);
+
+    return cbdata.rv;
+}
+
+
index 4d0b012f11f8adbff78755c3055878e81e5ba98b..5c13ad424c13511f9e54df725ba77aca45a41f0c 100644 (file)
@@ -1,44 +1,44 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<khuidefs.h>\r
-\r
-extern void alert_init(void);\r
-extern void alert_exit(void);\r
-extern void ps_init(void);\r
-extern void ps_exit(void);\r
-\r
-void\r
-uilib_process_attach(void) {\r
-    alert_init();\r
-    ps_init();\r
-}\r
-\r
-void\r
-uilib_process_detach(void) {\r
-    ps_exit();\r
-    alert_exit();\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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);
+extern void ps_init(void);
+extern void ps_exit(void);
+
+void
+uilib_process_attach(void) {
+    alert_init();
+    ps_init();
+}
+
+void
+uilib_process_detach(void) {
+    ps_exit();
+    alert_exit();
+}
index cf7f702bf15af12a3832d651be7333c55792184b..6e3e48fe63623c7693f3c47899d167e2cc8bf230 100644 (file)
@@ -1,83 +1,83 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<shlwapi.h>\r
-#include<khuidefs.h>\r
-#include<netidmgr_intver.h>\r
-\r
-DLLVERSIONINFO ver_commctl;\r
-\r
-static void\r
-get_dll_version(wchar_t * dllname, DLLVERSIONINFO * pdvi) {\r
-    HINSTANCE hdll;\r
-\r
-    hdll = LoadLibrary(dllname);\r
-\r
-    ZeroMemory(pdvi, sizeof(*pdvi));\r
-\r
-    if(hdll) {\r
-        DLLGETVERSIONPROC pDllGetVersion;\r
-\r
-        pDllGetVersion = (DLLGETVERSIONPROC) GetProcAddress(hdll, "DllGetVersion");\r
-        if(pDllGetVersion) {\r
-            pdvi->cbSize = sizeof(*pdvi);\r
-\r
-            (*pDllGetVersion)(pdvi);\r
-        }\r
-        FreeLibrary(hdll);\r
-    }\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khm_version_init(void) {\r
-    get_dll_version(L"comctl32.dll", &ver_commctl);\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-khm_get_lib_version(khm_version * libver, khm_ui_4 * apiver) {\r
-    if (!libver)\r
-        return;\r
-\r
-    libver->major = KH_VERSION_MAJOR;\r
-    libver->minor = KH_VERSION_MINOR;\r
-    libver->patch = KH_VERSION_PATCH;\r
-    libver->aux = KH_VERSION_AUX;\r
-\r
-    if (apiver)\r
-        *apiver = KH_VERSION_API;\r
-}\r
-\r
-KHMEXP khm_ui_4 KHMAPI\r
-khm_get_commctl_version(khm_version * pdvi) {\r
-    if (pdvi) {\r
-        pdvi->major = (khm_ui_2) ver_commctl.dwMajorVersion;\r
-        pdvi->minor = (khm_ui_2) ver_commctl.dwMinorVersion;\r
-        pdvi->patch = (khm_ui_2) ver_commctl.dwBuildNumber;\r
-        pdvi->aux =   (khm_ui_2) ver_commctl.dwPlatformID;\r
-    }\r
-\r
-    return MAKELONG(ver_commctl.dwMinorVersion, ver_commctl.dwMajorVersion);\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<shlwapi.h>
+#include<khuidefs.h>
+#include<netidmgr_intver.h>
+
+DLLVERSIONINFO ver_commctl;
+
+static void
+get_dll_version(wchar_t * dllname, DLLVERSIONINFO * pdvi) {
+    HINSTANCE hdll;
+
+    hdll = LoadLibrary(dllname);
+
+    ZeroMemory(pdvi, sizeof(*pdvi));
+
+    if(hdll) {
+        DLLGETVERSIONPROC pDllGetVersion;
+
+        pDllGetVersion = (DLLGETVERSIONPROC) GetProcAddress(hdll, "DllGetVersion");
+        if(pDllGetVersion) {
+            pdvi->cbSize = sizeof(*pdvi);
+
+            (*pDllGetVersion)(pdvi);
+        }
+        FreeLibrary(hdll);
+    }
+}
+
+KHMEXP void KHMAPI
+khm_version_init(void) {
+    get_dll_version(L"comctl32.dll", &ver_commctl);
+}
+
+KHMEXP void KHMAPI
+khm_get_lib_version(khm_version * libver, khm_ui_4 * apiver) {
+    if (!libver)
+        return;
+
+    libver->major = KH_VERSION_MAJOR;
+    libver->minor = KH_VERSION_MINOR;
+    libver->patch = KH_VERSION_PATCH;
+    libver->aux = KH_VERSION_AUX;
+
+    if (apiver)
+        *apiver = KH_VERSION_API;
+}
+
+KHMEXP khm_ui_4 KHMAPI
+khm_get_commctl_version(khm_version * pdvi) {
+    if (pdvi) {
+        pdvi->major = (khm_ui_2) ver_commctl.dwMajorVersion;
+        pdvi->minor = (khm_ui_2) ver_commctl.dwMinorVersion;
+        pdvi->patch = (khm_ui_2) ver_commctl.dwBuildNumber;
+        pdvi->aux =   (khm_ui_2) ver_commctl.dwPlatformID;
+    }
+
+    return MAKELONG(ver_commctl.dwMinorVersion, ver_commctl.dwMajorVersion);
+}
index ddf7b1bf8f02826b26125d77a31fc5a7161f9f98..5773d8021c071d332f4b69dfc2190a7e5b0b0d22 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<perfstat.h>\r
-#include<hashtable.h>\r
-#include<stdlib.h>\r
-\r
-KHMEXP hashtable * KHMAPI hash_new_hashtable(khm_int32 n, \r
-                               hash_function_t hash, \r
-                               comp_function_t comp,\r
-                               add_ref_function_t addr,\r
-                               del_ref_function_t delr) \r
-{\r
-    hashtable * h;\r
-\r
-    h = PMALLOC(sizeof(hashtable));\r
-\r
-    h->n = n;\r
-    h->addr = addr;\r
-    h->comp = comp;\r
-    h->delr = delr;\r
-    h->hash = hash;\r
-\r
-    h->bins = PCALLOC(sizeof(hash_bin *), n);\r
-\r
-    return h;\r
-}\r
-\r
-KHMEXP void KHMAPI hash_del_hashtable(hashtable * h) {\r
-    hash_bin * b;\r
-    int i;\r
-\r
-    for(i=0;i<h->n;i++) {\r
-        LPOP(&h->bins[i], &b);\r
-        while(b) {\r
-            if(h->delr)\r
-                h->delr(b->key, b->data);\r
-            PFREE(b);\r
-            LPOP(&h->bins[i], &b);\r
-        }\r
-    }\r
-\r
-    if (h->bins)\r
-        PFREE(h->bins);\r
-\r
-    PFREE(h);\r
-}\r
-\r
-KHMEXP void KHMAPI hash_add(hashtable * h, const void * key, void * data) {\r
-    int hv;\r
-    hash_bin * b;\r
-\r
-    hv = h->hash(key) % h->n;\r
-    b = h->bins[hv];\r
-    while(b) {\r
-        if(!h->comp(b->key, key)) {\r
-            /* found an existing value */\r
-            if(h->delr)\r
-                h->delr(b->key, b->data);\r
-            b->key = key;\r
-            b->data = data;\r
-            if(h->addr)\r
-                h->addr(b->key, b->data);\r
-            break;\r
-        }\r
-        b = LNEXT(b);\r
-    }\r
-\r
-    if(!b) {\r
-        b = PMALLOC(sizeof(hash_bin));\r
-        b->data = data;\r
-        b->key = key;\r
-        LINIT(b);\r
-        LPUSH(&h->bins[hv], b);\r
-        if(h->addr)\r
-            h->addr(b->key, b->data);\r
-    }\r
-}\r
-\r
-KHMEXP void KHMAPI hash_del(hashtable * h, const void * key) {\r
-    hash_bin * b;\r
-    int hv;\r
-\r
-    hv = h->hash(key) % h->n;\r
-\r
-    b = h->bins[hv];\r
-    while(b) {\r
-        if(!h->comp(b->key, key)) {\r
-            /* found it */\r
-            LDELETE(&h->bins[hv], b);\r
-            if(h->delr)\r
-                h->delr(b->key, b->data);\r
-            PFREE(b);\r
-            break;\r
-        }\r
-        b = LNEXT(b);\r
-    }\r
-}\r
-\r
-KHMEXP void * KHMAPI hash_lookup(hashtable * h, const void * key) {\r
-    hash_bin * b;\r
-    int hv;\r
-\r
-    hv = h->hash(key) % h->n;\r
-\r
-    b = h->bins[hv];\r
-\r
-    while(b) {\r
-        if(!h->comp(b->key, key)) {\r
-            return b->data;\r
-        }\r
-        b = LNEXT(b);\r
-    }\r
-\r
-    return NULL;\r
-}\r
-\r
-KHMEXP khm_boolean KHMAPI hash_exist(hashtable * h, const void * key) {\r
-    hash_bin * b;\r
-    int hv;\r
-\r
-    hv = h->hash(key) % h->n;\r
-    b = h->bins[hv];\r
-    while(b) {\r
-        if(!h->comp(b->key, key))\r
-            return 1;\r
-        b = LNEXT(b);\r
-    }\r
-\r
-    return 0;\r
-}\r
-\r
-KHMEXP khm_int32 hash_string(const void *vs) {\r
-    /* DJB algorithm */\r
-\r
-    khm_int32 hv = 13331;\r
-    wchar_t * c;\r
-    \r
-    for(c = (wchar_t *) vs; *c; c++) {\r
-        hv = ((hv<<5) + hv) + (khm_int32) *c;\r
-    }\r
-\r
-    return (hv & KHM_INT32_MAX);\r
-}\r
-\r
-KHMEXP khm_int32 hash_string_comp(const void *vs1, const void *vs2) {\r
-    return wcscmp((const wchar_t *) vs1, (const wchar_t *) vs2);\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<perfstat.h>
+#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 = PMALLOC(sizeof(hashtable));
+
+    h->n = n;
+    h->addr = addr;
+    h->comp = comp;
+    h->delr = delr;
+    h->hash = hash;
+
+    h->bins = PCALLOC(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);
+            PFREE(b);
+            LPOP(&h->bins[i], &b);
+        }
+    }
+
+    if (h->bins)
+        PFREE(h->bins);
+
+    PFREE(h);
+}
+
+KHMEXP void KHMAPI hash_add(hashtable * h, const 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 = PMALLOC(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, const 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);
+            PFREE(b);
+            break;
+        }
+        b = LNEXT(b);
+    }
+}
+
+KHMEXP void * KHMAPI hash_lookup(hashtable * h, const 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, const 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);
+}
index 1a3fb4b86ecfb2d589fec37a449a0a3909cb3461..72fff2294b8c7b347e767d9ea47319f19469f4a6 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_HASHTABLE_H\r
-#define __KHIMAIRA_HASHTABLE_H\r
-\r
-/*! \addtogroup util\r
-  @{ */\r
-\r
-/*! \defgroup util_ht Hashtable\r
-  @{*/\r
-\r
-#include<khdefs.h>\r
-#include<khlist.h>\r
-\r
-/*! \brief A hash function\r
-\r
-    The function should take a key as a parameter and return an\r
-    khm_int32 that serves as the hash of the key.\r
- */\r
-typedef khm_int32 (*hash_function_t)(const void *key);\r
-\r
-/*! \brief A comparison function\r
-\r
-    The function takes two keys and returns a value indicating the\r
-    relative ordering of the two keys.\r
-\r
-    The return value should be:\r
-    - \b Zero if \a key1 == \a key2\r
-    - \b Negative if \a key1 &lt; \a key2\r
-    - \b Positive if \a key1 &gt; \a key2\r
- */\r
-typedef khm_int32 (*comp_function_t)(const void *key1, const void *key2);\r
-\r
-/*! \brief Add-reference function\r
-\r
-    When an object is successfully added to a hashtable, this function\r
-    will be called with the \a key and \a data used to add the object.\r
-    The function is allowed to modify \a data, however, the\r
-    modification should not alter the \a key or the relationship\r
-    between \a key and \a data.\r
- */\r
-typedef void (*add_ref_function_t)(const void *key, void *data);\r
-\r
-/*! \brief Delete-reference function\r
-\r
-    When an object is successfully removed from the hashtable, this\r
-    function will be called.  As with the add-ref function, the object\r
-    can be modified, but the \a key and the relationship between \a\r
-    key and \a data should remain intact.\r
-\r
-    An object is removed if it is explicitly removed from the\r
-    hashtable or another object with the same \a key is added to the\r
-    hashtable.  There should be a 1-1 correspondence with keys and\r
-    objects in the hashtable.  The delete-reference function will be\r
-    called on all the remaining objects in the hashtable when the\r
-    hashtable is deleted.\r
- */\r
-typedef void (*del_ref_function_t)(const void *key, void *data);\r
-\r
-typedef struct tag_hash_bin {\r
-    void * data;\r
-    const void * key;\r
-\r
-    LDCL(struct tag_hash_bin);\r
-} hash_bin;\r
-\r
-typedef struct hashtable_t {\r
-    khm_int32 n;\r
-    hash_function_t hash;\r
-    comp_function_t comp;\r
-    add_ref_function_t addr;\r
-    del_ref_function_t delr;\r
-    hash_bin ** bins;\r
-} hashtable;\r
-\r
-/*! \brief Create a new hashtable\r
-\r
-    \param[in] n Number of bins in hashtable.\r
-    \param[in] hash A hash function. Required.\r
-    \param[in] comp A comparator.  Required.\r
-    \param[in] addr An add-ref function.  Optional; can be NULL.\r
-    \param[in] delr A del-ref function. Optional; can be NULL.\r
-\r
- */\r
-KHMEXP hashtable * KHMAPI hash_new_hashtable(khm_int32 n, \r
-                               hash_function_t hash, \r
-                               comp_function_t comp,\r
-                               add_ref_function_t addr,\r
-                               del_ref_function_t delr);\r
-\r
-/*! \brief Delete a hashtable\r
-\r
-    \note Not thread-safe.  Applications must serialize calls that\r
-        reference the same hashtable.\r
- */\r
-KHMEXP void KHMAPI hash_del_hashtable(hashtable * h);\r
-\r
-/*! \brief Add an object to a hashtable\r
-\r
-    Creates an association between the \a key and \a data in the\r
-    hashtable \a h.  If there is an add-ref function defined for the\r
-    hashtable, it will be called with \a key and \data after the\r
-    object is added.  If there is already an object with the same key\r
-    in the hashtable, that object will be removed (and the del-ref\r
-    function called, if appilcable) before adding the new object and\r
-    before the add-ref function is called for the new object.\r
-\r
-    Note that two keys \a key1 and \a key2 are equal (or same) in a\r
-    hashtable if the comparator returns zero when called with \a key1\r
-    and \a key2.\r
-\r
-    Also note that all additions and removals to the hashtable are\r
-    done by reference.  No data is copied.  Any objects pointed to are\r
-    expected to exist for the duration that the object and key are\r
-    contained in the hashtable.\r
-\r
-    \param[in] h Hashtable\r
-    \param[in] key A key.  If \a key points to a location in memory,\r
-        it should be within the object pointed to by \a data, or be a\r
-        constant. Can be NULL.\r
-    \param[in] data Data. Cannot be NULL.\r
-\r
-    \note Not thread-safe.  Applications must serialize calls that\r
-        reference the same hashtable.\r
- */\r
-KHMEXP void KHMAPI hash_add(hashtable * h, const void * key, void * data);\r
-\r
-/*! \brief Delete an object from a hashtable\r
-\r
-    Deletes the object in the hashtable \a h that is associated with\r
-    key \a key.  An object is associated with key \a key if the key \a\r
-    key_o that the object is associated with is the same as \a key as\r
-    determined by the comparator.  If the del-ref function is defined\r
-    for the hash-table, it will be called with the \a key_o and \a\r
-    data that was used to add the object.\r
-\r
-    \note Not thread-safe.  Applications must serialize calls that\r
-        reference the same hashtable.\r
- */\r
-KHMEXP void KHMAPI hash_del(hashtable * h, const void * key);\r
-\r
-/*! \brief Resolve and association\r
-\r
-    Return the object that is associated with key \a key in hashtable\r
-    \a h.  An object \a data is associated with key \a key in \a h if\r
-    the key \a key_o that was used to add \a data to \a h is equal to\r
-    \a key as determined by the comparator.\r
-\r
-    Returns NULL if no association is found.\r
-\r
-    \note Not thread-safe.  Applications must serialize calls that\r
-        reference the same hashtable.\r
- */\r
-KHMEXP void * KHMAPI hash_lookup(hashtable * h, const void * key);\r
-\r
-/*! \brief Check for the presence of an association\r
-\r
-    Returns non-zero if there exists an association between key \a key\r
-    and some object in hashtable \a h.  See hash_lookup() for\r
-    definition of "association".\r
-\r
-    Returns zero if there is no association.\r
-\r
-    \note (hash_lookup(h,key) == NULL) iff (hash_exist(h,key)==0)\r
-\r
-    \note Not thead-safe.  Application must serialize calls that\r
-        reference the same hashtable.\r
- */\r
-KHMEXP khm_boolean KHMAPI hash_exist(hashtable * h, const void * key);\r
-\r
-/*! \brief Compute a hashvalue for a unicode string\r
-\r
-    The hash value is computed using DJB with parameter 13331.\r
-\r
-    This function is suitable for use as the hash function for a\r
-    hashtable if the keys are NULL terminated safe unicode strings\r
-    that are either part of the data objects or are constants.\r
-\r
-    \param[in] str A pointer to a NULL terminated wchar_t string cast\r
-        as (void *).\r
-\r
-    \note This function does not check the length of the string \a\r
-        str.  If the string is not \a NULL terminated, the behavior is\r
-        undefined.\r
- */\r
-KHMEXP khm_int32 hash_string(const void *str);\r
-\r
-/*! \brief Compare two strings\r
-\r
-    Compares two strings are returns a value that is in accordance\r
-    with the comparator for a hashtable.\r
-\r
-    \param[in] vs1 A pointer to a NULL terminated wchar_t string cast\r
-        as (void *).\r
-    \param[in] vs2 A pointer to a NULL terminated wchar_t string cast\r
-        as (void *).\r
-\r
-    \note This function does not check the length of the strings \a\r
-        vs1 and \a vs2.  If the strings are not NULL terminated, the\r
-        behavior is undefined.\r
- */\r
-KHMEXP khm_int32 hash_string_comp(const void *vs1, const void *vs2);\r
-\r
-/*@}*/\r
-/*@}*/\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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;
+    const 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, const 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, const 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, const 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, const 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 *).
+
+    \note This function does not check the length of the string \a
+        str.  If the string is not \a NULL terminated, the behavior is
+        undefined.
+ */
+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 *).
+
+    \note This function does not check the length of the strings \a
+        vs1 and \a vs2.  If the strings are not NULL terminated, the
+        behavior is undefined.
+ */
+KHMEXP khm_int32 hash_string_comp(const void *vs1, const void *vs2);
+
+/*@}*/
+/*@}*/
+
+#endif
index b642d1af88382bbf463a8e0ed963759a936dadbf..d9eb9d3507e81648f4f5725559b9b1b8253edab9 100644 (file)
-/*\r
-* Copyright (c) 2005 Massachusetts Institute of Technology\r
-*\r
-* Permission is hereby granted, free of charge, to any person\r
-* obtaining a copy of this software and associated documentation\r
-* files (the "Software"), to deal in the Software without\r
-* restriction, including without limitation the rights to use, copy,\r
-* modify, merge, publish, distribute, sublicense, and/or sell copies\r
-* of the Software, and to permit persons to whom the Software is\r
-* furnished to do so, subject to the following conditions:\r
-*\r
-* The above copyright notice and this permission notice shall be\r
-* included in all copies or substantial portions of the Software.\r
-*\r
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
-* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
-* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
-* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
-* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
-* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
-* SOFTWARE.\r
-*/\r
-\r
-/* $Id$ */\r
-\r
-#include<mstring.h>\r
-#include<kherror.h>\r
-#include<strsafe.h>\r
-#include<stdlib.h>\r
-\r
-#define TRUE    1\r
-#define FALSE   0\r
-\r
-KHMEXP khm_int32 KHMAPI\r
-multi_string_init(wchar_t * ms,\r
-                  khm_size cb_ms) {\r
-    if (!ms || cb_ms < sizeof(wchar_t) * 2)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    memset(ms, 0, cb_ms);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-multi_string_append(wchar_t * ms,\r
-                    khm_size * pcb_ms,\r
-                    const wchar_t * str)\r
-{\r
-    wchar_t * s;\r
-    size_t cch_s;\r
-    size_t cch_t;\r
-    size_t cch_r;\r
-\r
-    if(!ms || !pcb_ms || !str)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(FAILED(StringCchLength(str, KHM_MAXCCH_STRING, &cch_s)) || cch_s == 0)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    cch_s++;\r
-\r
-    s = ms;\r
-\r
-    while(*s && ((s - ms) < KHM_MAXCCH_STRING)) {\r
-        if(FAILED(StringCchLength(s, KHM_MAXCB_STRING, &cch_t)))\r
-            return KHM_ERROR_INVALID_PARAM;\r
-        s += cch_t + 1;\r
-    }\r
-\r
-    if(*s || (s - ms) >= KHM_MAXCCH_STRING) {\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    }\r
-\r
-    /* now s points to the second NULL of the terminating double NULL */\r
-\r
-    cch_r = ((s - ms) + cch_s + 1) * sizeof(wchar_t);\r
-    if(*pcb_ms < cch_r) {\r
-        *pcb_ms = cch_r;\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-\r
-    *pcb_ms = cch_r;\r
-\r
-    StringCchCopy(s, cch_s, str);\r
-    s += cch_s;\r
-    *s = 0;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-multi_string_prepend(wchar_t * ms,\r
-                     khm_size * pcb_ms,\r
-                     const wchar_t * str)\r
-{\r
-    size_t cch_s;\r
-    size_t cch_t;\r
-    size_t cch_r;\r
-    khm_size cb_r;\r
-\r
-    if(!ms || !pcb_ms || !str)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(FAILED(StringCchLength(str, KHM_MAXCCH_STRING, &cch_s)) || cch_s == 0)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-    cch_s++;\r
-\r
-    if(KHM_FAILED(multi_string_length_cch(ms,\r
-                                              KHM_MAXCCH_STRING,\r
-                                              &cch_r)))\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cch_t = cch_s + cch_r;\r
-    cb_r = cch_t * sizeof(wchar_t);\r
-\r
-    if (*pcb_ms < cb_r) {\r
-        *pcb_ms = cb_r;\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-\r
-    memmove(ms + cch_s, ms, cch_r * sizeof(wchar_t));\r
-    memcpy(ms, str, cch_s * sizeof(wchar_t));\r
-\r
-    *pcb_ms = cb_r;\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-multi_string_delete(wchar_t * ms,\r
-                    const wchar_t * str,\r
-                    const khm_int32 flags)\r
-{\r
-    wchar_t * s;\r
-    wchar_t * n;\r
-    wchar_t * e;\r
-    size_t cch;\r
-\r
-    if(!ms || !str)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    s = multi_string_find(ms, str, flags);\r
-    if(!s)\r
-        return KHM_ERROR_NOT_FOUND;\r
-\r
-    e = s;\r
-    n = NULL;\r
-    while(*e && (e - s) < KHM_MAXCCH_STRING) {\r
-        if(FAILED(StringCchLength(e, KHM_MAXCCH_STRING, &cch)))\r
-            return KHM_ERROR_INVALID_PARAM;\r
-        e += cch + 1;\r
-\r
-        if(!n)\r
-            n = e;\r
-    }\r
-\r
-    if(*e || (e - s) >= KHM_MAXCCH_STRING)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    if(e == s)\r
-        return KHM_ERROR_SUCCESS;\r
-\r
-    memmove((void *) s, (void *) n, ((e - n) + 1) * sizeof(wchar_t));\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP wchar_t * KHMAPI \r
-multi_string_find(const wchar_t * ms,\r
-                  const wchar_t * str,\r
-                  const khm_int32 flags)\r
-{\r
-    const wchar_t *s;\r
-    size_t cch;\r
-    size_t cch_s;\r
-\r
-    if(!ms || !str)\r
-        return NULL;\r
-\r
-    if(FAILED(StringCchLength(str, KHM_MAXCCH_STRING, &cch_s)))\r
-        return NULL;\r
-\r
-    s = ms;\r
-\r
-    while(*s && (s - ms) < KHM_MAXCCH_STRING) {\r
-        if(FAILED(StringCchLength(s, KHM_MAXCCH_STRING, &cch)))\r
-            return NULL;\r
-        /* cch++ at end */\r
-\r
-        if(flags & KHM_PREFIX) {\r
-            if(((flags & KHM_CASE_SENSITIVE) && !wcsncmp(s, str, cch_s)) ||\r
-                (!(flags & KHM_CASE_SENSITIVE) && !_wcsnicmp(s, str, cch_s)))\r
-                return (wchar_t *) s;\r
-        } else {\r
-            if((cch == cch_s) &&\r
-                               ((flags & KHM_CASE_SENSITIVE) && !wcsncmp(s, str, cch)) ||\r
-                (!(flags & KHM_CASE_SENSITIVE) && !_wcsnicmp(s, str, cch)))\r
-                return (wchar_t *) s;\r
-        }\r
-\r
-        s += cch + 1;\r
-    }\r
-\r
-    return NULL;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-multi_string_to_csv(wchar_t * csvbuf,\r
-                    khm_size * pcb_csvbuf,\r
-                    const wchar_t * ms)\r
-{\r
-    size_t cb;\r
-    size_t cbt;\r
-    const wchar_t * t;\r
-    wchar_t * d;\r
-\r
-    if(!pcb_csvbuf || !ms)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    /* dry run */\r
-    cbt = 0;\r
-    t = ms;\r
-    while(*t && cbt <= KHM_MAXCB_STRING) {\r
-        khm_boolean quotes = FALSE;\r
-\r
-        if(FAILED(StringCbLength(t, KHM_MAXCB_STRING, &cb)))\r
-            return KHM_ERROR_INVALID_PARAM;\r
-        cb += sizeof(wchar_t);\r
-\r
-        cbt += cb;\r
-\r
-        if(wcschr(t, L','))\r
-            quotes = TRUE;\r
-\r
-        d = (wchar_t *) t;\r
-        while(d = wcschr(d, L'"')) {\r
-            cbt += sizeof(wchar_t); /* '"'-> '""' */\r
-            d++;\r
-            quotes = TRUE;\r
-        }\r
-\r
-        if(quotes)\r
-            cbt += 2*sizeof(wchar_t); /* make room for quotes */\r
-\r
-        t += cb / sizeof(wchar_t);\r
-    }\r
-\r
-    if(cbt > KHM_MAXCB_STRING)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    /* happens if the multi string contained no strings */\r
-    if(cbt == 0)\r
-        cbt = sizeof(wchar_t);\r
-\r
-    if(!csvbuf || *pcb_csvbuf < cbt)\r
-    {\r
-        *pcb_csvbuf = cbt;\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-\r
-    *pcb_csvbuf = cbt;\r
-\r
-    /* wet run */\r
-    t = ms;\r
-    d = csvbuf;\r
-    *csvbuf = 0;\r
-    while(*t) {\r
-        const wchar_t * s;\r
-\r
-        StringCbLength(t, KHM_MAXCB_STRING, &cb);\r
-        cb += sizeof(wchar_t);\r
-\r
-        if(d != csvbuf)\r
-            *d++ = L',';\r
-        if(wcschr(t, L',') || wcschr(t, L'"')) {\r
-            *d++ = L'"';\r
-            s = t;\r
-            while(*s) {\r
-                if(*s == L'"') {\r
-                    *d++ = L'"';\r
-                    *d++ = L'"';\r
-                } else\r
-                    *d++ = *s;\r
-                s++;\r
-            }\r
-            *d++ = L'"';\r
-            *d = 0;\r
-        } else {\r
-            StringCbCopy(d, cbt - ((d - csvbuf) * sizeof(wchar_t)), t);\r
-            d += cb / sizeof(wchar_t) - 1;\r
-        }\r
-        t += cb / sizeof(wchar_t);\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-csv_to_multi_string(wchar_t * ms,\r
-                    khm_size * pcb_ms,\r
-                    const wchar_t * csv)\r
-{\r
-    const wchar_t * t;\r
-    wchar_t * p;\r
-    size_t cchr;\r
-    int field = 1;\r
-\r
-\r
-    if(!pcb_ms || !csv)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cchr = 0;\r
-\r
-    /* dry run */\r
-    t = csv;\r
-    while(*t && (t - csv) < KHM_MAXCCH_STRING) {\r
-        if(field && *t == L'"') {\r
-            t++;\r
-            while(*t && (t - csv) < KHM_MAXCCH_STRING) {\r
-                if(*t == L'"') {\r
-                    t++;\r
-                    if(*t != L'"')\r
-                        break;\r
-                }\r
-                cchr++;\r
-                t++;\r
-            }\r
-        }\r
-\r
-        if(*t) {\r
-            cchr++;\r
-            if(*t == L',')\r
-                field = 1;\r
-            else\r
-                field = 0;\r
-\r
-            t++;\r
-        }\r
-    }\r
-\r
-    if((t - csv) >= KHM_MAXCCH_STRING)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    cchr++; /* last string ends */\r
-    cchr++; /* double NULL */\r
-\r
-    if(!ms || *pcb_ms < (cchr * sizeof(wchar_t))) {\r
-        *pcb_ms = cchr * sizeof(wchar_t);\r
-        return KHM_ERROR_TOO_LONG;\r
-    }\r
-\r
-    /* wet run */\r
-    t = csv;\r
-    p = ms;\r
-    field = 1;\r
-    while(*t) {\r
-        if(field && *t == L'"') {\r
-            t++;\r
-            while(*t) {\r
-                if(*t == L'"') {\r
-                    t++;\r
-                    if(*t != L'"')\r
-                        break;\r
-                }\r
-                *p++ = *t;\r
-                t++;\r
-            }\r
-        }\r
-\r
-        if(*t == L',') {\r
-            *p++ = 0;\r
-            field = 1;\r
-            t++;\r
-        } else if(*t) {\r
-            *p++ = *t;\r
-            field = 0;\r
-            t++;\r
-        }\r
-    }\r
-\r
-    *p++ = 0; /* last string ends */\r
-    *p++ = 0; /* double NULL */\r
-\r
-    *pcb_ms = (p - ms) * sizeof(wchar_t);\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP wchar_t * KHMAPI \r
-multi_string_next(const wchar_t * str)\r
-{\r
-    size_t cch;\r
-\r
-    if(*str) {\r
-        if(FAILED(StringCchLength(str, KHM_MAXCCH_STRING, &cch)))\r
-            return NULL;\r
-        str += cch + 1;\r
-        if(*str)\r
-            return (wchar_t *) str;\r
-        else\r
-            return NULL;\r
-    } else {\r
-        return NULL;\r
-    }\r
-}\r
-\r
-KHMEXP khm_size KHMAPI \r
-multi_string_length_n(const wchar_t * str)\r
-{\r
-    size_t n = 0;\r
-    const wchar_t * c = str;\r
-\r
-    while(c) {\r
-        n++;\r
-        c = multi_string_next(c);\r
-    }\r
-\r
-    return n;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-multi_string_length_cb(const wchar_t * str, \r
-                       khm_size max_cb, \r
-                       khm_size * len_cb)\r
-{\r
-    khm_size cch;\r
-    khm_int32 rv;\r
-\r
-    rv = multi_string_length_cch(str, max_cb / sizeof(wchar_t), &cch);\r
-    \r
-    if(KHM_FAILED(rv))\r
-        return rv;\r
-    \r
-    if(len_cb)\r
-        *len_cb = cch * sizeof(wchar_t);\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-multi_string_length_cch(const wchar_t * str, \r
-                        khm_size max_cch, \r
-                        khm_size * len_cch)\r
-{\r
-    const wchar_t * s;\r
-    khm_size cch;\r
-    size_t tcch;\r
-\r
-    if(!str)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    s = str;\r
-    cch = 0;\r
-    while(*s && (cch < max_cch)) {\r
-        if(FAILED(StringCchLength(s, max_cch, &tcch)))\r
-            return KHM_ERROR_TOO_LONG;\r
-        cch += ++tcch;\r
-        s += tcch;\r
-    }\r
-\r
-    if(cch >= max_cch)\r
-        return KHM_ERROR_TOO_LONG;\r
-\r
-    if(len_cch) {\r
-        *len_cch = ++cch;\r
-    }\r
-\r
-    return KHM_ERROR_SUCCESS;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-multi_string_copy_cb(wchar_t * s_dest, \r
-                         khm_size max_cb_dest, \r
-                         const wchar_t * src)\r
-{\r
-    khm_size cb_dest;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if(!s_dest)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    rv = multi_string_length_cb(src, max_cb_dest, &cb_dest);\r
-    if(KHM_FAILED(rv))\r
-        return rv;\r
-\r
-    memmove(s_dest, src, cb_dest);\r
-\r
-    return rv;\r
-}\r
-\r
-KHMEXP khm_int32 KHMAPI \r
-multi_string_copy_cch(wchar_t * s_dest, \r
-                      khm_size max_cch_dest, \r
-                      const wchar_t * src)\r
-{\r
-    khm_size cch_dest;\r
-    khm_int32 rv = KHM_ERROR_SUCCESS;\r
-\r
-    if(!s_dest)\r
-        return KHM_ERROR_INVALID_PARAM;\r
-\r
-    rv = multi_string_length_cch(src, max_cch_dest, &cch_dest);\r
-    if(KHM_FAILED(rv))\r
-        return rv;\r
-\r
-    memmove(s_dest, src, cch_dest * sizeof(wchar_t));\r
-\r
-    return rv;\r
-}\r
+/*
+* Copyright (c) 2005 Massachusetts Institute of Technology
+*
+* Permission is hereby granted, free of charge, to any person
+* obtaining a copy of this software and associated documentation
+* files (the "Software"), to deal in the Software without
+* restriction, including without limitation the rights to use, copy,
+* modify, merge, publish, distribute, sublicense, and/or sell copies
+* of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+* ACTION OF 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_PARAM;
+
+    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_PARAM;
+
+    if(FAILED(StringCchLength(str, KHM_MAXCCH_STRING, &cch_s)) || cch_s == 0)
+        return KHM_ERROR_INVALID_PARAM;
+    cch_s++;
+
+    s = ms;
+
+    while(*s && ((s - ms) < KHM_MAXCCH_STRING)) {
+        if(FAILED(StringCchLength(s, KHM_MAXCB_STRING, &cch_t)))
+            return KHM_ERROR_INVALID_PARAM;
+        s += cch_t + 1;
+    }
+
+    if(*s || (s - ms) >= KHM_MAXCCH_STRING) {
+        return KHM_ERROR_INVALID_PARAM;
+    }
+
+    /* 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_PARAM;
+
+    if(FAILED(StringCchLength(str, KHM_MAXCCH_STRING, &cch_s)) || cch_s == 0)
+        return KHM_ERROR_INVALID_PARAM;
+    cch_s++;
+
+    if(KHM_FAILED(multi_string_length_cch(ms,
+                                              KHM_MAXCCH_STRING,
+                                              &cch_r)))
+        return KHM_ERROR_INVALID_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+        e += cch + 1;
+
+        if(!n)
+            n = e;
+    }
+
+    if(*e || (e - s) >= KHM_MAXCCH_STRING)
+        return KHM_ERROR_INVALID_PARAM;
+
+    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_PARAM;
+
+    /* 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_PARAM;
+        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_PARAM;
+
+    /* 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_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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_PARAM;
+
+    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;
+}
index a7f6cf5234ff2f27cddb524be0b34042f97d29a7..497cb777d4236723de6f4fce3bd36b16b3718674 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_MSTRING_H\r
-#define __KHIMAIRA_MSTRING_H\r
-\r
-#include<khdefs.h>\r
-\r
-/*! \addtogroup util\r
-    @{ */\r
-\r
-/*! \defgroup util_mstring Multi String and CSV functions\r
-    @{*/\r
-\r
-#define KHM_PREFIX 8\r
-\r
-#define KHM_CASE_SENSITIVE 16\r
-\r
-#define KHM_MAXCCH_STRING 16384\r
-\r
-#define KHM_MAXCB_STRING (KHM_MAXCCH_STRING * sizeof(wchar_t))\r
-\r
-/*! \brief Initialize a multi-string\r
- */\r
-KHMEXP khm_int32 KHMAPI\r
-multi_string_init(wchar_t * ms,\r
-                  khm_size cb_ms);\r
-\r
-/*! \brief Prepend a string to a multi string\r
-\r
-    Adds the string \a str to the beginning of multi-string \a ms.\r
-\r
-    \param[in,out] ms  The multi-string to be modified.\r
-\r
-    \param[in,out] pcb_ms A pointer to the size of the multistring.\r
-        On entry this specifies the size of the buffer pointed to by\r
-        \a ms.  If the call is successful, on exit this will receive\r
-        the new size of the multi string in bytes.  If the buffer is\r
-        insufficient, the function will return KHM_ERROR_TOO_LONG and\r
-        set this to the required size of the buffer in bytes.\r
-\r
-    \param[in] str The string to prepend to \a ms.  This cannot be\r
-        longer than KHM_MAXCCH_STRING in characters including the\r
-        terminating NULL.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-multi_string_prepend(wchar_t * ms,\r
-                     khm_size * pcb_ms,\r
-                     const wchar_t * str);\r
-\r
-/*! \brief Append a string to a multi-string\r
-\r
-    Appends the string specified by \a str to the multi string\r
-    specified by \a ms.  The size of the multi string in characters\r
-    including terminating NULLs after appending \a str can not exceed\r
-    KHM_MAXCCH_STRING.\r
-\r
-    \param[in] ms The buffer containing the multi string\r
-\r
-    \param[in,out] pcb_ms Points to a khm_int32 indicating the size of\r
-        the buffer pointed to by \a ms.  On entry this contains the\r
-        size (in bytes) of the buffer pointed to by \a ms.  On exit,\r
-        contains the new size of the multi string in bytes.\r
-\r
-    \param[in] str The string to append to the multi string.  This\r
-        string cannot be NULL or an empty (zero length) string.  The\r
-        length of \a str cannot exceed KHM_MAXCCH_STRING in\r
-        characters including terminating NULL.\r
-\r
-    \retval KHM_ERROR_SUCCESS The string was appended to the multi string\r
-\r
-    \retval KHM_ERROR_TOO_LONG The buffer pointed to by \a ms was\r
-        insufficient.  The required size of the buffer is in \a pcb_ms\r
-\r
-    \retval KHM_ERROR_INVALID_PARAM One of more of the parameters were invalid.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-multi_string_append(wchar_t * ms,\r
-                    khm_size * pcb_ms,\r
-                    const wchar_t * str);\r
-\r
-/*! \brief Deletes a string from a multi string\r
-\r
-    Deletes the string specified by \a str from the multi string\r
-    specified by \a ms.  How the string is matched to the strings in\r
-    \a ms is determined by \a flags.  If more than one match is found,\r
-    then only the first match is deleted.\r
-\r
-    \param[in] ms The multi string to modify.  The length of the multi\r
-        string in characters cannot exceed KHM_MAXCCH_STRING.\r
\r
-    \param[in] str The string to search for\r
-\r
-    \param[in] flags How \a str is to be matched to existing strings\r
-        in \a ms.  This could be a combination of KHM_PREFIX and\r
-        KHM_CASE_SENSITIVE. If KHM_PREFIX is used, then \a ms is\r
-        searched for a string that begins with \a str.  Otherwise, \a\r
-        str must match the an entire string in the multi string.  If\r
-        KHM_CASE_SENSITIVE is specified, then a case sensitive match\r
-        is performed.  The defualt is to use a case insensitive\r
-        search.\r
-\r
-    \retval KHM_ERROR_SUCCESS A string was matched and deleted from \a ms\r
-\r
-    \retval KHM_ERROR_NOT_FOUND No matches were found\r
-\r
-    \retval KHM_ERROR_INVALID_PARAM One or more parameters were incorrect.\r
-\r
-    \note The search for the existing string is done with\r
-        multi_string_find()\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-multi_string_delete(wchar_t * ms,\r
-                    const wchar_t * str,\r
-                    const khm_int32 flags);\r
-\r
-/*! \brief Search a multi string for a string\r
-\r
-    Searches the string specified by \a ms for a string that matches\r
-    \a str.  How the match is performed is determined by \a flags.\r
-    Returns a poitner to the start of the matched string in \a ms.  If\r
-    more than one string in \a ms matches \a str, then only the first\r
-    match is returned.\r
-\r
-    \param[in] ms The multi string to search in.  The length of the\r
-        multi string cannot exceed KHM_MAXCCH_STRING in characters.\r
-\r
-    \param[in] str The string to search for\r
-\r
-    \param[in] flags How \a str is to be matched to existing strings\r
-        in \a ms.  This could be a combination of KHM_PREFIX and\r
-        KHM_CASE_SENSITIVE. If KHM_PREFIX is used, then \a ms is\r
-        searched for a string that begins with \a str.  Otherwise, \a\r
-        str must match the an entire string in the multi string.  If\r
-        KHM_CASE_SENSITIVE is specified, then a case sensitive match\r
-        is performed.  The defualt is to use a case insensitive\r
-        search.\r
-\r
-    \return A pointer to the start of the first matched string or\r
-        NULL if no matches were found.\r
-\r
- */\r
-KHMEXP wchar_t * KHMAPI \r
-multi_string_find(const wchar_t * ms,\r
-                  const wchar_t * str,\r
-                  const khm_int32 flags);\r
-\r
-/*! \brief Convert a multi string to CSV\r
-\r
-    Converts a multi string to a comma separated value string based on\r
-    the following rules.\r
-\r
-    - Each string in the multi string is treated an individual field \r
-\r
-    - A field is quoted if it has double quotes or commas \r
-\r
-    - Double quotes within quoted fields are escaped by two\r
-      consecutive double quotes.\r
-\r
-    For example:\r
-\r
-    \code\r
-    multi_string = L"foo\0bar\0baz,quux\0ab\"cd\0";\r
-    csv_string = L"foo,bar,\"baz,quux\",\"ab\"\"cd\"";\r
-    \endcode\r
-\r
-    If multi_string_to_csv() is called on \a multi_string above,\r
-    you would obtain \a csv_string.\r
-\r
-    \param[out] csvbuf The buffer to place the CSV string in.  Can be\r
-        NULL if only teh size of the needed buffer is required.\r
-\r
-    \param[in,out] pcb_csvbuf On entry, points to a khm_int32 that\r
-        holds the size of the buffer pointed to by \a csvbuf.  On\r
-        exit, gets the number of bytes writted to \a csvbuf or the\r
-        required size of \a csvbuf if the buffer is too small or \a\r
-        csvbuf is NULL.\r
-\r
-    \param[in] ms The mutli string to convert to a CSV.\r
-\r
-    \retval KHM_ERROR_SUCCESS The multi string was successfully\r
-        converted to a CSV string.  The number of bytes written is in\r
-        \a pcb_csvbuf.  The count includes the terminating NULL.\r
-\r
-    \retval KHM_ERROR_TOO_LONG The buffer was too small or \a csvbuf\r
-        was NULL.  The required number of bytes in the buffer is in \a\r
-        pcb_csvbuf.\r
-\r
-    \retval KHM_ERROR_INVALID_PARAM One or more parameters were ivnalid.\r
-\r
-    \see csv_to_multi_string()\r
-*/\r
-KHMEXP khm_int32 KHMAPI \r
-multi_string_to_csv(wchar_t * csvbuf,\r
-                    khm_size * pcb_csvbuf,\r
-                    const wchar_t * ms);\r
-\r
-/*! \brief Converts a CSV to a multi string\r
-\r
-    Undoes what multi_string_to_csv() does.\r
-\r
-    \param[out] ms The buffer that recieves the multi string.  This\r
-        can be NULL if only the size of the buffer is requried.\r
-\r
-    \param[in,out] pcb_ms On entry contains the number of bytes ni the\r
-        buffer poitned to by \a ms.  On exit contains the number of\r
-        bytes that were copied to \a ms including terminating NULLs,\r
-        or if the buffer was too small or \a ms was NULL, holds the\r
-        size in bytes of the requied buffer.\r
-\r
-    \param[in] csv The CSV string.\r
-\r
-    \retval KHM_ERROR_SUCCESS The CSV string was successfully\r
-       converted.  The number of bytes written is in \a pcb_ms.\r
-\r
-    \retval KHM_ERROR_TOO_LONG The provided buffer was too small or \a\r
-        ms was NULL. The required size of the buffer in bytes is in \a\r
-        pcb_ms.\r
-\r
-    \retval KHM_ERROR_INVALID_PARAM One or more parameters were invalid.\r
-\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-csv_to_multi_string(wchar_t * ms,\r
-                    khm_size * pcb_ms,\r
-                    const wchar_t * csv);\r
-\r
-/*! \brief Get the next string in a multi string\r
-\r
-    When \a str is pointing to a string that is in a multi string,\r
-    this function returns a pointer to the next string in the multi\r
-    string.\r
-\r
-    Typically, one would start by having \a str point to the start of\r
-    the multi string (which is the first string in the multi string),\r
-    and then call this function repeatedly, until it returns NULL, at\r
-    which point the end of the multi string has been reached.\r
-\r
-    \param[in] str Pointer to a string in a multi string.  Each string\r
-        in a multi string cannot exceed KHM_MAXCCH_STRING in charaters\r
-        including the terminating NULL.\r
-\r
-    \return A pointer to the start of the next string in the multi\r
-        string or NULL if there is no more strings.\r
- */\r
-KHMEXP wchar_t * KHMAPI \r
-multi_string_next(const wchar_t * str);\r
-\r
-/*! \brief Get the length of a multi string in bytes\r
-\r
-    The returned length includes the trailing double \a NULL and any\r
-    other \a NULL inbetween.\r
-\r
-    \param[in] str Pointer to a multi string.\r
-    \param[in] max_cb Maximum size that the str can be.  This can not\r
-        be larger than KHM_MAXCB_STRING.\r
-    \param[out] len_cb The length of the string in bytes if the call\r
-        is successful.\r
-\r
-    \retval KHM_ERROR_SUCCESS The length of the string is in \a len_cb\r
-    \retval KHM_ERROR_INVALID_PARAM One or more parameters were invalid\r
-    \retval KHM_ERROR_TOO_LONG The multi string is longer than \a\r
-        max_cb bytes.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-multi_string_length_cb(const wchar_t * str, \r
-                       khm_size max_cb, \r
-                       khm_size * len_cb);\r
-\r
-/*! \brief Get the length of a multi string in characters\r
-\r
-    The returned length includes the trailing double \a NULL and any\r
-    other \a NULL inbetween.\r
-\r
-    \param[in] str Pointer to a multi string.\r
-    \param[in] max_cch Maximum size that the str can be.  This can not\r
-        be larger than KHM_MAXCCH_STRING.\r
-    \param[out] len_cch The length of the string in characters if the call\r
-        is successful.\r
-\r
-    \retval KHM_ERROR_SUCCESS The length of the string is in \a len_cch\r
-    \retval KHM_ERROR_INVALID_PARAM One or more parameters were invalid\r
-    \retval KHM_ERROR_TOO_LONG The multi string is longer than \a\r
-        max_cch characters.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-multi_string_length_cch(const wchar_t * str, \r
-                        khm_size max_cch, \r
-                        khm_size * len_cch);\r
-\r
-/*! \brief Get the number of strings in a multi string\r
- */\r
-KHMEXP khm_size KHMAPI \r
-multi_string_length_n(const wchar_t * str);\r
-\r
-/*! \brief Copy a multi string with byte counts\r
-\r
-    Copy a multi string from one location to another.\r
-\r
-    \param[out] s_dest Receives a copy of the multi string\r
-    \param[in] max_cb_dest Number of bytes in the buffer pointed to by\r
-        \a s_dest.\r
-    \param[in] src The source multi string\r
-\r
-    \retval KHM_ERROR_SUCCESS The multi string was copied successfully\r
-    \retval KHM_ERROR_INVALID_PARAM One or more parameters were\r
-        invalid.\r
-    \retval KHM_ERROR_TOO_LONG The size of the destination buffer was\r
-        insufficient.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-multi_string_copy_cb(wchar_t * s_dest, \r
-                     khm_size max_cb_dest, \r
-                     const wchar_t * src);\r
-\r
-/*! \brief Copy a multi string with character count\r
-\r
-    Copy a multi string from one location to another.\r
-\r
-    \param[out] s_dest Receives a copy of the multi string\r
-    \param[in] max_cb_dest Number of characters in the buffer pointed\r
-        to by \a s_dest.\r
-    \param[in] src The source multi string\r
-\r
-    \retval KHM_ERROR_SUCCESS The multi string was copied successfully\r
-    \retval KHM_ERROR_INVALID_PARAM One or more parameters were\r
-        invalid.\r
-    \retval KHM_ERROR_TOO_LONG The size of the destination buffer was\r
-        insufficient.\r
- */\r
-KHMEXP khm_int32 KHMAPI \r
-multi_string_copy_cch(wchar_t * s_dest, \r
-                      khm_size max_cch_dest, \r
-                      const wchar_t * src);\r
-\r
-/*@}*/\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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_PARAM 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_PARAM 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_PARAM 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_PARAM 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_PARAM 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_PARAM 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_PARAM 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_PARAM 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
index cc27d93bd0f2429e29176fa37d59f7228b5a3da9..6a9dd810343dc794aa6d95c50269b23f3f0b0222 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<windows.h>\r
-#include<utils.h>\r
-#include<malloc.h>\r
-#include<stdio.h>\r
-#include<strsafe.h>\r
-#include<assert.h>\r
-\r
-#define HASHSIZE 1151\r
-#define ALLOCBLOCK 1024\r
-\r
-#define HASHPTR(p) (((size_t) (p)) % HASHSIZE)\r
-\r
-typedef struct tag_allocation {\r
-    const char * file;\r
-    int    line;\r
-    size_t size;\r
-    void * ptr;\r
-#ifdef _WIN32\r
-    DWORD  thread;\r
-#endif\r
-\r
-    LDCL(struct tag_allocation);\r
-} allocation;\r
-\r
-static allocation * ht[HASHSIZE];\r
-\r
-static allocation * next_alloc = NULL;\r
-static size_t       idx_next_alloc = 0;\r
-static allocation * free_alloc = NULL;\r
-\r
-typedef struct tag_thread_info {\r
-#ifdef _WIN32\r
-    DWORD thread;\r
-#else\r
-#error Unsupported platform\r
-#endif\r
-    wchar_t name[128];\r
-    wchar_t creator[128];\r
-\r
-    const char * file;\r
-    int line;\r
-\r
-    LDCL(struct tag_thread_info);\r
-} thread_info;\r
-\r
-static thread_info * threads = NULL;\r
-\r
-static hashtable fn_hash;\r
-\r
-static CRITICAL_SECTION cs_alloc;\r
-static LONG ctr = 0;\r
-static int  perf_ready = 0;\r
-\r
-static DWORD init_thread = 0;\r
-\r
-static khm_int32 hash_stringA(const void * vs) {\r
-    /* DJB algorithm */\r
-\r
-    khm_int32 hv = 13331;\r
-    char * c;\r
-\r
-    for (c = (char *) vs; *c; c++) {\r
-        hv = ((hv << 5) + hv) + (khm_int32) *c;\r
-    }\r
-\r
-    return (hv & KHM_INT32_MAX);\r
-}\r
-\r
-static khm_int32 hash_string_compA(const void * vs1,\r
-                                   const void * vs2) {\r
-    return strcmp((const char *) vs1, (const char *) vs2);\r
-}\r
-\r
-static void perf_once(void) {\r
-    if (InterlockedIncrement(&ctr) == 1) {\r
-        InitializeCriticalSection(&cs_alloc);\r
-        ZeroMemory(ht, sizeof(ht));\r
-\r
-        next_alloc = malloc(sizeof(allocation) * ALLOCBLOCK);\r
-        assert(next_alloc);\r
-        idx_next_alloc = 0;\r
-        free_alloc = NULL;\r
-\r
-        ZeroMemory(&fn_hash, sizeof(fn_hash));\r
-        fn_hash.n = 13;\r
-        fn_hash.hash = hash_stringA;\r
-        fn_hash.comp = hash_string_compA;\r
-        fn_hash.bins = calloc(sizeof(hash_bin *), fn_hash.n);\r
-\r
-        perf_ready = 1;\r
-    } else {\r
-        DWORD this_thread = GetCurrentThreadId();\r
-\r
-        while(!perf_ready &&\r
-              init_thread != this_thread) {\r
-            Sleep(0);           /* relinquish control to the thread\r
-                                   that is initializing the alloc\r
-                                   data. */\r
-        }\r
-    }\r
-}\r
-\r
-static allocation * get_allocation(void) {\r
-    allocation * a;\r
-\r
-    LPOP(&free_alloc, &a);\r
-    if (!a) {\r
-        if (idx_next_alloc == ALLOCBLOCK) {\r
-            next_alloc = malloc(sizeof(allocation) * ALLOCBLOCK);\r
-            assert(next_alloc);\r
-            idx_next_alloc = 0;\r
-        }\r
-\r
-        a = &next_alloc[idx_next_alloc];\r
-        idx_next_alloc++;\r
-    }\r
-\r
-    return a;\r
-}\r
-\r
-#define MAXCB_STR 32768\r
-\r
-KHMEXP wchar_t *\r
-perf_wcsdup(const char * file, int line, const wchar_t * str) {\r
-    size_t cb;\r
-    wchar_t * dest;\r
-\r
-    if (FAILED(StringCbLength(str, MAXCB_STR, &cb)))\r
-        return NULL;\r
-    cb += sizeof(wchar_t);\r
-\r
-    dest = (wchar_t *) perf_malloc(file, line, cb);\r
-    StringCbCopy(dest, cb, str);\r
-\r
-    return dest;\r
-}\r
-\r
-KHMEXP char *\r
-perf_strdup(const char * file, int line, const char * str) {\r
-    size_t cb;\r
-    char * dest;\r
-\r
-    if (FAILED(StringCbLengthA(str, MAXCB_STR, &cb)))\r
-        return NULL;\r
-    cb += sizeof(char);\r
-\r
-    dest = (char *) perf_malloc(file, line, cb);\r
-    StringCbCopyA(dest, cb, str);\r
-\r
-    return dest;\r
-}\r
-\r
-KHMEXP void *\r
-perf_calloc(const char * file, int line, size_t num, size_t size) {\r
-    void * ptr;\r
-    size_t tsize;\r
-\r
-    tsize = num * size;\r
-\r
-    ptr = perf_malloc(file,line,tsize);\r
-\r
-    if (ptr) {\r
-        ZeroMemory(ptr, tsize);\r
-    }\r
-\r
-    return ptr;\r
-}\r
-\r
-KHMEXP void * \r
-perf_malloc(const char * file, int line, size_t s) {\r
-    allocation * a;\r
-    void * ptr;\r
-    size_t h;\r
-    char * fn_copy = NULL;\r
-\r
-    perf_once();\r
-\r
-    assert(s > 0);\r
-\r
-    EnterCriticalSection(&cs_alloc);\r
-    a = get_allocation();\r
-\r
-    ptr = malloc(s);\r
-\r
-    assert(ptr);                /* TODO: handle this gracefully */\r
-\r
-    if (file[0] == '.' && file[1] == '\\')\r
-        file += 2;\r
-\r
-    fn_copy = hash_lookup(&fn_hash, file);\r
-    if (fn_copy == NULL) {\r
-\r
-        size_t cblen = 0;\r
-        if (FAILED(StringCbLengthA(file, MAX_PATH * sizeof(char),\r
-                                   &cblen)))\r
-            fn_copy = NULL;\r
-        else {\r
-            fn_copy = malloc(cblen + sizeof(char));\r
-            if (fn_copy) {\r
-                hash_bin * b;\r
-                int hv;\r
-\r
-                StringCbCopyA(fn_copy, cblen + sizeof(char), file);\r
-\r
-                hv = fn_hash.hash(fn_copy) % fn_hash.n;\r
-\r
-                b = malloc(sizeof(*b));\r
-                b->data = fn_copy;\r
-                b->key = fn_copy;\r
-                LINIT(b);\r
-                LPUSH(&fn_hash.bins[hv], b);\r
-            }\r
-        }\r
-    }\r
-\r
-    a->file = fn_copy;\r
-    a->line = line;\r
-    a->size = s;\r
-    a->ptr = ptr;\r
-#ifdef _WIN32\r
-    a->thread = GetCurrentThreadId();\r
-#endif\r
-\r
-    h = HASHPTR(ptr);\r
-\r
-    LPUSH(&ht[h], a);\r
-    LeaveCriticalSection(&cs_alloc);\r
-\r
-    return ptr;\r
-}\r
-\r
-KHMEXP void *\r
-perf_realloc(const char * file, int line, void * data, size_t s) {\r
-    void * n_data;\r
-    allocation * a;\r
-    size_t h;\r
-\r
-    if (data == NULL)\r
-        return perf_malloc(file, line, s);\r
-\r
-    perf_once();\r
-    h = HASHPTR(data);\r
-\r
-    n_data = realloc(data, s);\r
-\r
-    assert(n_data);\r
-\r
-    EnterCriticalSection(&cs_alloc);\r
-    for (a = ht[h]; a; a = LNEXT(a)) {\r
-        if (a->ptr == data)\r
-            break;\r
-    }\r
-\r
-    assert(a);\r
-\r
-    LDELETE(&ht[h], a);\r
-\r
-    a->size = s;\r
-    a->ptr = n_data;\r
-\r
-    h = HASHPTR(n_data);\r
-    LPUSH(&ht[h], a);\r
-    LeaveCriticalSection(&cs_alloc);\r
-\r
-    return n_data;\r
-}\r
-\r
-KHMEXP void\r
-perf_free  (void * b) {\r
-    size_t h;\r
-    allocation * a;\r
-\r
-    perf_once();\r
-    h = HASHPTR(b);\r
-\r
-    EnterCriticalSection(&cs_alloc);\r
-    for(a = ht[h]; a; a = LNEXT(a)) {\r
-        if (a->ptr == b)\r
-            break;\r
-    }\r
-\r
-    assert(a);\r
-\r
-    LDELETE(&ht[h], a);\r
-    LPUSH(&free_alloc, a);\r
-    LeaveCriticalSection(&cs_alloc);\r
-}\r
-\r
-KHMEXP void KHMAPI\r
-perf_dump(FILE * f) {\r
-    size_t i;\r
-    allocation * a;\r
-    size_t total = 0;\r
-    thread_info * t;\r
-\r
-    perf_once();\r
-\r
-    EnterCriticalSection(&cs_alloc);\r
-\r
-    fprintf(f, "p00\t*** Threads ***\n");\r
-    fprintf(f, "p00\tFile\tLine\tThread\tName\tCreated by\n");\r
-\r
-    for (t = threads; t; t = LNEXT(t)) {\r
-        fprintf(f, "p01\t%s\t%6d\t%6d\t%S\t%S\n",\r
-                t->file, t->line, t->thread,\r
-                t->name, t->creator);\r
-    }\r
-\r
-    fprintf(f, "p02\t--- End Threads ---\n");\r
-\r
-    fprintf(f, "p10\t*** Leaked allocations list ***\n");\r
-    fprintf(f, "p11\tFile\tLine\tThread\tSize\tAddress\n");\r
-\r
-    for (i=0; i < HASHSIZE; i++) {\r
-        for (a = ht[i]; a; a = LNEXT(a)) {\r
-            fprintf(f, "p12\t%s\t%6d\t%6d\t%6d\t0x%p\n", a->file, a->line,\r
-                   a->thread, a->size, a->ptr);\r
-            total += a->size;\r
-        }\r
-    }\r
-\r
-    fprintf(f, "p20\t----------------------------------------\n");\r
-    fprintf(f, "p21\tTotal\t\t%d\n", total);\r
-    fprintf(f, "p22\t----------------- End ------------------\n");\r
-\r
-    LeaveCriticalSection(&cs_alloc);\r
-}\r
-\r
-KHMEXP void\r
-perf_set_thread_desc(const char * file, int line,\r
-                     const wchar_t * name, const wchar_t * creator) {\r
-    thread_info * t;\r
-    char * fn_copy;\r
-\r
-    perf_once();\r
-\r
-    t = malloc(sizeof(*t));\r
-    ZeroMemory(t, sizeof(*t));\r
-\r
-#ifdef _WIN32\r
-    t->thread = GetCurrentThreadId();\r
-#else\r
-#error Unsupported platform\r
-#endif\r
-\r
-    StringCbCopy(t->name, sizeof(t->name), name);\r
-    if (creator)\r
-        StringCbCopy(t->creator, sizeof(t->creator), creator);\r
-\r
-    if (file[0] == '.' && file[1] == '\\')\r
-        file += 2;\r
-\r
-    EnterCriticalSection(&cs_alloc);\r
-\r
-    fn_copy = hash_lookup(&fn_hash, file);\r
-    if (fn_copy == NULL) {\r
-        size_t cblen = 0;\r
-        if (FAILED(StringCbLengthA(file, MAX_PATH * sizeof(char),\r
-                                   &cblen)))\r
-            fn_copy = NULL;\r
-        else {\r
-            fn_copy = malloc(cblen + sizeof(char));\r
-            if (fn_copy) {\r
-                hash_bin * b;\r
-                int hv;\r
-\r
-                StringCbCopyA(fn_copy, cblen + sizeof(char), file);\r
-\r
-                hv = fn_hash.hash(fn_copy) % fn_hash.n;\r
-\r
-                b = malloc(sizeof(*b));\r
-                b->data = fn_copy;\r
-                b->key = fn_copy;\r
-                LINIT(b);\r
-                LPUSH(&fn_hash.bins[hv], b);\r
-            }\r
-        }\r
-    }\r
-\r
-    t->file = fn_copy;\r
-    t->line = line;\r
-\r
-    LPUSH(&threads, t);\r
-    LeaveCriticalSection(&cs_alloc);\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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<utils.h>
+#include<malloc.h>
+#include<stdio.h>
+#include<strsafe.h>
+#include<assert.h>
+
+#define HASHSIZE 1151
+#define ALLOCBLOCK 1024
+
+#define HASHPTR(p) (((size_t) (p)) % HASHSIZE)
+
+typedef struct tag_allocation {
+    const char * file;
+    int    line;
+    size_t size;
+    void * ptr;
+#ifdef _WIN32
+    DWORD  thread;
+#endif
+
+    LDCL(struct tag_allocation);
+} allocation;
+
+static allocation * ht[HASHSIZE];
+
+static allocation * next_alloc = NULL;
+static size_t       idx_next_alloc = 0;
+static allocation * free_alloc = NULL;
+
+typedef struct tag_thread_info {
+#ifdef _WIN32
+    DWORD thread;
+#else
+#error Unsupported platform
+#endif
+    wchar_t name[128];
+    wchar_t creator[128];
+
+    const char * file;
+    int line;
+
+    LDCL(struct tag_thread_info);
+} thread_info;
+
+static thread_info * threads = NULL;
+
+static hashtable fn_hash;
+
+static CRITICAL_SECTION cs_alloc;
+static LONG ctr = 0;
+static int  perf_ready = 0;
+
+static DWORD init_thread = 0;
+
+static khm_int32 hash_stringA(const void * vs) {
+    /* DJB algorithm */
+
+    khm_int32 hv = 13331;
+    char * c;
+
+    for (c = (char *) vs; *c; c++) {
+        hv = ((hv << 5) + hv) + (khm_int32) *c;
+    }
+
+    return (hv & KHM_INT32_MAX);
+}
+
+static khm_int32 hash_string_compA(const void * vs1,
+                                   const void * vs2) {
+    return strcmp((const char *) vs1, (const char *) vs2);
+}
+
+static void perf_once(void) {
+    if (InterlockedIncrement(&ctr) == 1) {
+        InitializeCriticalSection(&cs_alloc);
+        ZeroMemory(ht, sizeof(ht));
+
+        next_alloc = malloc(sizeof(allocation) * ALLOCBLOCK);
+        assert(next_alloc);
+        idx_next_alloc = 0;
+        free_alloc = NULL;
+
+        ZeroMemory(&fn_hash, sizeof(fn_hash));
+        fn_hash.n = 13;
+        fn_hash.hash = hash_stringA;
+        fn_hash.comp = hash_string_compA;
+        fn_hash.bins = calloc(sizeof(hash_bin *), fn_hash.n);
+
+        perf_ready = 1;
+    } else {
+        DWORD this_thread = GetCurrentThreadId();
+
+        while(!perf_ready &&
+              init_thread != this_thread) {
+            Sleep(0);           /* relinquish control to the thread
+                                   that is initializing the alloc
+                                   data. */
+        }
+    }
+}
+
+static allocation * get_allocation(void) {
+    allocation * a;
+
+    LPOP(&free_alloc, &a);
+    if (!a) {
+        if (idx_next_alloc == ALLOCBLOCK) {
+            next_alloc = malloc(sizeof(allocation) * ALLOCBLOCK);
+            assert(next_alloc);
+            idx_next_alloc = 0;
+        }
+
+        a = &next_alloc[idx_next_alloc];
+        idx_next_alloc++;
+    }
+
+    return a;
+}
+
+#define MAXCB_STR 32768
+
+KHMEXP wchar_t *
+perf_wcsdup(const char * file, int line, const wchar_t * str) {
+    size_t cb;
+    wchar_t * dest;
+
+    if (FAILED(StringCbLength(str, MAXCB_STR, &cb)))
+        return NULL;
+    cb += sizeof(wchar_t);
+
+    dest = (wchar_t *) perf_malloc(file, line, cb);
+    StringCbCopy(dest, cb, str);
+
+    return dest;
+}
+
+KHMEXP char *
+perf_strdup(const char * file, int line, const char * str) {
+    size_t cb;
+    char * dest;
+
+    if (FAILED(StringCbLengthA(str, MAXCB_STR, &cb)))
+        return NULL;
+    cb += sizeof(char);
+
+    dest = (char *) perf_malloc(file, line, cb);
+    StringCbCopyA(dest, cb, str);
+
+    return dest;
+}
+
+KHMEXP void *
+perf_calloc(const char * file, int line, size_t num, size_t size) {
+    void * ptr;
+    size_t tsize;
+
+    tsize = num * size;
+
+    ptr = perf_malloc(file,line,tsize);
+
+    if (ptr) {
+        ZeroMemory(ptr, tsize);
+    }
+
+    return ptr;
+}
+
+KHMEXP void * 
+perf_malloc(const char * file, int line, size_t s) {
+    allocation * a;
+    void * ptr;
+    size_t h;
+    char * fn_copy = NULL;
+
+    perf_once();
+
+    assert(s > 0);
+
+    EnterCriticalSection(&cs_alloc);
+    a = get_allocation();
+
+    ptr = malloc(s);
+
+    assert(ptr);                /* TODO: handle this gracefully */
+
+    if (file[0] == '.' && file[1] == '\\')
+        file += 2;
+
+    fn_copy = hash_lookup(&fn_hash, file);
+    if (fn_copy == NULL) {
+
+        size_t cblen = 0;
+        if (FAILED(StringCbLengthA(file, MAX_PATH * sizeof(char),
+                                   &cblen)))
+            fn_copy = NULL;
+        else {
+            fn_copy = malloc(cblen + sizeof(char));
+            if (fn_copy) {
+                hash_bin * b;
+                int hv;
+
+                StringCbCopyA(fn_copy, cblen + sizeof(char), file);
+
+                hv = fn_hash.hash(fn_copy) % fn_hash.n;
+
+                b = malloc(sizeof(*b));
+                b->data = fn_copy;
+                b->key = fn_copy;
+                LINIT(b);
+                LPUSH(&fn_hash.bins[hv], b);
+            }
+        }
+    }
+
+    a->file = fn_copy;
+    a->line = line;
+    a->size = s;
+    a->ptr = ptr;
+#ifdef _WIN32
+    a->thread = GetCurrentThreadId();
+#endif
+
+    h = HASHPTR(ptr);
+
+    LPUSH(&ht[h], a);
+    LeaveCriticalSection(&cs_alloc);
+
+    return ptr;
+}
+
+KHMEXP void *
+perf_realloc(const char * file, int line, void * data, size_t s) {
+    void * n_data;
+    allocation * a;
+    size_t h;
+
+    if (data == NULL)
+        return perf_malloc(file, line, s);
+
+    perf_once();
+    h = HASHPTR(data);
+
+    n_data = realloc(data, s);
+
+    assert(n_data);
+
+    EnterCriticalSection(&cs_alloc);
+    for (a = ht[h]; a; a = LNEXT(a)) {
+        if (a->ptr == data)
+            break;
+    }
+
+    assert(a);
+
+    LDELETE(&ht[h], a);
+
+    a->size = s;
+    a->ptr = n_data;
+
+    h = HASHPTR(n_data);
+    LPUSH(&ht[h], a);
+    LeaveCriticalSection(&cs_alloc);
+
+    return n_data;
+}
+
+KHMEXP void
+perf_free  (void * b) {
+    size_t h;
+    allocation * a;
+
+    perf_once();
+    h = HASHPTR(b);
+
+    EnterCriticalSection(&cs_alloc);
+    for(a = ht[h]; a; a = LNEXT(a)) {
+        if (a->ptr == b)
+            break;
+    }
+
+    assert(a);
+
+    LDELETE(&ht[h], a);
+    LPUSH(&free_alloc, a);
+    LeaveCriticalSection(&cs_alloc);
+}
+
+KHMEXP void KHMAPI
+perf_dump(FILE * f) {
+    size_t i;
+    allocation * a;
+    size_t total = 0;
+    thread_info * t;
+
+    perf_once();
+
+    EnterCriticalSection(&cs_alloc);
+
+    fprintf(f, "p00\t*** Threads ***\n");
+    fprintf(f, "p00\tFile\tLine\tThread\tName\tCreated by\n");
+
+    for (t = threads; t; t = LNEXT(t)) {
+        fprintf(f, "p01\t%s\t%6d\t%6d\t%S\t%S\n",
+                t->file, t->line, t->thread,
+                t->name, t->creator);
+    }
+
+    fprintf(f, "p02\t--- End Threads ---\n");
+
+    fprintf(f, "p10\t*** Leaked allocations list ***\n");
+    fprintf(f, "p11\tFile\tLine\tThread\tSize\tAddress\n");
+
+    for (i=0; i < HASHSIZE; i++) {
+        for (a = ht[i]; a; a = LNEXT(a)) {
+            fprintf(f, "p12\t%s\t%6d\t%6d\t%6d\t0x%p\n", a->file, a->line,
+                   a->thread, a->size, a->ptr);
+            total += a->size;
+        }
+    }
+
+    fprintf(f, "p20\t----------------------------------------\n");
+    fprintf(f, "p21\tTotal\t\t%d\n", total);
+    fprintf(f, "p22\t----------------- End ------------------\n");
+
+    LeaveCriticalSection(&cs_alloc);
+}
+
+KHMEXP void
+perf_set_thread_desc(const char * file, int line,
+                     const wchar_t * name, const wchar_t * creator) {
+    thread_info * t;
+    char * fn_copy;
+
+    perf_once();
+
+    t = malloc(sizeof(*t));
+    ZeroMemory(t, sizeof(*t));
+
+#ifdef _WIN32
+    t->thread = GetCurrentThreadId();
+#else
+#error Unsupported platform
+#endif
+
+    StringCbCopy(t->name, sizeof(t->name), name);
+    if (creator)
+        StringCbCopy(t->creator, sizeof(t->creator), creator);
+
+    if (file[0] == '.' && file[1] == '\\')
+        file += 2;
+
+    EnterCriticalSection(&cs_alloc);
+
+    fn_copy = hash_lookup(&fn_hash, file);
+    if (fn_copy == NULL) {
+        size_t cblen = 0;
+        if (FAILED(StringCbLengthA(file, MAX_PATH * sizeof(char),
+                                   &cblen)))
+            fn_copy = NULL;
+        else {
+            fn_copy = malloc(cblen + sizeof(char));
+            if (fn_copy) {
+                hash_bin * b;
+                int hv;
+
+                StringCbCopyA(fn_copy, cblen + sizeof(char), file);
+
+                hv = fn_hash.hash(fn_copy) % fn_hash.n;
+
+                b = malloc(sizeof(*b));
+                b->data = fn_copy;
+                b->key = fn_copy;
+                LINIT(b);
+                LPUSH(&fn_hash.bins[hv], b);
+            }
+        }
+    }
+
+    t->file = fn_copy;
+    t->line = line;
+
+    LPUSH(&threads, t);
+    LeaveCriticalSection(&cs_alloc);
+}
index a4a0f2750f361d85357817d17d7cc1c5bf4849d7..816a13681e8a61046f1168e122d484073786b266 100644 (file)
@@ -1,74 +1,74 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_PERFSTAT_H\r
-#define __KHIMAIRA_PERFSTAT_H\r
-\r
-#include<khdefs.h>\r
-\r
-#ifdef DEBUG\r
-#define PMALLOC(s) perf_malloc(__FILE__,__LINE__,s)\r
-#define PCALLOC(n,s) perf_calloc(__FILE__,__LINE__,n,s)\r
-#define PREALLOC(d,s) perf_realloc(__FILE__,__LINE__,d,s)\r
-#define PFREE(p)   perf_free(p)\r
-#define PDUMP(f)   perf_dump(f)\r
-#define PWCSDUP(s) perf_wcsdup(__FILE__,__LINE__,s)\r
-#define PSTRDUP(s) perf_strdup(__FILE__,__LINE__,s)\r
-#define PDESCTHREAD(n,c) perf_set_thread_desc(__FILE__,__LINE__,n,c);\r
-#else\r
-#define PMALLOC(s) malloc(s)\r
-#define PCALLOC(n,s) calloc(n,s)\r
-#define PREALLOC(d,s) realloc(d,s)\r
-#define PFREE(p)   free(p)\r
-#define PDUMP(f)   ((void) 0)\r
-#define PWCSDUP(s) wcsdup(s)\r
-#define PSTRDUP(s) strdup(s)\r
-#define PDESCTHREAD(n,c)\r
-#endif\r
-\r
-KHMEXP void *\r
-perf_malloc(const char * file, int line, size_t s);\r
-\r
-KHMEXP void *\r
-perf_realloc(const char * file, int line, void * data, size_t s);\r
-\r
-KHMEXP void\r
-perf_free  (void * b);\r
-\r
-KHMEXP wchar_t *\r
-perf_wcsdup(const char * file, int line, const wchar_t * str);\r
-\r
-KHMEXP char *\r
-perf_strdup(const char * file, int line, const char * str);\r
-\r
-KHMEXP void *\r
-perf_calloc(const char * file, int line, size_t num, size_t size);\r
-\r
-KHMEXP void\r
-perf_set_thread_desc(const char * file, int line,\r
-                     const wchar_t * name, const wchar_t * creator);\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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_PERFSTAT_H
+#define __KHIMAIRA_PERFSTAT_H
+
+#include<khdefs.h>
+
+#ifdef DEBUG
+#define PMALLOC(s) perf_malloc(__FILE__,__LINE__,s)
+#define PCALLOC(n,s) perf_calloc(__FILE__,__LINE__,n,s)
+#define PREALLOC(d,s) perf_realloc(__FILE__,__LINE__,d,s)
+#define PFREE(p)   perf_free(p)
+#define PDUMP(f)   perf_dump(f)
+#define PWCSDUP(s) perf_wcsdup(__FILE__,__LINE__,s)
+#define PSTRDUP(s) perf_strdup(__FILE__,__LINE__,s)
+#define PDESCTHREAD(n,c) perf_set_thread_desc(__FILE__,__LINE__,n,c);
+#else
+#define PMALLOC(s) malloc(s)
+#define PCALLOC(n,s) calloc(n,s)
+#define PREALLOC(d,s) realloc(d,s)
+#define PFREE(p)   free(p)
+#define PDUMP(f)   ((void) 0)
+#define PWCSDUP(s) wcsdup(s)
+#define PSTRDUP(s) strdup(s)
+#define PDESCTHREAD(n,c)
+#endif
+
+KHMEXP void *
+perf_malloc(const char * file, int line, size_t s);
+
+KHMEXP void *
+perf_realloc(const char * file, int line, void * data, size_t s);
+
+KHMEXP void
+perf_free  (void * b);
+
+KHMEXP wchar_t *
+perf_wcsdup(const char * file, int line, const wchar_t * str);
+
+KHMEXP char *
+perf_strdup(const char * file, int line, const char * str);
+
+KHMEXP void *
+perf_calloc(const char * file, int line, size_t num, size_t size);
+
+KHMEXP void
+perf_set_thread_desc(const char * file, int line,
+                     const wchar_t * name, const wchar_t * creator);
+
+#endif
index d811eb99dafb6af12494bc6b6928f848fe91e32a..c829ada7c9aeb5e43824866a4ceea54c82c80f2b 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#include<windows.h>\r
-#include<sync.h>\r
-#include<assert.h>\r
-\r
-#define LOCK_OPEN 0\r
-#define LOCK_READING 1\r
-#define LOCK_WRITING 2\r
-\r
-KHMEXP void KHMAPI InitializeRwLock(PRWLOCK pLock)\r
-{\r
-    pLock->locks = 0;\r
-    pLock->status = LOCK_OPEN;\r
-    InitializeCriticalSection(&(pLock->cs));\r
-    pLock->writewx = CreateEvent(NULL, \r
-                                 FALSE, /* Manual reset */\r
-                                 TRUE,  /* Initial state */\r
-                                 NULL);\r
-    pLock->readwx = CreateEvent(NULL,\r
-                                TRUE, /* Manual reset */\r
-                                TRUE, /* Initial state */\r
-                                NULL);\r
-}\r
-\r
-KHMEXP void KHMAPI DeleteRwLock(PRWLOCK pLock)\r
-{\r
-    EnterCriticalSection(&pLock->cs);\r
-\r
-    CloseHandle(pLock->readwx);\r
-    CloseHandle(pLock->writewx);\r
-    pLock->readwx = NULL;\r
-    pLock->writewx = NULL;\r
-\r
-    LeaveCriticalSection(&pLock->cs);\r
-    DeleteCriticalSection(&(pLock->cs));\r
-}\r
-\r
-KHMEXP void KHMAPI LockObtainRead(PRWLOCK pLock)\r
-{\r
-    while(1) {\r
-        WaitForSingleObject(pLock->readwx, INFINITE);\r
-        EnterCriticalSection(&pLock->cs);\r
-        if(pLock->status == LOCK_WRITING) {\r
-            LeaveCriticalSection(&(pLock->cs));\r
-            continue;\r
-        } else\r
-            break;\r
-    }\r
-    pLock->locks ++;\r
-    pLock->status = LOCK_READING;\r
-    ResetEvent(pLock->writewx);\r
-    LeaveCriticalSection(&(pLock->cs));\r
-}\r
-\r
-KHMEXP void KHMAPI LockReleaseRead(PRWLOCK pLock)\r
-{\r
-    EnterCriticalSection(&(pLock->cs));\r
-    assert(pLock->status == LOCK_READING);\r
-    pLock->locks--;\r
-    if(!pLock->locks) {\r
-        pLock->status = LOCK_OPEN;\r
-        SetEvent(pLock->readwx);\r
-        SetEvent(pLock->writewx);\r
-    }\r
-    LeaveCriticalSection(&(pLock->cs));\r
-}\r
-\r
-KHMEXP void KHMAPI LockObtainWrite(PRWLOCK pLock)\r
-{\r
-    EnterCriticalSection(&(pLock->cs));\r
-    if(pLock->status == LOCK_WRITING && \r
-       pLock->writer == GetCurrentThreadId()) {\r
-        pLock->locks++;\r
-        LeaveCriticalSection(&(pLock->cs));\r
-        return;\r
-    }\r
-    LeaveCriticalSection(&(pLock->cs));\r
-    while(1) {\r
-        WaitForSingleObject(pLock->writewx, INFINITE);\r
-        EnterCriticalSection(&(pLock->cs));\r
-        if(pLock->status == LOCK_OPEN)\r
-            break;\r
-        LeaveCriticalSection(&(pLock->cs));\r
-    }\r
-    pLock->status = LOCK_WRITING;\r
-    pLock->locks++;\r
-    pLock->writer = GetCurrentThreadId();\r
-    ResetEvent(pLock->readwx);\r
-    ResetEvent(pLock->writewx);\r
-    LeaveCriticalSection(&(pLock->cs));\r
-}\r
-\r
-KHMEXP void KHMAPI LockReleaseWrite(PRWLOCK pLock)\r
-{\r
-    EnterCriticalSection(&(pLock->cs));\r
-    assert(pLock->status == LOCK_WRITING);\r
-    pLock->locks--;\r
-    if(!pLock->locks) {\r
-        pLock->status = LOCK_OPEN;\r
-        pLock->writer = 0;\r
-        SetEvent(pLock->readwx);\r
-        SetEvent(pLock->writewx);\r
-    }\r
-    LeaveCriticalSection(&(pLock->cs));\r
-}\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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)
+{
+    EnterCriticalSection(&pLock->cs);
+
+    CloseHandle(pLock->readwx);
+    CloseHandle(pLock->writewx);
+    pLock->readwx = NULL;
+    pLock->writewx = NULL;
+
+    LeaveCriticalSection(&pLock->cs);
+    DeleteCriticalSection(&(pLock->cs));
+}
+
+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++;
+    pLock->writer = GetCurrentThreadId();
+    ResetEvent(pLock->readwx);
+    ResetEvent(pLock->writewx);
+    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;
+        pLock->writer = 0;
+        SetEvent(pLock->readwx);
+        SetEvent(pLock->writewx);
+    }
+    LeaveCriticalSection(&(pLock->cs));
+}
index e423e6766fdcfaf3e291b94cc79ed59df059126d..a95db207ec5a3c789744b8d7c44c203251973d2f 100644 (file)
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_SYNC_H\r
-#define __KHIMAIRA_SYNC_H\r
-\r
-#include<khdefs.h>\r
-\r
-/*! \addtogroup util\r
-    @{ */\r
-\r
-/*! \defgroup util_sync Synchronization\r
-    @{*/\r
-\r
-/*! \brief A read/write lock\r
-\r
-    A classic read/write lock.  Allows multiple readers or a single\r
-    writer to access a protected object.  Readers will wait for any\r
-    pending writer to release the lock, while a writer will wait for\r
-    any pending readers to release the lock.\r
-*/\r
-typedef struct tag_rwlock {\r
-    int locks;\r
-    int status;\r
-    CRITICAL_SECTION cs;\r
-    HANDLE readwx;\r
-    HANDLE writewx;\r
-\r
-    DWORD writer;               /* TID of writer thread */\r
-} rw_lock_t;\r
-\r
-typedef rw_lock_t RWLOCK, *PRWLOCK;\r
-\r
-/*! \brief Initialize a read/write lock.\r
-\r
-    A lock <b>must</b> be initialized before it can be used.\r
-    Initializing the lock does not grant the caller any locks on the\r
-    object.\r
-*/\r
-KHMEXP void KHMAPI InitializeRwLock(PRWLOCK pLock);\r
-\r
-/*! \brief Delete a read/write lock\r
-\r
-    Once the application is done using the read/write lock, it must be\r
-    deleted with a call to DeleteRwLock()\r
-*/\r
-KHMEXP void KHMAPI DeleteRwLock(PRWLOCK pLock);\r
-\r
-/*! \brief Obtains a read lock on the read/write lock\r
-\r
-    Multiple readers can obtain read locks on the same r/w lock.\r
-    However, if any thread attempts to obtain a write lock on the\r
-    object, it will wait until all readers have released the read\r
-    locks.\r
-\r
-    Call LockReleaseRead() to release the read lock.  While the same\r
-    thread may obtain multiple read locks on the same object, each\r
-    call to LockObtainRead() must have a corresponding call to\r
-    LockReleaseRead() to properly relinquish the lock.\r
-\r
-    \see LockReleaseRead()\r
-*/\r
-KHMEXP void KHMAPI LockObtainRead(PRWLOCK pLock);\r
-\r
-/*! \brief Relase a read lock obtained on a read/write lock\r
-\r
-    Each call to LockObtainRead() must have a corresponding call to\r
-    LockReleaseRead().  Once all read locks are released, any threads\r
-    waiting on write locks on the object will be woken and assigned a\r
-    write lock.\r
-\r
-    \see LockObtainRead()\r
-*/\r
-KHMEXP void KHMAPI LockReleaseRead(PRWLOCK pLock);\r
-\r
-/*! \brief Obtains a write lock on the read/write lock\r
-\r
-    Only a single writer is allowed to lock a single r/w lock.\r
-    However, if any thread attempts to obtain a read lock on the\r
-    object, it will wait until the writer has released the lock.\r
-\r
-    Call LockReleaseWrite() to release the write lock.  While the same\r
-    thread may obtain multiple write locks on the same object, each\r
-    call to LockObtainWrite() must have a corresponding call to\r
-    LockReleaseWrite() to properly relinquish the lock.\r
-\r
-    \see LockReleaseWrite()\r
-*/\r
-KHMEXP void KHMAPI LockObtainWrite(PRWLOCK pLock);\r
-\r
-/*! \brief Relase a write lock obtained on a read/write lock\r
-\r
-    Each call to LockObtainWrite() must have a corresponding call to\r
-    LockReleaseWrite().  Once the write lock is released, any threads\r
-    waiting for read or write locks on the object will be woken and\r
-    assigned the proper lock.\r
-\r
-    \see LockObtainWrite()\r
-*/\r
-KHMEXP void KHMAPI LockReleaseWrite(PRWLOCK pLock);\r
-\r
-/*@}*/\r
-/*@}*/\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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
index 4ba86f6edb08090dcd367cf9fd9fdcda158ee142..4c3bef52ffe52b3ac25489f0c21b184fb99c8118 100644 (file)
@@ -1,37 +1,37 @@
-/*\r
- * Copyright (c) 2005 Massachusetts Institute of Technology\r
- *\r
- * Permission is hereby granted, free of charge, to any person\r
- * obtaining a copy of this software and associated documentation\r
- * files (the "Software"), to deal in the Software without\r
- * restriction, including without limitation the rights to use, copy,\r
- * modify, merge, publish, distribute, sublicense, and/or sell copies\r
- * of the Software, and to permit persons to whom the Software is\r
- * furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be\r
- * included in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
- * SOFTWARE.\r
- */\r
-\r
-/* $Id$ */\r
-\r
-#ifndef __KHIMAIRA_UTIL_H\r
-#define __KHIMAIRA_UTIL_H\r
-\r
-/*! \defgroup util Utilities\r
- */\r
-#include<hashtable.h>\r
-#include<sync.h>\r
-#include<mstring.h>\r
-#include<perfstat.h>\r
-\r
-#endif\r
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF 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>
+#include<perfstat.h>
+
+#endif
index ea9410ea6168391c22c289cf92ed75f2347a027f..c07bd81c3f2c91b016f8cd43aaeba79033f12b7a 100644 (file)
-/*\r
-Copyright 2005,2006 by the Massachusetts Institute of Technology\r
-Copyright 2007 by Secure Endpoints Inc.\r
-\r
-All rights reserved.\r
-\r
-Permission to use, copy, modify, and distribute this software and its\r
-documentation for any purpose and without fee is hereby granted,\r
-provided that the above copyright notice appear in all copies and that\r
-both that copyright notice and this permission notice appear in\r
-supporting documentation, and that the name of the Massachusetts\r
-Institute of Technology (M.I.T.) not be used in advertising or publicity\r
-pertaining to distribution of the software without specific, written\r
-prior permission.\r
-\r
-M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
-ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
-M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
-ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
-WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
-ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
-SOFTWARE.\r
-\r
-*/\r
-\r
-#include "kfwlogon.h"\r
-#include <windows.h>\r
-#include <Aclapi.h>\r
-#include <userenv.h>\r
-#include <Sddl.h>\r
-\r
-#include <io.h>\r
-#include <sys/stat.h>\r
-#include <sys/types.h>\r
-#include <fcntl.h>\r
-\r
-#include <winsock2.h>\r
-#include <lm.h>\r
-#include <nb30.h>\r
-\r
-#include <errno.h>\r
-#include <malloc.h>\r
-\r
-\r
-/* Function Pointer Declarations for Delayed Loading */\r
-// CCAPI\r
-DECL_FUNC_PTR(cc_initialize);\r
-DECL_FUNC_PTR(cc_shutdown);\r
-DECL_FUNC_PTR(cc_get_NC_info);\r
-DECL_FUNC_PTR(cc_free_NC_info);\r
-\r
-// leash functions\r
-DECL_FUNC_PTR(Leash_get_default_lifetime);\r
-DECL_FUNC_PTR(Leash_get_default_forwardable);\r
-DECL_FUNC_PTR(Leash_get_default_renew_till);\r
-DECL_FUNC_PTR(Leash_get_default_noaddresses);\r
-DECL_FUNC_PTR(Leash_get_default_proxiable);\r
-DECL_FUNC_PTR(Leash_get_default_publicip);\r
-DECL_FUNC_PTR(Leash_get_default_use_krb4);\r
-DECL_FUNC_PTR(Leash_get_default_life_min);\r
-DECL_FUNC_PTR(Leash_get_default_life_max);\r
-DECL_FUNC_PTR(Leash_get_default_renew_min);\r
-DECL_FUNC_PTR(Leash_get_default_renew_max);\r
-DECL_FUNC_PTR(Leash_get_default_renewable);\r
-DECL_FUNC_PTR(Leash_get_default_mslsa_import);\r
-\r
-// krb5 functions\r
-DECL_FUNC_PTR(krb5_change_password);\r
-DECL_FUNC_PTR(krb5_get_init_creds_opt_init);\r
-DECL_FUNC_PTR(krb5_get_init_creds_opt_set_tkt_life);\r
-DECL_FUNC_PTR(krb5_get_init_creds_opt_set_renew_life);\r
-DECL_FUNC_PTR(krb5_get_init_creds_opt_set_forwardable);\r
-DECL_FUNC_PTR(krb5_get_init_creds_opt_set_proxiable);\r
-DECL_FUNC_PTR(krb5_get_init_creds_opt_set_address_list);\r
-DECL_FUNC_PTR(krb5_get_init_creds_password);\r
-DECL_FUNC_PTR(krb5_build_principal_ext);\r
-DECL_FUNC_PTR(krb5_cc_get_name);\r
-DECL_FUNC_PTR(krb5_cc_resolve);\r
-DECL_FUNC_PTR(krb5_cc_default);\r
-DECL_FUNC_PTR(krb5_cc_default_name);\r
-DECL_FUNC_PTR(krb5_cc_set_default_name);\r
-DECL_FUNC_PTR(krb5_cc_initialize);\r
-DECL_FUNC_PTR(krb5_cc_destroy);\r
-DECL_FUNC_PTR(krb5_cc_close);\r
-DECL_FUNC_PTR(krb5_cc_store_cred);\r
-DECL_FUNC_PTR(krb5_cc_copy_creds);\r
-DECL_FUNC_PTR(krb5_cc_retrieve_cred);\r
-DECL_FUNC_PTR(krb5_cc_get_principal);\r
-DECL_FUNC_PTR(krb5_cc_start_seq_get);\r
-DECL_FUNC_PTR(krb5_cc_next_cred);\r
-DECL_FUNC_PTR(krb5_cc_end_seq_get);\r
-DECL_FUNC_PTR(krb5_cc_remove_cred);\r
-DECL_FUNC_PTR(krb5_cc_set_flags);\r
-DECL_FUNC_PTR(krb5_cc_get_type);\r
-DECL_FUNC_PTR(krb5_free_context);\r
-DECL_FUNC_PTR(krb5_free_cred_contents);\r
-DECL_FUNC_PTR(krb5_free_principal);\r
-DECL_FUNC_PTR(krb5_get_in_tkt_with_password);\r
-DECL_FUNC_PTR(krb5_init_context);\r
-DECL_FUNC_PTR(krb5_parse_name);\r
-DECL_FUNC_PTR(krb5_timeofday);\r
-DECL_FUNC_PTR(krb5_timestamp_to_sfstring);\r
-DECL_FUNC_PTR(krb5_unparse_name);\r
-DECL_FUNC_PTR(krb5_get_credentials);\r
-DECL_FUNC_PTR(krb5_mk_req);\r
-DECL_FUNC_PTR(krb5_sname_to_principal);\r
-DECL_FUNC_PTR(krb5_get_credentials_renew);\r
-DECL_FUNC_PTR(krb5_free_data);\r
-DECL_FUNC_PTR(krb5_free_data_contents);\r
-DECL_FUNC_PTR(krb5_free_unparsed_name);\r
-DECL_FUNC_PTR(krb5_os_localaddr);\r
-DECL_FUNC_PTR(krb5_copy_keyblock_contents);\r
-DECL_FUNC_PTR(krb5_copy_data);\r
-DECL_FUNC_PTR(krb5_free_creds);\r
-DECL_FUNC_PTR(krb5_build_principal);\r
-DECL_FUNC_PTR(krb5_get_renewed_creds);\r
-DECL_FUNC_PTR(krb5_get_default_config_files);\r
-DECL_FUNC_PTR(krb5_free_config_files);\r
-DECL_FUNC_PTR(krb5_get_default_realm);\r
-DECL_FUNC_PTR(krb5_free_default_realm);\r
-DECL_FUNC_PTR(krb5_free_ticket);\r
-DECL_FUNC_PTR(krb5_decode_ticket);\r
-DECL_FUNC_PTR(krb5_get_host_realm);\r
-DECL_FUNC_PTR(krb5_free_host_realm);\r
-DECL_FUNC_PTR(krb5_free_addresses);\r
-DECL_FUNC_PTR(krb5_c_random_make_octets);\r
-\r
-// ComErr functions\r
-DECL_FUNC_PTR(com_err);\r
-DECL_FUNC_PTR(error_message);\r
-\r
-// Profile functions\r
-DECL_FUNC_PTR(profile_init);\r
-DECL_FUNC_PTR(profile_release);\r
-DECL_FUNC_PTR(profile_get_subsection_names);\r
-DECL_FUNC_PTR(profile_free_list);\r
-DECL_FUNC_PTR(profile_get_string);\r
-DECL_FUNC_PTR(profile_release_string);\r
-\r
-// Service functions\r
-DECL_FUNC_PTR(OpenSCManagerA);\r
-DECL_FUNC_PTR(OpenServiceA);\r
-DECL_FUNC_PTR(QueryServiceStatus);\r
-DECL_FUNC_PTR(CloseServiceHandle);\r
-DECL_FUNC_PTR(LsaNtStatusToWinError);\r
-\r
-// LSA Functions\r
-DECL_FUNC_PTR(LsaConnectUntrusted);\r
-DECL_FUNC_PTR(LsaLookupAuthenticationPackage);\r
-DECL_FUNC_PTR(LsaCallAuthenticationPackage);\r
-DECL_FUNC_PTR(LsaFreeReturnBuffer);\r
-DECL_FUNC_PTR(LsaGetLogonSessionData);\r
-\r
-// CCAPI\r
-FUNC_INFO ccapi_fi[] = {\r
-    MAKE_FUNC_INFO(cc_initialize),\r
-    MAKE_FUNC_INFO(cc_shutdown),\r
-    MAKE_FUNC_INFO(cc_get_NC_info),\r
-    MAKE_FUNC_INFO(cc_free_NC_info),\r
-    END_FUNC_INFO\r
-};\r
-\r
-FUNC_INFO leash_fi[] = {\r
-    MAKE_FUNC_INFO(Leash_get_default_lifetime),\r
-    MAKE_FUNC_INFO(Leash_get_default_renew_till),\r
-    MAKE_FUNC_INFO(Leash_get_default_forwardable),\r
-    MAKE_FUNC_INFO(Leash_get_default_noaddresses),\r
-    MAKE_FUNC_INFO(Leash_get_default_proxiable),\r
-    MAKE_FUNC_INFO(Leash_get_default_publicip),\r
-    MAKE_FUNC_INFO(Leash_get_default_use_krb4),\r
-    MAKE_FUNC_INFO(Leash_get_default_life_min),\r
-    MAKE_FUNC_INFO(Leash_get_default_life_max),\r
-    MAKE_FUNC_INFO(Leash_get_default_renew_min),\r
-    MAKE_FUNC_INFO(Leash_get_default_renew_max),\r
-    MAKE_FUNC_INFO(Leash_get_default_renewable),\r
-    END_FUNC_INFO\r
-};\r
-\r
-FUNC_INFO leash_opt_fi[] = {\r
-    MAKE_FUNC_INFO(Leash_get_default_mslsa_import),\r
-    END_FUNC_INFO\r
-};\r
-\r
-FUNC_INFO k5_fi[] = {\r
-    MAKE_FUNC_INFO(krb5_change_password),\r
-    MAKE_FUNC_INFO(krb5_get_init_creds_opt_init),\r
-    MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_tkt_life),\r
-    MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_renew_life),\r
-    MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_forwardable),\r
-    MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_proxiable),\r
-    MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_address_list),\r
-    MAKE_FUNC_INFO(krb5_get_init_creds_password),\r
-    MAKE_FUNC_INFO(krb5_build_principal_ext),\r
-    MAKE_FUNC_INFO(krb5_cc_get_name),\r
-    MAKE_FUNC_INFO(krb5_cc_resolve),\r
-    MAKE_FUNC_INFO(krb5_cc_default),\r
-    MAKE_FUNC_INFO(krb5_cc_default_name),\r
-    MAKE_FUNC_INFO(krb5_cc_set_default_name),\r
-    MAKE_FUNC_INFO(krb5_cc_initialize),\r
-    MAKE_FUNC_INFO(krb5_cc_destroy),\r
-    MAKE_FUNC_INFO(krb5_cc_close),\r
-    MAKE_FUNC_INFO(krb5_cc_copy_creds),\r
-    MAKE_FUNC_INFO(krb5_cc_store_cred),\r
-    MAKE_FUNC_INFO(krb5_cc_retrieve_cred),\r
-    MAKE_FUNC_INFO(krb5_cc_get_principal),\r
-    MAKE_FUNC_INFO(krb5_cc_start_seq_get),\r
-    MAKE_FUNC_INFO(krb5_cc_next_cred),\r
-    MAKE_FUNC_INFO(krb5_cc_end_seq_get),\r
-    MAKE_FUNC_INFO(krb5_cc_remove_cred),\r
-    MAKE_FUNC_INFO(krb5_cc_set_flags),\r
-    MAKE_FUNC_INFO(krb5_cc_get_type),\r
-    MAKE_FUNC_INFO(krb5_free_context),\r
-    MAKE_FUNC_INFO(krb5_free_cred_contents),\r
-    MAKE_FUNC_INFO(krb5_free_principal),\r
-    MAKE_FUNC_INFO(krb5_get_in_tkt_with_password),\r
-    MAKE_FUNC_INFO(krb5_init_context),\r
-    MAKE_FUNC_INFO(krb5_parse_name),\r
-    MAKE_FUNC_INFO(krb5_timeofday),\r
-    MAKE_FUNC_INFO(krb5_timestamp_to_sfstring),\r
-    MAKE_FUNC_INFO(krb5_unparse_name),\r
-    MAKE_FUNC_INFO(krb5_get_credentials),\r
-    MAKE_FUNC_INFO(krb5_mk_req),\r
-    MAKE_FUNC_INFO(krb5_sname_to_principal),\r
-    MAKE_FUNC_INFO(krb5_get_credentials_renew),\r
-    MAKE_FUNC_INFO(krb5_free_data),\r
-    MAKE_FUNC_INFO(krb5_free_data_contents),\r
-    MAKE_FUNC_INFO(krb5_free_unparsed_name),\r
-    MAKE_FUNC_INFO(krb5_os_localaddr),\r
-    MAKE_FUNC_INFO(krb5_copy_keyblock_contents),\r
-    MAKE_FUNC_INFO(krb5_copy_data),\r
-    MAKE_FUNC_INFO(krb5_free_creds),\r
-    MAKE_FUNC_INFO(krb5_build_principal),\r
-    MAKE_FUNC_INFO(krb5_get_renewed_creds),\r
-    MAKE_FUNC_INFO(krb5_free_addresses),\r
-    MAKE_FUNC_INFO(krb5_get_default_config_files),\r
-    MAKE_FUNC_INFO(krb5_free_config_files),\r
-    MAKE_FUNC_INFO(krb5_get_default_realm),\r
-    MAKE_FUNC_INFO(krb5_free_default_realm),\r
-    MAKE_FUNC_INFO(krb5_free_ticket),\r
-    MAKE_FUNC_INFO(krb5_decode_ticket),\r
-    MAKE_FUNC_INFO(krb5_get_host_realm),\r
-    MAKE_FUNC_INFO(krb5_free_host_realm),\r
-    MAKE_FUNC_INFO(krb5_free_addresses),\r
-    MAKE_FUNC_INFO(krb5_c_random_make_octets),\r
-    END_FUNC_INFO\r
-};\r
-\r
-FUNC_INFO profile_fi[] = {\r
-        MAKE_FUNC_INFO(profile_init),\r
-        MAKE_FUNC_INFO(profile_release),\r
-        MAKE_FUNC_INFO(profile_get_subsection_names),\r
-        MAKE_FUNC_INFO(profile_free_list),\r
-        MAKE_FUNC_INFO(profile_get_string),\r
-        MAKE_FUNC_INFO(profile_release_string),\r
-        END_FUNC_INFO\r
-};\r
-\r
-FUNC_INFO ce_fi[] = {\r
-    MAKE_FUNC_INFO(com_err),\r
-    MAKE_FUNC_INFO(error_message),\r
-    END_FUNC_INFO\r
-};\r
-\r
-FUNC_INFO service_fi[] = {\r
-    MAKE_FUNC_INFO(OpenSCManagerA),\r
-    MAKE_FUNC_INFO(OpenServiceA),\r
-    MAKE_FUNC_INFO(QueryServiceStatus),\r
-    MAKE_FUNC_INFO(CloseServiceHandle),\r
-    MAKE_FUNC_INFO(LsaNtStatusToWinError),\r
-    END_FUNC_INFO\r
-};\r
-\r
-FUNC_INFO lsa_fi[] = {\r
-    MAKE_FUNC_INFO(LsaConnectUntrusted),\r
-    MAKE_FUNC_INFO(LsaLookupAuthenticationPackage),\r
-    MAKE_FUNC_INFO(LsaCallAuthenticationPackage),\r
-    MAKE_FUNC_INFO(LsaFreeReturnBuffer),\r
-    MAKE_FUNC_INFO(LsaGetLogonSessionData),\r
-    END_FUNC_INFO\r
-};\r
-\r
-/* Static Declarations */\r
-static int       inited = 0;\r
-static HINSTANCE hKrb5 = 0;\r
-static HINSTANCE hKrb524 = 0;\r
-static HINSTANCE hSecur32 = 0;\r
-static HINSTANCE hAdvApi32 = 0;\r
-static HINSTANCE hComErr = 0;\r
-static HINSTANCE hService = 0;\r
-static HINSTANCE hProfile = 0;\r
-static HINSTANCE hLeash = 0;\r
-static HINSTANCE hLeashOpt = 0;\r
-static HINSTANCE hCCAPI = 0;\r
-\r
-static DWORD TraceOption = 0;\r
-static HANDLE hDLL;\r
-\r
-BOOL IsDebugLogging(void)\r
-{\r
-    DWORD LSPsize;\r
-    HKEY NPKey;\r
-    DWORD dwDebug = FALSE;\r
-\r
-    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, \r
-                    "System\\CurrentControlSet\\Services\\MIT Kerberos\\NetworkProvider", \r
-                    0, KEY_QUERY_VALUE, &NPKey) == ERROR_SUCCESS) \r
-    {\r
-       LSPsize=sizeof(dwDebug);\r
-       if (RegQueryValueEx(NPKey, "Debug", NULL, NULL, (LPBYTE)&dwDebug, &LSPsize) != ERROR_SUCCESS) \r
-       {\r
-           dwDebug = FALSE;\r
-       }\r
-       RegCloseKey (NPKey);\r
-    }\r
-\r
-    return(dwDebug ? TRUE : FALSE);\r
-}\r
-\r
-void DebugEvent0(char *a) \r
-{\r
-    HANDLE h; char *ptbuf[1];\r
-    \r
-    if (IsDebugLogging()) {\r
-       h = RegisterEventSource(NULL, KFW_LOGON_EVENT_NAME);\r
-       if (h) {\r
-            ptbuf[0] = a;\r
-            ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL);\r
-            DeregisterEventSource(h);\r
-        }\r
-    }\r
-}\r
-\r
-#define MAXBUF_ 512\r
-void DebugEvent(char *b,...) \r
-{\r
-    HANDLE h; char *ptbuf[1],buf[MAXBUF_+1];\r
-    va_list marker;\r
-\r
-    if (IsDebugLogging()) {\r
-       h = RegisterEventSource(NULL, KFW_LOGON_EVENT_NAME);\r
-        if (h) {\r
-            va_start(marker,b);\r
-            StringCbVPrintf(buf, MAXBUF_+1,b,marker);\r
-            buf[MAXBUF_] = '\0';\r
-            ptbuf[0] = buf;\r
-            ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL);\r
-            DeregisterEventSource(h);\r
-            va_end(marker);\r
-        }\r
-    }\r
-}\r
-\r
-void\r
-UnloadFuncs(\r
-    FUNC_INFO fi[], \r
-    HINSTANCE h\r
-    )\r
-{\r
-    int n;\r
-    if (fi)\r
-        for (n = 0; fi[n].func_ptr_var; n++)\r
-            *(fi[n].func_ptr_var) = 0;\r
-    if (h) FreeLibrary(h);\r
-}\r
-\r
-int\r
-LoadFuncs(\r
-    const char* dll_name, \r
-    FUNC_INFO fi[], \r
-    HINSTANCE* ph,  // [out, optional] - DLL handle\r
-    int* pindex,    // [out, optional] - index of last func loaded (-1 if none)\r
-    int cleanup,    // cleanup function pointers and unload on error\r
-    int go_on,      // continue loading even if some functions cannot be loaded\r
-    int silent      // do not pop-up a system dialog if DLL cannot be loaded\r
-    )\r
-{\r
-    HINSTANCE h;\r
-    int i, n, last_i;\r
-    int error = 0;\r
-    UINT em;\r
-\r
-    if (ph) *ph = 0;\r
-    if (pindex) *pindex = -1;\r
-\r
-    for (n = 0; fi[n].func_ptr_var; n++)\r
-       *(fi[n].func_ptr_var) = 0;\r
-\r
-    if (silent)\r
-       em = SetErrorMode(SEM_FAILCRITICALERRORS);\r
-    h = LoadLibrary(dll_name);\r
-    if (silent)\r
-        SetErrorMode(em);\r
-\r
-    if (!h)\r
-        return 0;\r
-\r
-    last_i = -1;\r
-    for (i = 0; (go_on || !error) && (i < n); i++)\r
-    {\r
-       void* p = (void*)GetProcAddress(h, fi[i].func_name);\r
-       if (!p)\r
-           error = 1;\r
-        else\r
-        {\r
-            last_i = i;\r
-           *(fi[i].func_ptr_var) = p;\r
-        }\r
-    }\r
-    if (pindex) *pindex = last_i;\r
-    if (error && cleanup && !go_on) {\r
-       for (i = 0; i < n; i++) {\r
-           *(fi[i].func_ptr_var) = 0;\r
-       }\r
-       FreeLibrary(h);\r
-       return 0;\r
-    }\r
-    if (ph) *ph = h;\r
-    if (error) return 0;\r
-    return 1;\r
-}\r
-\r
-static HANDLE hInitMutex = NULL;\r
-static BOOL bInit = FALSE;\r
-\r
-/* KFW_initialize cannot be called from DllEntryPoint */\r
-void\r
-KFW_initialize(void)\r
-{\r
-    static int inited = 0;\r
-\r
-    if ( !inited ) {\r
-        char mutexName[MAX_PATH];\r
-        HANDLE hMutex = NULL;\r
-\r
-        sprintf(mutexName, "AFS KFW Init pid=%d", getpid());\r
-        \r
-        hMutex = CreateMutex( NULL, TRUE, mutexName );\r
-        if ( GetLastError() == ERROR_ALREADY_EXISTS ) {\r
-            if ( WaitForSingleObject( hMutex, INFINITE ) != WAIT_OBJECT_0 ) {\r
-                return;\r
-            }\r
-        }\r
-        if ( !inited ) {\r
-            inited = 1;\r
-            LoadFuncs(KRB5_DLL, k5_fi, &hKrb5, 0, 1, 0, 0);\r
-            LoadFuncs(COMERR_DLL, ce_fi, &hComErr, 0, 0, 1, 0);\r
-            LoadFuncs(SERVICE_DLL, service_fi, &hService, 0, 1, 0, 0);\r
-            LoadFuncs(SECUR32_DLL, lsa_fi, &hSecur32, 0, 1, 1, 1);\r
-            LoadFuncs(PROFILE_DLL, profile_fi, &hProfile, 0, 1, 0, 0);\r
-            LoadFuncs(LEASH_DLL, leash_fi, &hLeash, 0, 1, 0, 0);\r
-            LoadFuncs(CCAPI_DLL, ccapi_fi, &hCCAPI, 0, 1, 0, 0);\r
-            LoadFuncs(LEASH_DLL, leash_opt_fi, &hLeashOpt, 0, 1, 0, 0);\r
-        }\r
-        ReleaseMutex(hMutex);\r
-        CloseHandle(hMutex);\r
-    }\r
-}\r
-\r
-void\r
-KFW_cleanup(void)\r
-{\r
-    if (hLeashOpt)\r
-        FreeLibrary(hLeashOpt);\r
-    if (hCCAPI)\r
-        FreeLibrary(hCCAPI);\r
-    if (hLeash)\r
-        FreeLibrary(hLeash);\r
-    if (hKrb524)\r
-        FreeLibrary(hKrb524);\r
-    if (hSecur32)\r
-        FreeLibrary(hSecur32);\r
-    if (hService)\r
-        FreeLibrary(hService);\r
-    if (hComErr)\r
-        FreeLibrary(hComErr);\r
-    if (hProfile)\r
-        FreeLibrary(hProfile);\r
-    if (hKrb5)\r
-        FreeLibrary(hKrb5);\r
-}\r
-\r
-\r
-int \r
-KFW_is_available(void)\r
-{\r
-    KFW_initialize();\r
-    if ( hKrb5 && hComErr && hService && \r
-#ifdef USE_MS2MIT\r
-         hSecur32 && \r
-#endif /* USE_MS2MIT */\r
-         hProfile && hLeash && hCCAPI )\r
-        return TRUE;\r
-\r
-    return FALSE;\r
-}\r
-\r
-/* Given a principal return an existing ccache or create one and return */\r
-int\r
-KFW_get_ccache(krb5_context alt_ctx, krb5_principal principal, krb5_ccache * cc)\r
-{\r
-    krb5_context ctx;\r
-    char * pname = 0;\r
-    char * ccname = 0;\r
-    krb5_error_code code;\r
-\r
-    if (!pkrb5_init_context)\r
-        return 0;\r
-\r
-    if ( alt_ctx ) {\r
-        ctx = alt_ctx;\r
-    } else {\r
-        code = pkrb5_init_context(&ctx);\r
-        if (code) goto cleanup;\r
-    }\r
-\r
-    if ( principal ) {\r
-        code = pkrb5_unparse_name(ctx, principal, &pname);\r
-        if (code) goto cleanup;\r
-\r
-       ccname = (char *)malloc(strlen(pname) + 5);\r
-       sprintf(ccname,"API:%s",pname);\r
-\r
-       DebugEvent0(ccname);\r
-       code = pkrb5_cc_resolve(ctx, ccname, cc);\r
-    } else {\r
-        code = pkrb5_cc_default(ctx, cc);\r
-        if (code) goto cleanup;\r
-    }\r
-\r
-  cleanup:\r
-    if (ccname)\r
-        free(ccname);\r
-    if (pname)\r
-        pkrb5_free_unparsed_name(ctx,pname);\r
-    if (ctx && (ctx != alt_ctx))\r
-        pkrb5_free_context(ctx);\r
-    return(code);\r
-}\r
-\r
-\r
-int\r
-KFW_kinit( krb5_context alt_ctx,\r
-            krb5_ccache  alt_cc,\r
-            HWND hParent,\r
-            char *principal_name,\r
-            char *password,\r
-            krb5_deltat lifetime,\r
-            DWORD                       forwardable,\r
-            DWORD                       proxiable,\r
-            krb5_deltat                 renew_life,\r
-            DWORD                       addressless,\r
-            DWORD                       publicIP\r
-            )\r
-{\r
-    krb5_error_code                    code = 0;\r
-    krb5_context                       ctx = 0;\r
-    krb5_ccache                                cc = 0;\r
-    krb5_principal                     me = 0;\r
-    char*                       name = 0;\r
-    krb5_creds                         my_creds;\r
-    krb5_get_init_creds_opt     options;\r
-    krb5_address **             addrs = NULL;\r
-    int                         i = 0, addr_count = 0;\r
-\r
-    if (!pkrb5_init_context)\r
-        return 0;\r
-\r
-    pkrb5_get_init_creds_opt_init(&options);\r
-    memset(&my_creds, 0, sizeof(my_creds));\r
-\r
-    if (alt_ctx)\r
-    {\r
-        ctx = alt_ctx;\r
-    }\r
-    else\r
-    {\r
-        code = pkrb5_init_context(&ctx);\r
-        if (code) goto cleanup;\r
-    }\r
-\r
-    if ( alt_cc ) {\r
-        cc = alt_cc;\r
-    } else {\r
-        code = pkrb5_cc_default(ctx, &cc);  \r
-        if (code) goto cleanup;\r
-    }\r
-\r
-    code = pkrb5_parse_name(ctx, principal_name, &me);\r
-    if (code) \r
-       goto cleanup;\r
-\r
-    code = pkrb5_unparse_name(ctx, me, &name);\r
-    if (code) \r
-       goto cleanup;\r
-\r
-    if (lifetime == 0)\r
-        lifetime = pLeash_get_default_lifetime();\r
-    lifetime *= 60;\r
-\r
-    if (renew_life > 0)\r
-       renew_life *= 60;\r
-\r
-    if (lifetime)\r
-        pkrb5_get_init_creds_opt_set_tkt_life(&options, lifetime);\r
-       pkrb5_get_init_creds_opt_set_forwardable(&options,\r
-                                                 forwardable ? 1 : 0);\r
-       pkrb5_get_init_creds_opt_set_proxiable(&options,\r
-                                               proxiable ? 1 : 0);\r
-       pkrb5_get_init_creds_opt_set_renew_life(&options,\r
-                                               renew_life);\r
-    if (addressless)\r
-        pkrb5_get_init_creds_opt_set_address_list(&options,NULL);\r
-    else {\r
-       if (publicIP)\r
-        {\r
-            // we are going to add the public IP address specified by the user\r
-            // to the list provided by the operating system\r
-            krb5_address ** local_addrs=NULL;\r
-            DWORD           netIPAddr;\r
-\r
-            pkrb5_os_localaddr(ctx, &local_addrs);\r
-            while ( local_addrs[i++] );\r
-            addr_count = i + 1;\r
-\r
-            addrs = (krb5_address **) malloc((addr_count+1) * sizeof(krb5_address *));\r
-            if ( !addrs ) {\r
-                pkrb5_free_addresses(ctx, local_addrs);\r
-                goto cleanup;\r
-            }\r
-            memset(addrs, 0, sizeof(krb5_address *) * (addr_count+1));\r
-            i = 0;\r
-            while ( local_addrs[i] ) {\r
-                addrs[i] = (krb5_address *)malloc(sizeof(krb5_address));\r
-                if (addrs[i] == NULL) {\r
-                    pkrb5_free_addresses(ctx, local_addrs);\r
-                    goto cleanup;\r
-                }\r
-\r
-                addrs[i]->magic = local_addrs[i]->magic;\r
-                addrs[i]->addrtype = local_addrs[i]->addrtype;\r
-                addrs[i]->length = local_addrs[i]->length;\r
-                addrs[i]->contents = (unsigned char *)malloc(addrs[i]->length);\r
-                if (!addrs[i]->contents) {\r
-                    pkrb5_free_addresses(ctx, local_addrs);\r
-                    goto cleanup;\r
-                }\r
-\r
-                memcpy(addrs[i]->contents,local_addrs[i]->contents,\r
-                        local_addrs[i]->length);        /* safe */\r
-                i++;\r
-            }\r
-            pkrb5_free_addresses(ctx, local_addrs);\r
-\r
-            addrs[i] = (krb5_address *)malloc(sizeof(krb5_address));\r
-            if (addrs[i] == NULL)\r
-                goto cleanup;\r
-\r
-            addrs[i]->magic = KV5M_ADDRESS;\r
-            addrs[i]->addrtype = AF_INET;\r
-            addrs[i]->length = 4;\r
-            addrs[i]->contents = (unsigned char *)malloc(addrs[i]->length);\r
-            if (!addrs[i]->contents)\r
-                goto cleanup;\r
-\r
-            netIPAddr = htonl(publicIP);\r
-            memcpy(addrs[i]->contents,&netIPAddr,4);\r
-        \r
-            pkrb5_get_init_creds_opt_set_address_list(&options,addrs);\r
-\r
-        }\r
-    }\r
-\r
-    code = pkrb5_get_init_creds_password(ctx, \r
-                                       &my_creds, \r
-                                       me,\r
-                                       password, // password\r
-                                       NULL,     // no prompter\r
-                                       hParent, // prompter data\r
-                                       0, // start time\r
-                                       0, // service name\r
-                                       &options);\r
-    if (code) \r
-       goto cleanup;\r
-\r
-    code = pkrb5_cc_initialize(ctx, cc, me);\r
-    if (code) \r
-       goto cleanup;\r
-\r
-    code = pkrb5_cc_store_cred(ctx, cc, &my_creds);\r
-    if (code) \r
-       goto cleanup;\r
-\r
- cleanup:\r
-    if ( addrs ) {\r
-        for ( i=0;i<addr_count;i++ ) {\r
-            if ( addrs[i] ) {\r
-                if ( addrs[i]->contents )\r
-                    free(addrs[i]->contents);\r
-                free(addrs[i]);\r
-            }\r
-        }\r
-    }\r
-    if (my_creds.client == me)\r
-       my_creds.client = 0;\r
-    pkrb5_free_cred_contents(ctx, &my_creds);\r
-    if (name)\r
-        pkrb5_free_unparsed_name(ctx, name);\r
-    if (me)\r
-        pkrb5_free_principal(ctx, me);\r
-    if (cc && (cc != alt_cc))\r
-        pkrb5_cc_close(ctx, cc);\r
-    if (ctx && (ctx != alt_ctx))\r
-        pkrb5_free_context(ctx);\r
-    return(code);\r
-}\r
-\r
-\r
-int\r
-KFW_get_cred( char * username, \r
-             char * password,\r
-             int lifetime,\r
-             char ** reasonP )\r
-{\r
-    krb5_context ctx = 0;\r
-    krb5_ccache cc = 0;\r
-    char * realm = 0;\r
-    krb5_principal principal = 0;\r
-    char * pname = 0;\r
-    krb5_error_code code;\r
-\r
-    if (!pkrb5_init_context || !username || !password || !password[0])\r
-        return 0;\r
-\r
-    DebugEvent0(username);\r
-\r
-    code = pkrb5_init_context(&ctx);\r
-    if ( code ) goto cleanup;\r
-\r
-    code = pkrb5_get_default_realm(ctx, &realm);\r
-\r
-    if (realm) {\r
-        pname = malloc(strlen(username) + strlen(realm) + 2);\r
-       if (!pname)\r
-           goto cleanup;\r
-       strcpy(pname, username);\r
-       strcat(pname, "@");\r
-       strcat(pname, realm);\r
-    } else {\r
-       goto cleanup;\r
-    }\r
-    \r
-    DebugEvent0(realm);\r
-    DebugEvent0(pname);\r
-\r
-    code = pkrb5_parse_name(ctx, pname, &principal);\r
-    if ( code ) goto cleanup;\r
-\r
-    DebugEvent0("parsed name");\r
-    code = KFW_get_ccache(ctx, principal, &cc);\r
-    if ( code ) goto cleanup;\r
-\r
-    DebugEvent0("got ccache");\r
-\r
-    if ( lifetime == 0 )\r
-        lifetime = pLeash_get_default_lifetime();\r
-\r
-    DebugEvent0("got lifetime");\r
-\r
-    code = KFW_kinit( ctx, cc, HWND_DESKTOP, \r
-                     pname, \r
-                     password,\r
-                     lifetime,\r
-                     pLeash_get_default_forwardable(),\r
-                     pLeash_get_default_proxiable(),\r
-                     pLeash_get_default_renewable() ? pLeash_get_default_renew_till() : 0,\r
-                     pLeash_get_default_noaddresses(),\r
-                     pLeash_get_default_publicip());\r
-    DebugEvent0("kinit returned");\r
-    if ( code ) goto cleanup;\r
-\r
-  cleanup:\r
-    if ( pname )\r
-        free(pname);\r
-    if ( realm )\r
-       pkrb5_free_default_realm(ctx, realm);\r
-    if ( cc )\r
-        pkrb5_cc_close(ctx, cc);\r
-\r
-    if ( code && reasonP ) {\r
-        *reasonP = (char *)perror_message(code);\r
-    }\r
-    return(code);\r
-}\r
-\r
-int KFW_set_ccache_dacl(char *filename, HANDLE hUserToken)\r
-{\r
-    // SID_IDENTIFIER_AUTHORITY authority = SECURITY_NT_SID_AUTHORITY;\r
-    PSID pSystemSID = NULL;\r
-    DWORD SystemSIDlength = 0, UserSIDlength = 0;\r
-    PACL ccacheACL = NULL;\r
-    DWORD ccacheACLlength = 0;\r
-    PTOKEN_USER pTokenUser = NULL;\r
-    DWORD retLen;\r
-    DWORD gle;\r
-    int ret = 0;  \r
-\r
-    if (!filename) {\r
-       DebugEvent0("KFW_set_ccache_dacl - invalid parms");\r
-       return 1;\r
-    }\r
-\r
-    DebugEvent0("KFW_set_ccache_dacl");\r
-\r
-    /* Get System SID */\r
-    if (!ConvertStringSidToSid("S-1-5-18", &pSystemSID)) {\r
-       DebugEvent("KFW_set_ccache_dacl - ConvertStringSidToSid GLE = 0x%x", GetLastError());\r
-       ret = 1;\r
-       goto cleanup;\r
-    }\r
-\r
-    /* Create ACL */\r
-    SystemSIDlength = GetLengthSid(pSystemSID);\r
-    ccacheACLlength = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE)\r
-        + SystemSIDlength - sizeof(DWORD);\r
-\r
-    if (hUserToken) {\r
-       if (!GetTokenInformation(hUserToken, TokenUser, NULL, 0, &retLen))\r
-       {\r
-           if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) {\r
-               pTokenUser = (PTOKEN_USER) LocalAlloc(LPTR, retLen);\r
-\r
-               if (!GetTokenInformation(hUserToken, TokenUser, pTokenUser, retLen, &retLen))\r
-               {\r
-                   DebugEvent("GetTokenInformation failed: GLE = %lX", GetLastError());\r
-               }\r
-           }            \r
-       }\r
-\r
-       if (pTokenUser) {\r
-           UserSIDlength = GetLengthSid(pTokenUser->User.Sid);\r
-\r
-           ccacheACLlength += sizeof(ACCESS_ALLOWED_ACE) + UserSIDlength \r
-               - sizeof(DWORD);\r
-       }\r
-    }\r
-\r
-    ccacheACL = (PACL) LocalAlloc(LPTR, ccacheACLlength);\r
-    if (!ccacheACL) {\r
-       DebugEvent("KFW_set_ccache_dacl - LocalAlloc GLE = 0x%x", GetLastError());\r
-       ret = 1;\r
-       goto cleanup;\r
-    }\r
-\r
-    InitializeAcl(ccacheACL, ccacheACLlength, ACL_REVISION);\r
-    AddAccessAllowedAceEx(ccacheACL, ACL_REVISION, 0,\r
-                         STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,\r
-                         pSystemSID);\r
-    if (pTokenUser) {\r
-       AddAccessAllowedAceEx(ccacheACL, ACL_REVISION, 0,\r
-                            STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,\r
-                            pTokenUser->User.Sid);\r
-       if (!SetNamedSecurityInfo( filename, SE_FILE_OBJECT,\r
-                                  DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,\r
-                                  NULL,\r
-                                  NULL, \r
-                                  ccacheACL,\r
-                                  NULL)) {\r
-           gle = GetLastError();\r
-           DebugEvent("SetNamedSecurityInfo DACL (1) failed: GLE = 0x%lX", gle);\r
-           if (gle != ERROR_NO_TOKEN)\r
-               ret = 1;\r
-       }\r
-       if (!SetNamedSecurityInfo( filename, SE_FILE_OBJECT,\r
-                                  OWNER_SECURITY_INFORMATION,\r
-                                  pTokenUser->User.Sid,\r
-                                  NULL, \r
-                                  NULL,\r
-                                  NULL)) {\r
-           gle = GetLastError();\r
-           DebugEvent("SetNamedSecurityInfo OWNER (2) failed: GLE = 0x%lX", gle);\r
-           if (gle != ERROR_NO_TOKEN)\r
-               ret = 1;\r
-       }\r
-    } else {\r
-       if (!SetNamedSecurityInfo( filename, SE_FILE_OBJECT,\r
-                                  DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,\r
-                                  NULL,\r
-                                  NULL, \r
-                                  ccacheACL,\r
-                                  NULL)) {\r
-           gle = GetLastError();\r
-           DebugEvent("SetNamedSecurityInfo DACL (3) failed: GLE = 0x%lX", gle);\r
-           if (gle != ERROR_NO_TOKEN)\r
-               ret = 1;\r
-       }\r
-    }\r
-\r
-  cleanup:\r
-    if (pSystemSID)\r
-       LocalFree(pSystemSID);\r
-    if (pTokenUser)\r
-       LocalFree(pTokenUser);\r
-    if (ccacheACL)\r
-       LocalFree(ccacheACL);\r
-    return ret;\r
-}\r
-\r
-int KFW_set_ccache_dacl_with_user_sid(char *filename, PSID pUserSID)\r
-{\r
-    // SID_IDENTIFIER_AUTHORITY authority = SECURITY_NT_SID_AUTHORITY;\r
-    PSID pSystemSID = NULL;\r
-    DWORD SystemSIDlength = 0, UserSIDlength = 0;\r
-    PACL ccacheACL = NULL;\r
-    DWORD ccacheACLlength = 0;\r
-    DWORD gle;\r
-    int ret = 0;  \r
-\r
-    if (!filename) {\r
-       DebugEvent0("KFW_set_ccache_dacl_with_user_sid - invalid parms");\r
-       return 1;\r
-    }\r
-\r
-    DebugEvent0("KFW_set_ccache_dacl_with_user_sid");\r
-\r
-    /* Get System SID */\r
-    if (!ConvertStringSidToSid("S-1-5-18", &pSystemSID)) {\r
-       DebugEvent("KFW_set_ccache_dacl - ConvertStringSidToSid GLE = 0x%x", GetLastError());\r
-       ret = 1;\r
-       goto cleanup;\r
-    }\r
-\r
-    /* Create ACL */\r
-    SystemSIDlength = GetLengthSid(pSystemSID);\r
-    ccacheACLlength = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE)\r
-        + SystemSIDlength - sizeof(DWORD);\r
-\r
-    if (pUserSID) {\r
-       UserSIDlength = GetLengthSid(pUserSID);\r
-\r
-       ccacheACLlength += sizeof(ACCESS_ALLOWED_ACE) + UserSIDlength \r
-           - sizeof(DWORD);\r
-    }\r
-\r
-    ccacheACL = (PACL) LocalAlloc(LPTR, ccacheACLlength);\r
-    if (!ccacheACL) {\r
-       DebugEvent("KFW_set_ccache_dacl - LocalAlloc GLE = 0x%x", GetLastError());\r
-       ret = 1;\r
-       goto cleanup;\r
-    }\r
-\r
-    InitializeAcl(ccacheACL, ccacheACLlength, ACL_REVISION);\r
-    AddAccessAllowedAceEx(ccacheACL, ACL_REVISION, 0,\r
-                         STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,\r
-                         pSystemSID);\r
-    if (pUserSID) {\r
-       AddAccessAllowedAceEx(ccacheACL, ACL_REVISION, 0,\r
-                            STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,\r
-                            pUserSID);\r
-       if (!SetNamedSecurityInfo( filename, SE_FILE_OBJECT,\r
-                                  DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,\r
-                                  NULL,\r
-                                  NULL, \r
-                                  ccacheACL,\r
-                                  NULL)) {\r
-           gle = GetLastError();\r
-           DebugEvent("SetNamedSecurityInfo DACL (4) failed: GLE = 0x%lX", gle);\r
-           if (gle != ERROR_NO_TOKEN)\r
-               ret = 1;\r
-       }\r
-       if (!SetNamedSecurityInfo( filename, SE_FILE_OBJECT,\r
-                                  OWNER_SECURITY_INFORMATION,\r
-                                  pUserSID,\r
-                                  NULL, \r
-                                  NULL,\r
-                                  NULL)) {\r
-           gle = GetLastError();\r
-           DebugEvent("SetNamedSecurityInfo OWNER (5) failed: GLE = 0x%lX", gle);\r
-           if (gle != ERROR_NO_TOKEN)\r
-               ret = 1;\r
-       }\r
-    } else {\r
-       if (!SetNamedSecurityInfo( filename, SE_FILE_OBJECT,\r
-                                  DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,\r
-                                  NULL,\r
-                                  NULL, \r
-                                  ccacheACL,\r
-                                  NULL)) {\r
-           gle = GetLastError();\r
-           DebugEvent("SetNamedSecurityInfo DACL (6) failed: GLE = 0x%lX", gle);\r
-           if (gle != ERROR_NO_TOKEN)\r
-               ret = 1;\r
-       }\r
-    }\r
-\r
-  cleanup:\r
-    if (pSystemSID)\r
-       LocalFree(pSystemSID);\r
-    if (ccacheACL)\r
-       LocalFree(ccacheACL);\r
-    return ret;\r
-}\r
-\r
-int KFW_obtain_user_temp_directory(HANDLE hUserToken, char *newfilename, int size)\r
-{\r
-    int  retval = 0;\r
-    DWORD dwSize = size-1;     /* leave room for nul */\r
-    DWORD dwLen  = 0;\r
-\r
-    if (!hUserToken || !newfilename || size <= 0)\r
-       return 1;\r
-\r
-    *newfilename = '\0';\r
-\r
-    dwLen = ExpandEnvironmentStringsForUser(hUserToken, "%TEMP%", newfilename, dwSize);\r
-    if ( !dwLen || dwLen > dwSize )\r
-       dwLen = ExpandEnvironmentStringsForUser(hUserToken, "%TMP%", newfilename, dwSize);\r
-    if ( !dwLen || dwLen > dwSize )\r
-       return 1;\r
-\r
-    newfilename[dwSize] = '\0';\r
-    return 0;\r
-}\r
-\r
-void\r
-KFW_copy_cache_to_system_file(const char * user, const char * filename)\r
-{\r
-    char cachename[MAX_PATH + 8] = "FILE:";\r
-    krb5_context               ctx = 0;\r
-    krb5_error_code            code;\r
-    krb5_principal              princ = 0;\r
-    krb5_ccache                        cc  = 0;\r
-    krb5_ccache                 ncc = 0;\r
-    PSECURITY_ATTRIBUTES        pSA = NULL;\r
-    \r
-    if (!pkrb5_init_context || !user || !filename)\r
-        return;\r
-\r
-    strncat(cachename, filename, sizeof(cachename));\r
-    cachename[sizeof(cachename)-1] = '\0';\r
-\r
-    DebugEvent("KFW_Logon_Event - ccache %s", cachename);\r
-\r
-    DeleteFile(filename);\r
-\r
-    code = pkrb5_init_context(&ctx);\r
-    if (code) goto cleanup;\r
-\r
-    code = pkrb5_parse_name(ctx, user, &princ);\r
-    if (code) goto cleanup;\r
-\r
-    code = KFW_get_ccache(ctx, princ, &cc);\r
-    if (code) goto cleanup;\r
-\r
-    code = pkrb5_cc_resolve(ctx, cachename, &ncc);\r
-    if (code) goto cleanup;\r
-\r
-    code = pkrb5_cc_initialize(ctx, ncc, princ);\r
-    if (code) goto cleanup;\r
-\r
-    code = KFW_set_ccache_dacl(filename, NULL);\r
-    if (code) goto cleanup;\r
-\r
-    code = pkrb5_cc_copy_creds(ctx,cc,ncc);\r
-\r
-  cleanup:\r
-    if ( cc ) {\r
-        pkrb5_cc_close(ctx, cc);\r
-        cc = 0;\r
-    }\r
-    if ( ncc ) {\r
-        pkrb5_cc_close(ctx, ncc);\r
-        ncc = 0;\r
-    }\r
-    if ( princ ) {\r
-        pkrb5_free_principal(ctx, princ);\r
-        princ = 0;\r
-    }\r
-\r
-    if (ctx)\r
-        pkrb5_free_context(ctx);\r
-}\r
-\r
-int\r
-KFW_copy_file_cache_to_default_cache(char * filename)\r
-{\r
-    char cachename[MAX_PATH + 8] = "FILE:";\r
-    krb5_context               ctx = 0;\r
-    krb5_error_code            code;\r
-    krb5_principal              princ = 0;\r
-    krb5_ccache                        cc  = 0;\r
-    krb5_ccache                 ncc = 0;\r
-    int retval = 1;\r
-\r
-    if (!pkrb5_init_context || !filename)\r
-        return 1;\r
-\r
-    if ( strlen(filename) + sizeof("FILE:") > sizeof(cachename) )\r
-        return 1;\r
-\r
-    code = pkrb5_init_context(&ctx);\r
-    if (code) return 1;\r
-\r
-    strcat(cachename, filename);\r
-\r
-    code = pkrb5_cc_resolve(ctx, cachename, &cc);\r
-    if (code) {\r
-       DebugEvent0("kfwcpcc krb5_cc_resolve failed");\r
-       goto cleanup;\r
-    }\r
-    \r
-    code = pkrb5_cc_get_principal(ctx, cc, &princ);\r
-    if (code) {\r
-       DebugEvent0("kfwcpcc krb5_cc_get_principal failed");\r
-       goto cleanup;\r
-    }\r
-\r
-    code = pkrb5_cc_default(ctx, &ncc);\r
-    if (code) {\r
-       DebugEvent0("kfwcpcc krb5_cc_default failed");\r
-       goto cleanup;\r
-    }\r
-    if (!code) {\r
-        code = pkrb5_cc_initialize(ctx, ncc, princ);\r
-\r
-        if (!code)\r
-            code = pkrb5_cc_copy_creds(ctx,cc,ncc);\r
-       if (code) {\r
-           DebugEvent0("kfwcpcc krb5_cc_copy_creds failed");\r
-           goto cleanup;\r
-       }\r
-    }\r
-    if ( ncc ) {\r
-        pkrb5_cc_close(ctx, ncc);\r
-        ncc = 0;\r
-    }\r
-\r
-    retval=0;   /* success */\r
-\r
-  cleanup:\r
-    if ( cc ) {\r
-        pkrb5_cc_close(ctx, cc);\r
-        cc = 0;\r
-    }\r
-\r
-    DeleteFile(filename);\r
-\r
-    if ( princ ) {\r
-        pkrb5_free_principal(ctx, princ);\r
-        princ = 0;\r
-    }\r
-\r
-    if (ctx)\r
-        pkrb5_free_context(ctx);\r
-\r
-    return 0;\r
-}\r
-\r
-\r
-int\r
-KFW_copy_file_cache_to_api_cache(char * filename)\r
-{\r
-    char cachename[MAX_PATH + 8] = "FILE:";\r
-    krb5_context               ctx = 0;\r
-    krb5_error_code            code;\r
-    krb5_principal              princ = 0;\r
-    krb5_ccache                        cc  = 0;\r
-    krb5_ccache                 ncc = 0;\r
-    char                       *name = NULL;\r
-    int retval = 1;\r
-\r
-    if (!pkrb5_init_context || !filename)\r
-        return 1;\r
-\r
-    if ( strlen(filename) + sizeof("FILE:") > sizeof(cachename) )\r
-        return 1;\r
-\r
-    code = pkrb5_init_context(&ctx);\r
-    if (code) return 1;\r
-\r
-    strcat(cachename, filename);\r
-\r
-    code = pkrb5_cc_resolve(ctx, cachename, &cc);\r
-    if (code) {\r
-       DebugEvent0("kfwcpcc krb5_cc_resolve failed");\r
-       goto cleanup;\r
-    }\r
-    \r
-    code = pkrb5_cc_get_principal(ctx, cc, &princ);\r
-    if (code) {\r
-       DebugEvent0("kfwcpcc krb5_cc_get_principal failed");\r
-       goto cleanup;\r
-    }\r
-\r
-    code = pkrb5_unparse_name(ctx, princ, &name);\r
-    if (code) {\r
-       DebugEvent0("kfwcpcc krb5_unparse_name failed");\r
-       goto cleanup;\r
-    }\r
-\r
-    sprintf(cachename, "API:%s", name);\r
-\r
-    code = pkrb5_cc_resolve(ctx, cachename, &ncc);\r
-    if (code) {\r
-       DebugEvent0("kfwcpcc krb5_cc_default failed");\r
-       goto cleanup;\r
-    }\r
-    if (!code) {\r
-        code = pkrb5_cc_initialize(ctx, ncc, princ);\r
-\r
-        if (!code)\r
-            code = pkrb5_cc_copy_creds(ctx,cc,ncc);\r
-       if (code) {\r
-           DebugEvent0("kfwcpcc krb5_cc_copy_creds failed");\r
-           goto cleanup;\r
-       }\r
-    }\r
-    if ( ncc ) {\r
-        pkrb5_cc_close(ctx, ncc);\r
-        ncc = 0;\r
-    }\r
-\r
-    retval=0;   /* success */\r
-\r
-  cleanup:\r
-    if (name)\r
-       pkrb5_free_unparsed_name(ctx, name);\r
-\r
-    if ( cc ) {\r
-        pkrb5_cc_close(ctx, cc);\r
-        cc = 0;\r
-    }\r
-\r
-    DeleteFile(filename);\r
-\r
-    if ( princ ) {\r
-        pkrb5_free_principal(ctx, princ);\r
-        princ = 0;\r
-    }\r
-\r
-    if (ctx)\r
-        pkrb5_free_context(ctx);\r
-\r
-    return 0;\r
-}\r
-\r
-\r
-int \r
-KFW_destroy_tickets_for_principal(char * user)\r
-{\r
-    krb5_context               ctx = 0;\r
-    krb5_error_code            code;\r
-    krb5_principal      princ = 0;\r
-    krb5_ccache                        cc  = 0;\r
-\r
-    if (!pkrb5_init_context)\r
-        return 0;\r
-\r
-    code = pkrb5_init_context(&ctx);\r
-    if (code) return 1;\r
-\r
-    code = pkrb5_parse_name(ctx, user, &princ);\r
-    if (code) goto loop_cleanup;\r
-\r
-    code = KFW_get_ccache(ctx, princ, &cc);\r
-    if (code) goto loop_cleanup;\r
-\r
-    code = pkrb5_cc_destroy(ctx, cc);\r
-    if (!code) cc = 0;\r
-\r
-  loop_cleanup:\r
-    if ( cc ) {\r
-        pkrb5_cc_close(ctx, cc);\r
-        cc = 0;\r
-    }\r
-    if ( princ ) {\r
-        pkrb5_free_principal(ctx, princ);\r
-        princ = 0;\r
-    }\r
-\r
-    pkrb5_free_context(ctx);\r
-    return 0;\r
-}\r
-\r
-\r
-/* There are scenarios in which an interactive logon will not\r
- * result in the LogonScript being executed.  This will result\r
- * in orphaned cache files being left in the Temp directory.\r
- * This function will search for cache files in the Temp \r
- * directory and delete any that are older than five minutes.\r
- */\r
-void\r
-KFW_cleanup_orphaned_caches(void)\r
-{\r
-    char * temppath = NULL;\r
-    char * curdir = NULL;\r
-    DWORD count, count2;\r
-    WIN32_FIND_DATA FindFileData;\r
-    HANDLE hFind = INVALID_HANDLE_VALUE;\r
-    FILETIME now;\r
-    ULARGE_INTEGER uli_now;\r
-    FILETIME expired;\r
-\r
-    count = GetTempPath(0, NULL);\r
-    if (count <= 0)\r
-        return;\r
-    temppath = (char *) malloc(count);\r
-    if (!temppath)\r
-        goto cleanup;\r
-    count2 = GetTempPath(count, temppath);\r
-    if (count2 <= 0 || count2 > count)\r
-        goto cleanup;\r
-\r
-    count = GetCurrentDirectory(0, NULL);\r
-    curdir = (char *)malloc(count);\r
-    if (!curdir)\r
-        goto cleanup;\r
-    count2 = GetCurrentDirectory(count, curdir);\r
-    if (count2 <= 0 || count2 > count)\r
-        goto cleanup;\r
-\r
-    if (!SetCurrentDirectory(temppath))\r
-        goto cleanup;\r
-\r
-    GetSystemTimeAsFileTime(&now);\r
-    uli_now.u.LowPart = now.dwLowDateTime;\r
-    uli_now.u.HighPart = now.dwHighDateTime;\r
-\r
-    uli_now.QuadPart -= 3000000000;        /* 5 minutes == 3 billion 100 nano seconds */\r
-\r
-    expired.dwLowDateTime = uli_now.u.LowPart;\r
-    expired.dwHighDateTime = uli_now.u.HighPart;\r
-\r
-    hFind = FindFirstFile("kfwlogon-*", &FindFileData);\r
-    if (hFind != INVALID_HANDLE_VALUE) {\r
-        do {\r
-            if (CompareFileTime(&FindFileData.ftCreationTime, &expired) < 0) {\r
-                DebugEvent("Deleting orphaned cache file: \"%s\"", FindFileData.cFileName);\r
-                DeleteFile(FindFileData.cFileName);\r
-            }\r
-        } while ( FindNextFile(hFind, &FindFileData) );\r
-    }\r
-\r
-    SetCurrentDirectory(curdir);\r
-\r
-  cleanup:\r
-    if (temppath)\r
-        free(temppath);\r
-    if (hFind != INVALID_HANDLE_VALUE)\r
-        FindClose(hFind);\r
-    if (curdir)\r
-        free(curdir);\r
-}\r
+/*
+Copyright 2005,2006 by the Massachusetts Institute of Technology
+Copyright 2007 by Secure Endpoints Inc.
+
+All rights reserved.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of the Massachusetts
+Institute of Technology (M.I.T.) not be used in advertising or publicity
+pertaining to distribution of the software without specific, written
+prior permission.
+
+M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+*/
+
+#include "kfwlogon.h"
+#include <windows.h>
+#include <Aclapi.h>
+#include <userenv.h>
+#include <Sddl.h>
+
+#include <io.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include <winsock2.h>
+#include <lm.h>
+#include <nb30.h>
+
+#include <errno.h>
+#include <malloc.h>
+
+
+/* Function Pointer Declarations for Delayed Loading */
+// 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);
+
+// leash functions
+DECL_FUNC_PTR(Leash_get_default_lifetime);
+DECL_FUNC_PTR(Leash_get_default_forwardable);
+DECL_FUNC_PTR(Leash_get_default_renew_till);
+DECL_FUNC_PTR(Leash_get_default_noaddresses);
+DECL_FUNC_PTR(Leash_get_default_proxiable);
+DECL_FUNC_PTR(Leash_get_default_publicip);
+DECL_FUNC_PTR(Leash_get_default_use_krb4);
+DECL_FUNC_PTR(Leash_get_default_life_min);
+DECL_FUNC_PTR(Leash_get_default_life_max);
+DECL_FUNC_PTR(Leash_get_default_renew_min);
+DECL_FUNC_PTR(Leash_get_default_renew_max);
+DECL_FUNC_PTR(Leash_get_default_renewable);
+DECL_FUNC_PTR(Leash_get_default_mslsa_import);
+
+// 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_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_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_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_free_addresses);
+DECL_FUNC_PTR(krb5_c_random_make_octets);
+
+// 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 leash_fi[] = {
+    MAKE_FUNC_INFO(Leash_get_default_lifetime),
+    MAKE_FUNC_INFO(Leash_get_default_renew_till),
+    MAKE_FUNC_INFO(Leash_get_default_forwardable),
+    MAKE_FUNC_INFO(Leash_get_default_noaddresses),
+    MAKE_FUNC_INFO(Leash_get_default_proxiable),
+    MAKE_FUNC_INFO(Leash_get_default_publicip),
+    MAKE_FUNC_INFO(Leash_get_default_use_krb4),
+    MAKE_FUNC_INFO(Leash_get_default_life_min),
+    MAKE_FUNC_INFO(Leash_get_default_life_max),
+    MAKE_FUNC_INFO(Leash_get_default_renew_min),
+    MAKE_FUNC_INFO(Leash_get_default_renew_max),
+    MAKE_FUNC_INFO(Leash_get_default_renewable),
+    END_FUNC_INFO
+};
+
+FUNC_INFO leash_opt_fi[] = {
+    MAKE_FUNC_INFO(Leash_get_default_mslsa_import),
+    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_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_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_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_free_addresses),
+    MAKE_FUNC_INFO(krb5_c_random_make_octets),
+    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
+};
+
+/* Static Declarations */
+static int       inited = 0;
+static HINSTANCE hKrb5 = 0;
+static HINSTANCE hKrb524 = 0;
+static HINSTANCE hSecur32 = 0;
+static HINSTANCE hAdvApi32 = 0;
+static HINSTANCE hComErr = 0;
+static HINSTANCE hService = 0;
+static HINSTANCE hProfile = 0;
+static HINSTANCE hLeash = 0;
+static HINSTANCE hLeashOpt = 0;
+static HINSTANCE hCCAPI = 0;
+
+static DWORD TraceOption = 0;
+static HANDLE hDLL;
+
+BOOL IsDebugLogging(void)
+{
+    DWORD LSPsize;
+    HKEY NPKey;
+    DWORD dwDebug = FALSE;
+
+    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, 
+                    "System\\CurrentControlSet\\Services\\MIT Kerberos\\NetworkProvider", 
+                    0, KEY_QUERY_VALUE, &NPKey) == ERROR_SUCCESS) 
+    {
+       LSPsize=sizeof(dwDebug);
+       if (RegQueryValueEx(NPKey, "Debug", NULL, NULL, (LPBYTE)&dwDebug, &LSPsize) != ERROR_SUCCESS) 
+       {
+           dwDebug = FALSE;
+       }
+       RegCloseKey (NPKey);
+    }
+
+    return(dwDebug ? TRUE : FALSE);
+}
+
+void DebugEvent0(char *a) 
+{
+    HANDLE h; char *ptbuf[1];
+    
+    if (IsDebugLogging()) {
+       h = RegisterEventSource(NULL, KFW_LOGON_EVENT_NAME);
+       if (h) {
+            ptbuf[0] = a;
+            ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL);
+            DeregisterEventSource(h);
+        }
+    }
+}
+
+#define MAXBUF_ 512
+void DebugEvent(char *b,...) 
+{
+    HANDLE h; char *ptbuf[1],buf[MAXBUF_+1];
+    va_list marker;
+
+    if (IsDebugLogging()) {
+       h = RegisterEventSource(NULL, KFW_LOGON_EVENT_NAME);
+        if (h) {
+            va_start(marker,b);
+            StringCbVPrintf(buf, MAXBUF_+1,b,marker);
+            buf[MAXBUF_] = '\0';
+            ptbuf[0] = buf;
+            ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL);
+            DeregisterEventSource(h);
+            va_end(marker);
+        }
+    }
+}
+
+void
+UnloadFuncs(
+    FUNC_INFO fi[], 
+    HINSTANCE h
+    )
+{
+    int n;
+    if (fi)
+        for (n = 0; fi[n].func_ptr_var; n++)
+            *(fi[n].func_ptr_var) = 0;
+    if (h) FreeLibrary(h);
+}
+
+int
+LoadFuncs(
+    const char* dll_name, 
+    FUNC_INFO fi[], 
+    HINSTANCE* ph,  // [out, optional] - DLL handle
+    int* pindex,    // [out, optional] - index of last func loaded (-1 if none)
+    int cleanup,    // cleanup function pointers and unload on error
+    int go_on,      // continue loading even if some functions cannot be loaded
+    int silent      // do not pop-up a system dialog if DLL cannot be loaded
+    )
+{
+    HINSTANCE h;
+    int i, n, last_i;
+    int error = 0;
+    UINT em;
+
+    if (ph) *ph = 0;
+    if (pindex) *pindex = -1;
+
+    for (n = 0; fi[n].func_ptr_var; n++)
+       *(fi[n].func_ptr_var) = 0;
+
+    if (silent)
+       em = SetErrorMode(SEM_FAILCRITICALERRORS);
+    h = LoadLibrary(dll_name);
+    if (silent)
+        SetErrorMode(em);
+
+    if (!h)
+        return 0;
+
+    last_i = -1;
+    for (i = 0; (go_on || !error) && (i < n); i++)
+    {
+       void* p = (void*)GetProcAddress(h, fi[i].func_name);
+       if (!p)
+           error = 1;
+        else
+        {
+            last_i = i;
+           *(fi[i].func_ptr_var) = p;
+        }
+    }
+    if (pindex) *pindex = last_i;
+    if (error && cleanup && !go_on) {
+       for (i = 0; i < n; i++) {
+           *(fi[i].func_ptr_var) = 0;
+       }
+       FreeLibrary(h);
+       return 0;
+    }
+    if (ph) *ph = h;
+    if (error) return 0;
+    return 1;
+}
+
+static HANDLE hInitMutex = NULL;
+static BOOL bInit = FALSE;
+
+/* KFW_initialize cannot be called from DllEntryPoint */
+void
+KFW_initialize(void)
+{
+    static int inited = 0;
+
+    if ( !inited ) {
+        char mutexName[MAX_PATH];
+        HANDLE hMutex = NULL;
+
+        sprintf(mutexName, "AFS KFW Init pid=%d", getpid());
+        
+        hMutex = CreateMutex( NULL, TRUE, mutexName );
+        if ( GetLastError() == ERROR_ALREADY_EXISTS ) {
+            if ( WaitForSingleObject( hMutex, INFINITE ) != WAIT_OBJECT_0 ) {
+                return;
+            }
+        }
+        if ( !inited ) {
+            inited = 1;
+            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(PROFILE_DLL, profile_fi, &hProfile, 0, 1, 0, 0);
+            LoadFuncs(LEASH_DLL, leash_fi, &hLeash, 0, 1, 0, 0);
+            LoadFuncs(CCAPI_DLL, ccapi_fi, &hCCAPI, 0, 1, 0, 0);
+            LoadFuncs(LEASH_DLL, leash_opt_fi, &hLeashOpt, 0, 1, 0, 0);
+        }
+        ReleaseMutex(hMutex);
+        CloseHandle(hMutex);
+    }
+}
+
+void
+KFW_cleanup(void)
+{
+    if (hLeashOpt)
+        FreeLibrary(hLeashOpt);
+    if (hCCAPI)
+        FreeLibrary(hCCAPI);
+    if (hLeash)
+        FreeLibrary(hLeash);
+    if (hKrb524)
+        FreeLibrary(hKrb524);
+    if (hSecur32)
+        FreeLibrary(hSecur32);
+    if (hService)
+        FreeLibrary(hService);
+    if (hComErr)
+        FreeLibrary(hComErr);
+    if (hProfile)
+        FreeLibrary(hProfile);
+    if (hKrb5)
+        FreeLibrary(hKrb5);
+}
+
+
+int 
+KFW_is_available(void)
+{
+    KFW_initialize();
+    if ( hKrb5 && hComErr && hService && 
+#ifdef USE_MS2MIT
+         hSecur32 && 
+#endif /* USE_MS2MIT */
+         hProfile && hLeash && hCCAPI )
+        return TRUE;
+
+    return FALSE;
+}
+
+/* Given a principal return an existing ccache or create one and return */
+int
+KFW_get_ccache(krb5_context alt_ctx, krb5_principal principal, krb5_ccache * cc)
+{
+    krb5_context ctx;
+    char * pname = 0;
+    char * ccname = 0;
+    krb5_error_code code;
+
+    if (!pkrb5_init_context)
+        return 0;
+
+    if ( alt_ctx ) {
+        ctx = alt_ctx;
+    } else {
+        code = pkrb5_init_context(&ctx);
+        if (code) goto cleanup;
+    }
+
+    if ( principal ) {
+        code = pkrb5_unparse_name(ctx, principal, &pname);
+        if (code) goto cleanup;
+
+       ccname = (char *)malloc(strlen(pname) + 5);
+       sprintf(ccname,"API:%s",pname);
+
+       DebugEvent0(ccname);
+       code = pkrb5_cc_resolve(ctx, ccname, cc);
+    } else {
+        code = pkrb5_cc_default(ctx, cc);
+        if (code) goto cleanup;
+    }
+
+  cleanup:
+    if (ccname)
+        free(ccname);
+    if (pname)
+        pkrb5_free_unparsed_name(ctx,pname);
+    if (ctx && (ctx != alt_ctx))
+        pkrb5_free_context(ctx);
+    return(code);
+}
+
+
+int
+KFW_kinit( krb5_context alt_ctx,
+            krb5_ccache  alt_cc,
+            HWND hParent,
+            char *principal_name,
+            char *password,
+            krb5_deltat lifetime,
+            DWORD                       forwardable,
+            DWORD                       proxiable,
+            krb5_deltat                 renew_life,
+            DWORD                       addressless,
+            DWORD                       publicIP
+            )
+{
+    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;
+    }
+
+    if ( alt_cc ) {
+        cc = alt_cc;
+    } else {
+        code = pkrb5_cc_default(ctx, &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)
+        lifetime = pLeash_get_default_lifetime();
+    lifetime *= 60;
+
+    if (renew_life > 0)
+       renew_life *= 60;
+
+    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);
+                goto cleanup;
+            }
+            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);
+                    goto cleanup;
+                }
+
+                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);
+                    goto cleanup;
+                }
+
+                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)
+                goto cleanup;
+
+            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)
+                goto cleanup;
+
+            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
+                                       NULL,     // no prompter
+                                       hParent, // 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 && (cc != alt_cc))
+        pkrb5_cc_close(ctx, cc);
+    if (ctx && (ctx != alt_ctx))
+        pkrb5_free_context(ctx);
+    return(code);
+}
+
+
+int
+KFW_get_cred( char * username, 
+             char * password,
+             int lifetime,
+             char ** reasonP )
+{
+    krb5_context ctx = 0;
+    krb5_ccache cc = 0;
+    char * realm = 0;
+    krb5_principal principal = 0;
+    char * pname = 0;
+    krb5_error_code code;
+
+    if (!pkrb5_init_context || !username || !password || !password[0])
+        return 0;
+
+    DebugEvent0(username);
+
+    code = pkrb5_init_context(&ctx);
+    if ( code ) goto cleanup;
+
+    code = pkrb5_get_default_realm(ctx, &realm);
+
+    if (realm) {
+        pname = malloc(strlen(username) + strlen(realm) + 2);
+       if (!pname)
+           goto cleanup;
+       strcpy(pname, username);
+       strcat(pname, "@");
+       strcat(pname, realm);
+    } else {
+       goto cleanup;
+    }
+    
+    DebugEvent0(realm);
+    DebugEvent0(pname);
+
+    code = pkrb5_parse_name(ctx, pname, &principal);
+    if ( code ) goto cleanup;
+
+    DebugEvent0("parsed name");
+    code = KFW_get_ccache(ctx, principal, &cc);
+    if ( code ) goto cleanup;
+
+    DebugEvent0("got ccache");
+
+    if ( lifetime == 0 )
+        lifetime = pLeash_get_default_lifetime();
+
+    DebugEvent0("got lifetime");
+
+    code = KFW_kinit( ctx, cc, HWND_DESKTOP, 
+                     pname, 
+                     password,
+                     lifetime,
+                     pLeash_get_default_forwardable(),
+                     pLeash_get_default_proxiable(),
+                     pLeash_get_default_renewable() ? pLeash_get_default_renew_till() : 0,
+                     pLeash_get_default_noaddresses(),
+                     pLeash_get_default_publicip());
+    DebugEvent0("kinit returned");
+    if ( code ) goto cleanup;
+
+  cleanup:
+    if ( pname )
+        free(pname);
+    if ( realm )
+       pkrb5_free_default_realm(ctx, realm);
+    if ( cc )
+        pkrb5_cc_close(ctx, cc);
+
+    if ( code && reasonP ) {
+        *reasonP = (char *)perror_message(code);
+    }
+    return(code);
+}
+
+int KFW_set_ccache_dacl(char *filename, HANDLE hUserToken)
+{
+    // SID_IDENTIFIER_AUTHORITY authority = SECURITY_NT_SID_AUTHORITY;
+    PSID pSystemSID = NULL;
+    DWORD SystemSIDlength = 0, UserSIDlength = 0;
+    PACL ccacheACL = NULL;
+    DWORD ccacheACLlength = 0;
+    PTOKEN_USER pTokenUser = NULL;
+    DWORD retLen;
+    DWORD gle;
+    int ret = 0;  
+
+    if (!filename) {
+       DebugEvent0("KFW_set_ccache_dacl - invalid parms");
+       return 1;
+    }
+
+    DebugEvent0("KFW_set_ccache_dacl");
+
+    /* Get System SID */
+    if (!ConvertStringSidToSid("S-1-5-18", &pSystemSID)) {
+       DebugEvent("KFW_set_ccache_dacl - ConvertStringSidToSid GLE = 0x%x", GetLastError());
+       ret = 1;
+       goto cleanup;
+    }
+
+    /* Create ACL */
+    SystemSIDlength = GetLengthSid(pSystemSID);
+    ccacheACLlength = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE)
+        + SystemSIDlength - sizeof(DWORD);
+
+    if (hUserToken) {
+       if (!GetTokenInformation(hUserToken, TokenUser, NULL, 0, &retLen))
+       {
+           if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) {
+               pTokenUser = (PTOKEN_USER) LocalAlloc(LPTR, retLen);
+
+               if (!GetTokenInformation(hUserToken, TokenUser, pTokenUser, retLen, &retLen))
+               {
+                   DebugEvent("GetTokenInformation failed: GLE = %lX", GetLastError());
+               }
+           }            
+       }
+
+       if (pTokenUser) {
+           UserSIDlength = GetLengthSid(pTokenUser->User.Sid);
+
+           ccacheACLlength += sizeof(ACCESS_ALLOWED_ACE) + UserSIDlength 
+               - sizeof(DWORD);
+       }
+    }
+
+    ccacheACL = (PACL) LocalAlloc(LPTR, ccacheACLlength);
+    if (!ccacheACL) {
+       DebugEvent("KFW_set_ccache_dacl - LocalAlloc GLE = 0x%x", GetLastError());
+       ret = 1;
+       goto cleanup;
+    }
+
+    InitializeAcl(ccacheACL, ccacheACLlength, ACL_REVISION);
+    AddAccessAllowedAceEx(ccacheACL, ACL_REVISION, 0,
+                         STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,
+                         pSystemSID);
+    if (pTokenUser) {
+       AddAccessAllowedAceEx(ccacheACL, ACL_REVISION, 0,
+                            STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,
+                            pTokenUser->User.Sid);
+       if (!SetNamedSecurityInfo( filename, SE_FILE_OBJECT,
+                                  DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
+                                  NULL,
+                                  NULL, 
+                                  ccacheACL,
+                                  NULL)) {
+           gle = GetLastError();
+           DebugEvent("SetNamedSecurityInfo DACL (1) failed: GLE = 0x%lX", gle);
+           if (gle != ERROR_NO_TOKEN)
+               ret = 1;
+       }
+       if (!SetNamedSecurityInfo( filename, SE_FILE_OBJECT,
+                                  OWNER_SECURITY_INFORMATION,
+                                  pTokenUser->User.Sid,
+                                  NULL, 
+                                  NULL,
+                                  NULL)) {
+           gle = GetLastError();
+           DebugEvent("SetNamedSecurityInfo OWNER (2) failed: GLE = 0x%lX", gle);
+           if (gle != ERROR_NO_TOKEN)
+               ret = 1;
+       }
+    } else {
+       if (!SetNamedSecurityInfo( filename, SE_FILE_OBJECT,
+                                  DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
+                                  NULL,
+                                  NULL, 
+                                  ccacheACL,
+                                  NULL)) {
+           gle = GetLastError();
+           DebugEvent("SetNamedSecurityInfo DACL (3) failed: GLE = 0x%lX", gle);
+           if (gle != ERROR_NO_TOKEN)
+               ret = 1;
+       }
+    }
+
+  cleanup:
+    if (pSystemSID)
+       LocalFree(pSystemSID);
+    if (pTokenUser)
+       LocalFree(pTokenUser);
+    if (ccacheACL)
+       LocalFree(ccacheACL);
+    return ret;
+}
+
+int KFW_set_ccache_dacl_with_user_sid(char *filename, PSID pUserSID)
+{
+    // SID_IDENTIFIER_AUTHORITY authority = SECURITY_NT_SID_AUTHORITY;
+    PSID pSystemSID = NULL;
+    DWORD SystemSIDlength = 0, UserSIDlength = 0;
+    PACL ccacheACL = NULL;
+    DWORD ccacheACLlength = 0;
+    DWORD gle;
+    int ret = 0;  
+
+    if (!filename) {
+       DebugEvent0("KFW_set_ccache_dacl_with_user_sid - invalid parms");
+       return 1;
+    }
+
+    DebugEvent0("KFW_set_ccache_dacl_with_user_sid");
+
+    /* Get System SID */
+    if (!ConvertStringSidToSid("S-1-5-18", &pSystemSID)) {
+       DebugEvent("KFW_set_ccache_dacl - ConvertStringSidToSid GLE = 0x%x", GetLastError());
+       ret = 1;
+       goto cleanup;
+    }
+
+    /* Create ACL */
+    SystemSIDlength = GetLengthSid(pSystemSID);
+    ccacheACLlength = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE)
+        + SystemSIDlength - sizeof(DWORD);
+
+    if (pUserSID) {
+       UserSIDlength = GetLengthSid(pUserSID);
+
+       ccacheACLlength += sizeof(ACCESS_ALLOWED_ACE) + UserSIDlength 
+           - sizeof(DWORD);
+    }
+
+    ccacheACL = (PACL) LocalAlloc(LPTR, ccacheACLlength);
+    if (!ccacheACL) {
+       DebugEvent("KFW_set_ccache_dacl - LocalAlloc GLE = 0x%x", GetLastError());
+       ret = 1;
+       goto cleanup;
+    }
+
+    InitializeAcl(ccacheACL, ccacheACLlength, ACL_REVISION);
+    AddAccessAllowedAceEx(ccacheACL, ACL_REVISION, 0,
+                         STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,
+                         pSystemSID);
+    if (pUserSID) {
+       AddAccessAllowedAceEx(ccacheACL, ACL_REVISION, 0,
+                            STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,
+                            pUserSID);
+       if (!SetNamedSecurityInfo( filename, SE_FILE_OBJECT,
+                                  DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
+                                  NULL,
+                                  NULL, 
+                                  ccacheACL,
+                                  NULL)) {
+           gle = GetLastError();
+           DebugEvent("SetNamedSecurityInfo DACL (4) failed: GLE = 0x%lX", gle);
+           if (gle != ERROR_NO_TOKEN)
+               ret = 1;
+       }
+       if (!SetNamedSecurityInfo( filename, SE_FILE_OBJECT,
+                                  OWNER_SECURITY_INFORMATION,
+                                  pUserSID,
+                                  NULL, 
+                                  NULL,
+                                  NULL)) {
+           gle = GetLastError();
+           DebugEvent("SetNamedSecurityInfo OWNER (5) failed: GLE = 0x%lX", gle);
+           if (gle != ERROR_NO_TOKEN)
+               ret = 1;
+       }
+    } else {
+       if (!SetNamedSecurityInfo( filename, SE_FILE_OBJECT,
+                                  DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
+                                  NULL,
+                                  NULL, 
+                                  ccacheACL,
+                                  NULL)) {
+           gle = GetLastError();
+           DebugEvent("SetNamedSecurityInfo DACL (6) failed: GLE = 0x%lX", gle);
+           if (gle != ERROR_NO_TOKEN)
+               ret = 1;
+       }
+    }
+
+  cleanup:
+    if (pSystemSID)
+       LocalFree(pSystemSID);
+    if (ccacheACL)
+       LocalFree(ccacheACL);
+    return ret;
+}
+
+int KFW_obtain_user_temp_directory(HANDLE hUserToken, char *newfilename, int size)
+{
+    int  retval = 0;
+    DWORD dwSize = size-1;     /* leave room for nul */
+    DWORD dwLen  = 0;
+
+    if (!hUserToken || !newfilename || size <= 0)
+       return 1;
+
+    *newfilename = '\0';
+
+    dwLen = ExpandEnvironmentStringsForUser(hUserToken, "%TEMP%", newfilename, dwSize);
+    if ( !dwLen || dwLen > dwSize )
+       dwLen = ExpandEnvironmentStringsForUser(hUserToken, "%TMP%", newfilename, dwSize);
+    if ( !dwLen || dwLen > dwSize )
+       return 1;
+
+    newfilename[dwSize] = '\0';
+    return 0;
+}
+
+void
+KFW_copy_cache_to_system_file(const char * user, const char * filename)
+{
+    char cachename[MAX_PATH + 8] = "FILE:";
+    krb5_context               ctx = 0;
+    krb5_error_code            code;
+    krb5_principal              princ = 0;
+    krb5_ccache                        cc  = 0;
+    krb5_ccache                 ncc = 0;
+    PSECURITY_ATTRIBUTES        pSA = NULL;
+    
+    if (!pkrb5_init_context || !user || !filename)
+        return;
+
+    strncat(cachename, filename, sizeof(cachename));
+    cachename[sizeof(cachename)-1] = '\0';
+
+    DebugEvent("KFW_Logon_Event - ccache %s", cachename);
+
+    DeleteFile(filename);
+
+    code = pkrb5_init_context(&ctx);
+    if (code) goto cleanup;
+
+    code = pkrb5_parse_name(ctx, user, &princ);
+    if (code) goto cleanup;
+
+    code = KFW_get_ccache(ctx, princ, &cc);
+    if (code) goto cleanup;
+
+    code = pkrb5_cc_resolve(ctx, cachename, &ncc);
+    if (code) goto cleanup;
+
+    code = pkrb5_cc_initialize(ctx, ncc, princ);
+    if (code) goto cleanup;
+
+    code = KFW_set_ccache_dacl(filename, NULL);
+    if (code) goto cleanup;
+
+    code = pkrb5_cc_copy_creds(ctx,cc,ncc);
+
+  cleanup:
+    if ( cc ) {
+        pkrb5_cc_close(ctx, cc);
+        cc = 0;
+    }
+    if ( ncc ) {
+        pkrb5_cc_close(ctx, ncc);
+        ncc = 0;
+    }
+    if ( princ ) {
+        pkrb5_free_principal(ctx, princ);
+        princ = 0;
+    }
+
+    if (ctx)
+        pkrb5_free_context(ctx);
+}
+
+int
+KFW_copy_file_cache_to_default_cache(char * filename)
+{
+    char cachename[MAX_PATH + 8] = "FILE:";
+    krb5_context               ctx = 0;
+    krb5_error_code            code;
+    krb5_principal              princ = 0;
+    krb5_ccache                        cc  = 0;
+    krb5_ccache                 ncc = 0;
+    int retval = 1;
+
+    if (!pkrb5_init_context || !filename)
+        return 1;
+
+    if ( strlen(filename) + sizeof("FILE:") > sizeof(cachename) )
+        return 1;
+
+    code = pkrb5_init_context(&ctx);
+    if (code) return 1;
+
+    strcat(cachename, filename);
+
+    code = pkrb5_cc_resolve(ctx, cachename, &cc);
+    if (code) {
+       DebugEvent0("kfwcpcc krb5_cc_resolve failed");
+       goto cleanup;
+    }
+    
+    code = pkrb5_cc_get_principal(ctx, cc, &princ);
+    if (code) {
+       DebugEvent0("kfwcpcc krb5_cc_get_principal failed");
+       goto cleanup;
+    }
+
+    code = pkrb5_cc_default(ctx, &ncc);
+    if (code) {
+       DebugEvent0("kfwcpcc krb5_cc_default failed");
+       goto cleanup;
+    }
+    if (!code) {
+        code = pkrb5_cc_initialize(ctx, ncc, princ);
+
+        if (!code)
+            code = pkrb5_cc_copy_creds(ctx,cc,ncc);
+       if (code) {
+           DebugEvent0("kfwcpcc krb5_cc_copy_creds failed");
+           goto cleanup;
+       }
+    }
+    if ( ncc ) {
+        pkrb5_cc_close(ctx, ncc);
+        ncc = 0;
+    }
+
+    retval=0;   /* success */
+
+  cleanup:
+    if ( cc ) {
+        pkrb5_cc_close(ctx, cc);
+        cc = 0;
+    }
+
+    DeleteFile(filename);
+
+    if ( princ ) {
+        pkrb5_free_principal(ctx, princ);
+        princ = 0;
+    }
+
+    if (ctx)
+        pkrb5_free_context(ctx);
+
+    return 0;
+}
+
+
+int
+KFW_copy_file_cache_to_api_cache(char * filename)
+{
+    char cachename[MAX_PATH + 8] = "FILE:";
+    krb5_context               ctx = 0;
+    krb5_error_code            code;
+    krb5_principal              princ = 0;
+    krb5_ccache                        cc  = 0;
+    krb5_ccache                 ncc = 0;
+    char                       *name = NULL;
+    int retval = 1;
+
+    if (!pkrb5_init_context || !filename)
+        return 1;
+
+    if ( strlen(filename) + sizeof("FILE:") > sizeof(cachename) )
+        return 1;
+
+    code = pkrb5_init_context(&ctx);
+    if (code) return 1;
+
+    strcat(cachename, filename);
+
+    code = pkrb5_cc_resolve(ctx, cachename, &cc);
+    if (code) {
+       DebugEvent0("kfwcpcc krb5_cc_resolve failed");
+       goto cleanup;
+    }
+    
+    code = pkrb5_cc_get_principal(ctx, cc, &princ);
+    if (code) {
+       DebugEvent0("kfwcpcc krb5_cc_get_principal failed");
+       goto cleanup;
+    }
+
+    code = pkrb5_unparse_name(ctx, princ, &name);
+    if (code) {
+       DebugEvent0("kfwcpcc krb5_unparse_name failed");
+       goto cleanup;
+    }
+
+    sprintf(cachename, "API:%s", name);
+
+    code = pkrb5_cc_resolve(ctx, cachename, &ncc);
+    if (code) {
+       DebugEvent0("kfwcpcc krb5_cc_default failed");
+       goto cleanup;
+    }
+    if (!code) {
+        code = pkrb5_cc_initialize(ctx, ncc, princ);
+
+        if (!code)
+            code = pkrb5_cc_copy_creds(ctx,cc,ncc);
+       if (code) {
+           DebugEvent0("kfwcpcc krb5_cc_copy_creds failed");
+           goto cleanup;
+       }
+    }
+    if ( ncc ) {
+        pkrb5_cc_close(ctx, ncc);
+        ncc = 0;
+    }
+
+    retval=0;   /* success */
+
+  cleanup:
+    if (name)
+       pkrb5_free_unparsed_name(ctx, name);
+
+    if ( cc ) {
+        pkrb5_cc_close(ctx, cc);
+        cc = 0;
+    }
+
+    DeleteFile(filename);
+
+    if ( princ ) {
+        pkrb5_free_principal(ctx, princ);
+        princ = 0;
+    }
+
+    if (ctx)
+        pkrb5_free_context(ctx);
+
+    return 0;
+}
+
+
+int 
+KFW_destroy_tickets_for_principal(char * user)
+{
+    krb5_context               ctx = 0;
+    krb5_error_code            code;
+    krb5_principal      princ = 0;
+    krb5_ccache                        cc  = 0;
+
+    if (!pkrb5_init_context)
+        return 0;
+
+    code = pkrb5_init_context(&ctx);
+    if (code) return 1;
+
+    code = pkrb5_parse_name(ctx, user, &princ);
+    if (code) goto loop_cleanup;
+
+    code = KFW_get_ccache(ctx, princ, &cc);
+    if (code) goto loop_cleanup;
+
+    code = pkrb5_cc_destroy(ctx, cc);
+    if (!code) cc = 0;
+
+  loop_cleanup:
+    if ( cc ) {
+        pkrb5_cc_close(ctx, cc);
+        cc = 0;
+    }
+    if ( princ ) {
+        pkrb5_free_principal(ctx, princ);
+        princ = 0;
+    }
+
+    pkrb5_free_context(ctx);
+    return 0;
+}
+
+
+/* There are scenarios in which an interactive logon will not
+ * result in the LogonScript being executed.  This will result
+ * in orphaned cache files being left in the Temp directory.
+ * This function will search for cache files in the Temp 
+ * directory and delete any that are older than five minutes.
+ */
+void
+KFW_cleanup_orphaned_caches(void)
+{
+    char * temppath = NULL;
+    char * curdir = NULL;
+    DWORD count, count2;
+    WIN32_FIND_DATA FindFileData;
+    HANDLE hFind = INVALID_HANDLE_VALUE;
+    FILETIME now;
+    ULARGE_INTEGER uli_now;
+    FILETIME expired;
+
+    count = GetTempPath(0, NULL);
+    if (count <= 0)
+        return;
+    temppath = (char *) malloc(count);
+    if (!temppath)
+        goto cleanup;
+    count2 = GetTempPath(count, temppath);
+    if (count2 <= 0 || count2 > count)
+        goto cleanup;
+
+    count = GetCurrentDirectory(0, NULL);
+    curdir = (char *)malloc(count);
+    if (!curdir)
+        goto cleanup;
+    count2 = GetCurrentDirectory(count, curdir);
+    if (count2 <= 0 || count2 > count)
+        goto cleanup;
+
+    if (!SetCurrentDirectory(temppath))
+        goto cleanup;
+
+    GetSystemTimeAsFileTime(&now);
+    uli_now.u.LowPart = now.dwLowDateTime;
+    uli_now.u.HighPart = now.dwHighDateTime;
+
+    uli_now.QuadPart -= 3000000000;        /* 5 minutes == 3 billion 100 nano seconds */
+
+    expired.dwLowDateTime = uli_now.u.LowPart;
+    expired.dwHighDateTime = uli_now.u.HighPart;
+
+    hFind = FindFirstFile("kfwlogon-*", &FindFileData);
+    if (hFind != INVALID_HANDLE_VALUE) {
+        do {
+            if (CompareFileTime(&FindFileData.ftCreationTime, &expired) < 0) {
+                DebugEvent("Deleting orphaned cache file: \"%s\"", FindFileData.cFileName);
+                DeleteFile(FindFileData.cFileName);
+            }
+        } while ( FindNextFile(hFind, &FindFileData) );
+    }
+
+    SetCurrentDirectory(curdir);
+
+  cleanup:
+    if (temppath)
+        free(temppath);
+    if (hFind != INVALID_HANDLE_VALUE)
+        FindClose(hFind);
+    if (curdir)
+        free(curdir);
+}
index 5ff778552704d982f93e9a9910f7899a04d6ebcc..fb0861e4741710c7bad4542af8214a7602a3f61a 100644 (file)
@@ -1,39 +1,39 @@
-/*\r
-\r
-Copyright 2005 by the Massachusetts Institute of Technology\r
-\r
-All rights reserved.\r
-\r
-Permission to use, copy, modify, and distribute this software and its\r
-documentation for any purpose and without fee is hereby granted,\r
-provided that the above copyright notice appear in all copies and that\r
-both that copyright notice and this permission notice appear in\r
-supporting documentation, and that the name of the Massachusetts\r
-Institute of Technology (M.I.T.) not be used in advertising or publicity\r
-pertaining to distribution of the software without specific, written\r
-prior permission.\r
-\r
-M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
-ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
-M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
-ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
-WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
-ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
-SOFTWARE.\r
-\r
-*/\r
-\r
-#include <windows.h>\r
-#include "kfwlogon.h"\r
-\r
-int main(int argc, char *argv[])\r
-{\r
-    if ( argc != 2 )\r
-        return 1;\r
-\r
-    KFW_initialize();\r
-\r
-    return KFW_copy_file_cache_to_api_cache(argv[1]);\r
-}\r
-\r
-\r
+/*
+
+Copyright 2005 by the Massachusetts Institute of Technology
+
+All rights reserved.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of the Massachusetts
+Institute of Technology (M.I.T.) not be used in advertising or publicity
+pertaining to distribution of the software without specific, written
+prior permission.
+
+M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+*/
+
+#include <windows.h>
+#include "kfwlogon.h"
+
+int main(int argc, char *argv[])
+{
+    if ( argc != 2 )
+        return 1;
+
+    KFW_initialize();
+
+    return KFW_copy_file_cache_to_api_cache(argv[1]);
+}
+
+
index 8422f58b118c3f94408106771c29e5375c1eaa57..54d7a5a1d603a0671ef01fe7fa55bf429e71e2e9 100644 (file)
-/*\r
-Copyright 2005,2006 by the Massachusetts Institute of Technology\r
-Copyright 2007 by Secure Endpoints Inc.\r
-\r
-All rights reserved.\r
-\r
-Permission to use, copy, modify, and distribute this software and its\r
-documentation for any purpose and without fee is hereby granted,\r
-provided that the above copyright notice appear in all copies and that\r
-both that copyright notice and this permission notice appear in\r
-supporting documentation, and that the name of the Massachusetts\r
-Institute of Technology (M.I.T.) not be used in advertising or publicity\r
-pertaining to distribution of the software without specific, written\r
-prior permission.\r
-\r
-M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
-ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
-M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
-ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
-WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
-ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
-SOFTWARE.\r
-\r
-*/\r
-\r
-#include "kfwlogon.h"\r
-\r
-#include <io.h>\r
-#include <stdio.h>\r
-#include <sys/stat.h>\r
-#include <sys/types.h>\r
-#include <fcntl.h>\r
-\r
-#include <winsock2.h>\r
-#include <lm.h>\r
-#include <nb30.h>\r
-\r
-static HANDLE hDLL;\r
-\r
-static HANDLE hInitMutex = NULL;\r
-static BOOL bInit = FALSE;\r
-\r
-\r
-BOOLEAN APIENTRY DllEntryPoint(HANDLE dll, DWORD reason, PVOID reserved)\r
-{\r
-    hDLL = dll;\r
-    switch (reason) {\r
-    case DLL_PROCESS_ATTACH:\r
-        /* Initialization Mutex */\r
-        hInitMutex = CreateMutex(NULL, FALSE, NULL);\r
-        break;\r
-\r
-    case DLL_PROCESS_DETACH:\r
-        CloseHandle(hInitMutex);\r
-        break;\r
-\r
-    case DLL_THREAD_ATTACH:\r
-    case DLL_THREAD_DETACH:\r
-    default:\r
-        /* Everything else succeeds but does nothing. */\r
-        break;\r
-    }\r
-\r
-    return TRUE;\r
-}\r
-\r
-DWORD APIENTRY NPGetCaps(DWORD index)\r
-{\r
-    switch (index) {\r
-    case WNNC_NET_TYPE:\r
-        /* We aren't a file system; We don't have our own type; use somebody else's. */\r
-        return WNNC_NET_SUN_PC_NFS;\r
-    case WNNC_START:\r
-        /* Say we are already started, even though we might wait after we receive NPLogonNotify */\r
-        return 1;\r
-\r
-    default:\r
-        return 0;\r
-    }\r
-}       \r
-\r
-\r
-static BOOL\r
-WINAPI\r
-UnicodeStringToANSI(UNICODE_STRING uInputString, LPSTR lpszOutputString, int nOutStringLen)\r
-{\r
-    CPINFO CodePageInfo;\r
-\r
-    GetCPInfo(CP_ACP, &CodePageInfo);\r
-\r
-    if (CodePageInfo.MaxCharSize > 1)\r
-        // Only supporting non-Unicode strings\r
-        return FALSE;\r
-    \r
-    if (uInputString.Buffer && ((LPBYTE) uInputString.Buffer)[1] == '\0')\r
-    {\r
-        // Looks like unicode, better translate it\r
-        // UNICODE_STRING specifies the length of the buffer string in Bytes not WCHARS\r
-        WideCharToMultiByte(CP_ACP, 0, (LPCWSTR) uInputString.Buffer, uInputString.Length/2,\r
-                            lpszOutputString, nOutStringLen-1, NULL, NULL);\r
-        lpszOutputString[min(uInputString.Length/2,nOutStringLen-1)] = '\0';\r
-        return TRUE;\r
-    }\r
-\r
-    lpszOutputString[0] = '\0';\r
-    return FALSE;\r
-}  // UnicodeStringToANSI\r
-\r
-\r
-static BOOL\r
-is_windows_vista(void)\r
-{\r
-   static BOOL fChecked = FALSE;\r
-   static BOOL fIsWinVista = FALSE;\r
-\r
-   if (!fChecked)\r
-   {\r
-       OSVERSIONINFO Version;\r
-\r
-       memset (&Version, 0x00, sizeof(Version));\r
-       Version.dwOSVersionInfoSize = sizeof(Version);\r
-\r
-       if (GetVersionEx (&Version))\r
-       {\r
-           if (Version.dwPlatformId == VER_PLATFORM_WIN32_NT &&\r
-               Version.dwMajorVersion >= 6)\r
-               fIsWinVista = TRUE;\r
-       }\r
-       fChecked = TRUE;\r
-   }\r
-\r
-   return fIsWinVista;\r
-}\r
-\r
-\r
-/* Construct a Logon Script that will cause the LogonEventHandler to be executed\r
- * under in the logon session \r
- */\r
-\r
-#define RUNDLL32_CMDLINE "rundll32.exe kfwlogon.dll,LogonEventHandler "\r
-VOID \r
-ConfigureLogonScript(LPWSTR *lpLogonScript, char * filename) {\r
-    DWORD dwLogonScriptLen;\r
-    LPWSTR lpScript;\r
-    LPSTR lpTemp;\r
-    \r
-    if (!lpLogonScript)\r
-       return;\r
-    *lpLogonScript = NULL;\r
-\r
-    if (!filename)\r
-       return;\r
-\r
-    dwLogonScriptLen = strlen(RUNDLL32_CMDLINE) + strlen(filename) + 2;\r
-    lpTemp = (LPSTR) malloc(dwLogonScriptLen); \r
-    if (!lpTemp)\r
-       return;\r
-\r
-    _snprintf(lpTemp, dwLogonScriptLen, "%s%s", RUNDLL32_CMDLINE, filename);\r
-\r
-    SetLastError(0);\r
-    dwLogonScriptLen = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, lpTemp, -1, NULL, 0);\r
-    DebugEvent("ConfigureLogonScript %s requires %d bytes gle=0x%x", lpTemp, dwLogonScriptLen, GetLastError());\r
-\r
-    lpScript = LocalAlloc(LMEM_ZEROINIT, dwLogonScriptLen * 2);\r
-    if (lpScript) {\r
-       if (MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, lpTemp, -1, lpScript, 2 * dwLogonScriptLen))\r
-           *lpLogonScript = lpScript;\r
-       else {\r
-           DebugEvent("ConfigureLogonScript - MultiByteToWideChar failed gle = 0x%x", GetLastError());\r
-           LocalFree(lpScript);\r
-       }\r
-    } else {\r
-       DebugEvent("LocalAlloc failed gle=0x%x", GetLastError());\r
-    }\r
-    free(lpTemp);\r
-}\r
-\r
-\r
-DWORD APIENTRY NPLogonNotify(\r
-       PLUID lpLogonId,\r
-       LPCWSTR lpAuthentInfoType,\r
-       LPVOID lpAuthentInfo,\r
-       LPCWSTR lpPreviousAuthentInfoType,\r
-       LPVOID lpPreviousAuthentInfo,\r
-       LPWSTR lpStationName,\r
-       LPVOID StationHandle,\r
-       LPWSTR *lpLogonScript)\r
-{\r
-    char uname[MAX_USERNAME_LENGTH+1]="";\r
-    char password[MAX_PASSWORD_LENGTH+1]="";\r
-    char logonDomain[MAX_DOMAIN_LENGTH+1]="";\r
-\r
-    MSV1_0_INTERACTIVE_LOGON *IL;\r
-\r
-    DWORD code = 0;\r
-\r
-    char *reason;\r
-    char *ctemp;\r
-\r
-    BOOLEAN interactive = TRUE;\r
-    HWND hwndOwner = (HWND)StationHandle;\r
-    BOOLEAN lowercased_name = TRUE;\r
-\r
-    /* Can we load KFW binaries? */\r
-    if ( !KFW_is_available() )\r
-        return 0;\r
-\r
-    DebugEvent0("NPLogonNotify start");\r
-\r
-    /* Remote Desktop / Terminal Server connections to existing sessions \r
-     * are interactive logons.  Unfortunately, because the session already\r
-     * exists the logon script does not get executed and this prevents \r
-     * us from being able to execute the rundll32 entrypoint \r
-     * LogonEventHandlerA which would process the credential cache this\r
-     * routine will produce.  Therefore, we must cleanup orphaned cache\r
-     * files from this routine.  We will take care of it before doing\r
-     * anything else.\r
-     */\r
-    KFW_cleanup_orphaned_caches();\r
-\r
-    /* Are we interactive? */\r
-    if (lpStationName)\r
-        interactive = (wcsicmp(lpStationName, L"WinSta0") == 0);\r
-\r
-    if ( !interactive ) {\r
-       char station[64]="station";\r
-        DWORD rv;\r
-\r
-        SetLastError(0);\r
-       rv = WideCharToMultiByte(CP_UTF8, 0, lpStationName, -1, \r
-                           station, sizeof(station), NULL, NULL);\r
-        DebugEvent("Skipping NPLogonNotify- LoginId(%d,%d) - Interactive(%d:%s) - gle %d", \r
-                    lpLogonId->HighPart, lpLogonId->LowPart, interactive, rv != 0 ? station : "failure", GetLastError());\r
-        return 0;\r
-    } else\r
-        DebugEvent("NPLogonNotify - LoginId(%d,%d)", lpLogonId->HighPart, lpLogonId->LowPart);\r
-\r
-    /* Initialize Logon Script to none */\r
-    *lpLogonScript=NULL;\r
-    \r
-    /* MSV1_0_INTERACTIVE_LOGON and KERB_INTERACTIVE_LOGON are equivalent for\r
-     * our purposes */\r
-\r
-    if ( wcsicmp(lpAuthentInfoType,L"MSV1_0:Interactive") && \r
-         wcsicmp(lpAuthentInfoType,L"Kerberos:Interactive") )\r
-    {\r
-       char msg[64];\r
-       WideCharToMultiByte(CP_ACP, 0, lpAuthentInfoType, -1, \r
-                           msg, sizeof(msg), NULL, NULL);\r
-       msg[sizeof(msg)-1]='\0';\r
-        DebugEvent("NPLogonNotify - Unsupported Authentication Info Type: %s", msg);\r
-        return 0;\r
-    }\r
-\r
-    IL = (MSV1_0_INTERACTIVE_LOGON *) lpAuthentInfo;\r
-\r
-    /* Convert from Unicode to ANSI */\r
-\r
-    /*TODO: Use SecureZeroMemory to erase passwords */\r
-    if (!UnicodeStringToANSI(IL->UserName, uname, MAX_USERNAME_LENGTH) ||\r
-       !UnicodeStringToANSI(IL->Password, password, MAX_PASSWORD_LENGTH) ||\r
-       !UnicodeStringToANSI(IL->LogonDomainName, logonDomain, MAX_DOMAIN_LENGTH))\r
-       return 0;\r
-\r
-    /* Make sure AD-DOMAINS sent from login that is sent to us is stripped */\r
-    ctemp = strchr(uname, '@');\r
-    if (ctemp) *ctemp = 0;\r
-\r
-    /* is the name all lowercase? */\r
-    for ( ctemp = uname; *ctemp ; ctemp++) {\r
-        if ( !islower(*ctemp) ) {\r
-            lowercased_name = FALSE;\r
-            break;\r
-        }\r
-    }\r
-\r
-    code = KFW_get_cred(uname, password, 0, &reason);\r
-    DebugEvent("NPLogonNotify - KFW_get_cred  uname=[%s] code=[%d]",uname, code);\r
-    \r
-    /* remove any kerberos 5 tickets currently held by the SYSTEM account\r
-     * for this user \r
-     */\r
-    if (!code) {\r
-       char filename[MAX_PATH+1] = "";\r
-       char acctname[MAX_USERNAME_LENGTH+MAX_DOMAIN_LENGTH+3]="";\r
-       PSID pUserSid = NULL;\r
-       LPTSTR pReferencedDomainName = NULL;\r
-       DWORD dwSidLen = 0, dwDomainLen = 0, count;\r
-       SID_NAME_USE eUse;\r
-\r
-       if (_snprintf(acctname, sizeof(acctname), "%s\\%s", logonDomain, uname) < 0) {\r
-           code = -1;\r
-           goto cleanup;\r
-       }\r
-\r
-       count = GetTempPath(sizeof(filename), filename);\r
-        if (count == 0 || count > (sizeof(filename)-1)) {\r
-            code = -1;\r
-            goto cleanup;\r
-        }\r
-\r
-       if (_snprintf(filename, sizeof(filename), "%s\\kfwlogon-%x.%x",\r
-                      filename, lpLogonId->HighPart, lpLogonId->LowPart) < 0) \r
-       {\r
-           code = -1;\r
-           goto cleanup;\r
-       }\r
-\r
-       KFW_copy_cache_to_system_file(uname, filename);\r
-\r
-       /* Need to determine the SID */\r
-\r
-       /* First get the size of the required buffers */\r
-       LookupAccountName (NULL,\r
-                          acctname,\r
-                          pUserSid,\r
-                          &dwSidLen,\r
-                          pReferencedDomainName,\r
-                          &dwDomainLen,\r
-                          &eUse);\r
-       if(dwSidLen){\r
-           pUserSid = (PSID) malloc (dwSidLen);\r
-           memset(pUserSid,0,dwSidLen);\r
-       }\r
-\r
-       if(dwDomainLen){\r
-           pReferencedDomainName = (LPTSTR) malloc (dwDomainLen * sizeof(TCHAR));\r
-           memset(pReferencedDomainName,0,dwDomainLen * sizeof(TCHAR));\r
-       }\r
\r
-       //Now get the SID and the domain name\r
-       if (pUserSid && LookupAccountName( NULL,\r
-                                          acctname,\r
-                                          pUserSid,\r
-                                          &dwSidLen,\r
-                                          pReferencedDomainName,\r
-                                          &dwDomainLen,\r
-                                          &eUse)) \r
-       {\r
-           DebugEvent("LookupAccountName obtained user %s sid in domain %s", acctname, pReferencedDomainName);\r
-           code = KFW_set_ccache_dacl_with_user_sid(filename, pUserSid);\r
-\r
-#ifdef USE_WINLOGON_EVENT\r
-           /* If we are on Vista, setup a LogonScript \r
-            * that will execute the LogonEventHandler entry point via rundll32.exe \r
-            */\r
-           if (is_windows_vista()) {\r
-               ConfigureLogonScript(lpLogonScript, filename);\r
-               if (*lpLogonScript)\r
-                   DebugEvent0("LogonScript assigned");\r
-               else\r
-                   DebugEvent0("No Logon Script");\r
-           }\r
-#else\r
-           ConfigureLogonScript(lpLogonScript, filename);\r
-           if (*lpLogonScript)\r
-                   DebugEvent0("LogonScript assigned");\r
-           else        \r
-                   DebugEvent0("No Logon Script");\r
-#endif\r
-       } else {\r
-           DebugEvent0("LookupAccountName failed");\r
-           DeleteFile(filename);\r
-           code = -1;\r
-       }\r
-\r
-      cleanup:\r
-       if (pUserSid)\r
-           free(pUserSid);\r
-       if (pReferencedDomainName)\r
-           free(pReferencedDomainName);\r
-    }\r
-\r
-    KFW_destroy_tickets_for_principal(uname);\r
-\r
-    if (code) {\r
-        char msg[128];\r
-        HANDLE h;\r
-        char *ptbuf[1];\r
-\r
-        StringCbPrintf(msg, sizeof(msg), "Kerberos ticket acquisition failed: %s", reason);\r
-\r
-        h = RegisterEventSource(NULL, KFW_LOGON_EVENT_NAME);\r
-        ptbuf[0] = msg;\r
-        ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1008, NULL, 1, 0, ptbuf, NULL);\r
-        DeregisterEventSource(h);\r
-        SetLastError(code);\r
-    }\r
-\r
-    if (code)\r
-       DebugEvent0("NPLogonNotify failure");\r
-    else\r
-       DebugEvent0("NPLogonNotify success");\r
-\r
-    return code;\r
-}       \r
-\r
-\r
-DWORD APIENTRY NPPasswordChangeNotify(\r
-       LPCWSTR lpAuthentInfoType,\r
-       LPVOID lpAuthentInfo,\r
-       LPCWSTR lpPreviousAuthentInfoType,\r
-       LPVOID lpPreviousAuthentInfo,\r
-       LPWSTR lpStationName,\r
-       LPVOID StationHandle,\r
-       DWORD dwChangeInfo)\r
-{\r
-    return 0;\r
-}\r
-\r
-#include <userenv.h>\r
-#include <Winwlx.h>\r
-\r
-#ifdef COMMENT\r
-typedef struct _WLX_NOTIFICATION_INFO {  \r
-    ULONG Size;  \r
-    ULONG Flags;  \r
-    PWSTR UserName;  \r
-    PWSTR Domain;  \r
-    PWSTR WindowStation;  \r
-    HANDLE hToken;  \r
-    HDESK hDesktop;  \r
-    PFNMSGECALLBACK pStatusCallback;\r
-} WLX_NOTIFICATION_INFO, *PWLX_NOTIFICATION_INFO;\r
-#endif\r
-\r
-VOID KFW_Startup_Event( PWLX_NOTIFICATION_INFO pInfo )\r
-{\r
-    DebugEvent0("KFW_Startup_Event");\r
-}\r
-\r
-static BOOL\r
-GetSecurityLogonSessionData(HANDLE hToken, PSECURITY_LOGON_SESSION_DATA * ppSessionData)\r
-{\r
-    NTSTATUS Status = 0;\r
-#if 0\r
-    HANDLE  TokenHandle;\r
-#endif\r
-    TOKEN_STATISTICS Stats;\r
-    DWORD   ReqLen;\r
-    BOOL    Success;\r
-\r
-    if (!ppSessionData)\r
-        return FALSE;\r
-    *ppSessionData = NULL;\r
-\r
-#if 0\r
-    Success = OpenProcessToken( HANDLE GetCurrentProcess(), TOKEN_QUERY, &TokenHandle );\r
-    if ( !Success )\r
-        return FALSE;\r
-#endif\r
-\r
-    Success = GetTokenInformation( hToken, TokenStatistics, &Stats, sizeof(TOKEN_STATISTICS), &ReqLen );\r
-#if 0\r
-    CloseHandle( TokenHandle );\r
-#endif\r
-    if ( !Success )\r
-        return FALSE;\r
-\r
-    Status = LsaGetLogonSessionData( &Stats.AuthenticationId, ppSessionData );\r
-    if ( FAILED(Status) || !ppSessionData )\r
-        return FALSE;\r
-\r
-    return TRUE;\r
-}\r
-\r
-VOID KFW_Logon_Event( PWLX_NOTIFICATION_INFO pInfo )\r
-{\r
-#ifdef USE_WINLOGON_EVENT\r
-    WCHAR szUserW[128] = L"";\r
-    char  szUserA[128] = "";\r
-    char szPath[MAX_PATH] = "";\r
-    char szLogonId[128] = "";\r
-    DWORD count;\r
-    char filename[MAX_PATH] = "";\r
-    char newfilename[MAX_PATH] = "";\r
-    char commandline[MAX_PATH+256] = "";\r
-    STARTUPINFO startupinfo;\r
-    PROCESS_INFORMATION procinfo;\r
-    HANDLE hf = NULL;\r
-\r
-    LUID LogonId = {0, 0};\r
-    PSECURITY_LOGON_SESSION_DATA pLogonSessionData = NULL;\r
-\r
-    HKEY hKey1 = NULL, hKey2 = NULL;\r
-\r
-    DebugEvent0("KFW_Logon_Event - Start");\r
-\r
-    GetSecurityLogonSessionData( pInfo->hToken, &pLogonSessionData );\r
-\r
-    if ( pLogonSessionData ) {\r
-        LogonId = pLogonSessionData->LogonId;\r
-        DebugEvent("KFW_Logon_Event - LogonId(%d,%d)", LogonId.HighPart, LogonId.LowPart);\r
-\r
-        _snprintf(szLogonId, sizeof(szLogonId), "kfwlogon-%d.%d",LogonId.HighPart, LogonId.LowPart);\r
-        LsaFreeReturnBuffer( pLogonSessionData );\r
-    } else {\r
-        DebugEvent0("KFW_Logon_Event - Unable to determine LogonId");\r
-        return;\r
-    }\r
-\r
-    count = GetEnvironmentVariable("TEMP", filename, sizeof(filename));\r
-    if ( count > sizeof(filename) || count == 0 ) {\r
-        GetWindowsDirectory(filename, sizeof(filename));\r
-    }\r
-\r
-    if ( strlen(filename) + strlen(szLogonId) + 2 > sizeof(filename) ) {\r
-        DebugEvent0("KFW_Logon_Event - filename too long");\r
-       return;\r
-    }\r
-\r
-    strcat(filename, "\\");\r
-    strcat(filename, szLogonId);    \r
-\r
-    hf = CreateFile(filename, FILE_ALL_ACCESS, 0, NULL, OPEN_EXISTING, \r
-                   FILE_ATTRIBUTE_NORMAL, NULL);\r
-    if (hf == INVALID_HANDLE_VALUE) {\r
-        DebugEvent0("KFW_Logon_Event - file cannot be opened");\r
-       return;\r
-    }\r
-    CloseHandle(hf);\r
-\r
-    if (KFW_set_ccache_dacl(filename, pInfo->hToken)) {\r
-        DebugEvent0("KFW_Logon_Event - unable to set dacl");\r
-       DeleteFile(filename);\r
-       return;\r
-    }\r
-\r
-    if (KFW_obtain_user_temp_directory(pInfo->hToken, newfilename, sizeof(newfilename))) {\r
-        DebugEvent0("KFW_Logon_Event - unable to obtain temp directory");\r
-       return;\r
-    }\r
-\r
-    if ( strlen(newfilename) + strlen(szLogonId) + 2 > sizeof(newfilename) ) {\r
-        DebugEvent0("KFW_Logon_Event - new filename too long");\r
-       return;\r
-    }\r
-\r
-    strcat(newfilename, "\\");\r
-    strcat(newfilename, szLogonId);    \r
-\r
-    if (!MoveFileEx(filename, newfilename, \r
-                    MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) {\r
-        DebugEvent("KFW_Logon_Event - MoveFileEx failed GLE = 0x%x", GetLastError());\r
-       return;\r
-    }\r
-\r
-    _snprintf(commandline, sizeof(commandline), "kfwcpcc.exe \"%s\"", newfilename);\r
-\r
-    GetStartupInfo(&startupinfo);\r
-    if (CreateProcessAsUser( pInfo->hToken,\r
-                             "kfwcpcc.exe",\r
-                             commandline,\r
-                             NULL,\r
-                             NULL,\r
-                             FALSE,\r
-                             CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS,\r
-                             NULL,\r
-                             NULL,\r
-                             &startupinfo,\r
-                             &procinfo)) \r
-    {\r
-       DebugEvent("KFW_Logon_Event - CommandLine %s", commandline);\r
-\r
-       WaitForSingleObject(procinfo.hProcess, 30000);\r
-\r
-       CloseHandle(procinfo.hThread);\r
-       CloseHandle(procinfo.hProcess);\r
-    } else {\r
-       DebugEvent0("KFW_Logon_Event - CreateProcessFailed");\r
-    }\r
-\r
-    DeleteFile(newfilename);\r
-\r
-    DebugEvent0("KFW_Logon_Event - End");\r
-#endif /* USE_WINLOGON_EVENT */\r
-}\r
-\r
-\r
-/* Documentation on the use of RunDll32 entrypoints can be found \r
- * at http://support.microsoft.com/kb/164787 \r
- */\r
-void CALLBACK\r
-LogonEventHandlerA(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow)\r
-{\r
-    HANDLE hf = NULL;\r
-    char commandline[MAX_PATH+256] = "";\r
-    STARTUPINFO startupinfo;\r
-    PROCESS_INFORMATION procinfo;\r
-\r
-    DebugEvent0("LogonEventHandler - Start");\r
-\r
-    /* Validate lpszCmdLine as a file */\r
-    hf = CreateFile(lpszCmdLine, GENERIC_READ | DELETE, 0, NULL, OPEN_EXISTING, \r
-                   FILE_ATTRIBUTE_NORMAL, NULL);\r
-    if (hf == INVALID_HANDLE_VALUE) {\r
-        DebugEvent("LogonEventHandler - \"%s\" cannot be opened", lpszCmdLine);\r
-       return;\r
-    }\r
-    CloseHandle(hf);\r
-\r
-\r
-    _snprintf(commandline, sizeof(commandline), "kfwcpcc.exe \"%s\"", lpszCmdLine);\r
-\r
-    GetStartupInfo(&startupinfo);\r
-    SetLastError(0);\r
-    if (CreateProcess( NULL,\r
-                      commandline,\r
-                      NULL,\r
-                      NULL,\r
-                      FALSE,\r
-                      CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS,\r
-                      NULL,\r
-                      NULL,\r
-                      &startupinfo,\r
-                      &procinfo)) \r
-    {\r
-       DebugEvent("KFW_Logon_Event - CommandLine %s", commandline);\r
-\r
-       WaitForSingleObject(procinfo.hProcess, 30000);\r
-\r
-       CloseHandle(procinfo.hThread);\r
-       CloseHandle(procinfo.hProcess);\r
-    } else {\r
-       DebugEvent("KFW_Logon_Event - CreateProcessFailed \"%s\" GLE 0x%x", \r
-                     commandline, GetLastError());\r
-        DebugEvent("KFW_Logon_Event PATH %s", getenv("PATH"));\r
-    }\r
-\r
-    DeleteFile(lpszCmdLine);\r
-\r
-    DebugEvent0("KFW_Logon_Event - End");\r
-}\r
+/*
+Copyright 2005,2006 by the Massachusetts Institute of Technology
+Copyright 2007 by Secure Endpoints Inc.
+
+All rights reserved.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of the Massachusetts
+Institute of Technology (M.I.T.) not be used in advertising or publicity
+pertaining to distribution of the software without specific, written
+prior permission.
+
+M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+*/
+
+#include "kfwlogon.h"
+
+#include <io.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include <winsock2.h>
+#include <lm.h>
+#include <nb30.h>
+
+static HANDLE hDLL;
+
+static HANDLE hInitMutex = NULL;
+static BOOL bInit = FALSE;
+
+
+BOOLEAN APIENTRY DllEntryPoint(HANDLE dll, DWORD reason, PVOID reserved)
+{
+    hDLL = dll;
+    switch (reason) {
+    case DLL_PROCESS_ATTACH:
+        /* Initialization Mutex */
+        hInitMutex = CreateMutex(NULL, FALSE, NULL);
+        break;
+
+    case DLL_PROCESS_DETACH:
+        CloseHandle(hInitMutex);
+        break;
+
+    case DLL_THREAD_ATTACH:
+    case DLL_THREAD_DETACH:
+    default:
+        /* Everything else succeeds but does nothing. */
+        break;
+    }
+
+    return TRUE;
+}
+
+DWORD APIENTRY NPGetCaps(DWORD index)
+{
+    switch (index) {
+    case WNNC_NET_TYPE:
+        /* We aren't a file system; We don't have our own type; use somebody else's. */
+        return WNNC_NET_SUN_PC_NFS;
+    case WNNC_START:
+        /* Say we are already started, even though we might wait after we receive NPLogonNotify */
+        return 1;
+
+    default:
+        return 0;
+    }
+}       
+
+
+static BOOL
+WINAPI
+UnicodeStringToANSI(UNICODE_STRING uInputString, LPSTR lpszOutputString, int nOutStringLen)
+{
+    CPINFO CodePageInfo;
+
+    GetCPInfo(CP_ACP, &CodePageInfo);
+
+    if (CodePageInfo.MaxCharSize > 1)
+        // Only supporting non-Unicode strings
+        return FALSE;
+    
+    if (uInputString.Buffer && ((LPBYTE) uInputString.Buffer)[1] == '\0')
+    {
+        // Looks like unicode, better translate it
+        // UNICODE_STRING specifies the length of the buffer string in Bytes not WCHARS
+        WideCharToMultiByte(CP_ACP, 0, (LPCWSTR) uInputString.Buffer, uInputString.Length/2,
+                            lpszOutputString, nOutStringLen-1, NULL, NULL);
+        lpszOutputString[min(uInputString.Length/2,nOutStringLen-1)] = '\0';
+        return TRUE;
+    }
+
+    lpszOutputString[0] = '\0';
+    return FALSE;
+}  // UnicodeStringToANSI
+
+
+static BOOL
+is_windows_vista(void)
+{
+   static BOOL fChecked = FALSE;
+   static BOOL fIsWinVista = FALSE;
+
+   if (!fChecked)
+   {
+       OSVERSIONINFO Version;
+
+       memset (&Version, 0x00, sizeof(Version));
+       Version.dwOSVersionInfoSize = sizeof(Version);
+
+       if (GetVersionEx (&Version))
+       {
+           if (Version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
+               Version.dwMajorVersion >= 6)
+               fIsWinVista = TRUE;
+       }
+       fChecked = TRUE;
+   }
+
+   return fIsWinVista;
+}
+
+
+/* Construct a Logon Script that will cause the LogonEventHandler to be executed
+ * under in the logon session 
+ */
+
+#define RUNDLL32_CMDLINE "rundll32.exe kfwlogon.dll,LogonEventHandler "
+VOID 
+ConfigureLogonScript(LPWSTR *lpLogonScript, char * filename) {
+    DWORD dwLogonScriptLen;
+    LPWSTR lpScript;
+    LPSTR lpTemp;
+    
+    if (!lpLogonScript)
+       return;
+    *lpLogonScript = NULL;
+
+    if (!filename)
+       return;
+
+    dwLogonScriptLen = strlen(RUNDLL32_CMDLINE) + strlen(filename) + 2;
+    lpTemp = (LPSTR) malloc(dwLogonScriptLen); 
+    if (!lpTemp)
+       return;
+
+    _snprintf(lpTemp, dwLogonScriptLen, "%s%s", RUNDLL32_CMDLINE, filename);
+
+    SetLastError(0);
+    dwLogonScriptLen = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, lpTemp, -1, NULL, 0);
+    DebugEvent("ConfigureLogonScript %s requires %d bytes gle=0x%x", lpTemp, dwLogonScriptLen, GetLastError());
+
+    lpScript = LocalAlloc(LMEM_ZEROINIT, dwLogonScriptLen * 2);
+    if (lpScript) {
+       if (MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, lpTemp, -1, lpScript, 2 * dwLogonScriptLen))
+           *lpLogonScript = lpScript;
+       else {
+           DebugEvent("ConfigureLogonScript - MultiByteToWideChar failed gle = 0x%x", GetLastError());
+           LocalFree(lpScript);
+       }
+    } else {
+       DebugEvent("LocalAlloc failed gle=0x%x", GetLastError());
+    }
+    free(lpTemp);
+}
+
+
+DWORD APIENTRY NPLogonNotify(
+       PLUID lpLogonId,
+       LPCWSTR lpAuthentInfoType,
+       LPVOID lpAuthentInfo,
+       LPCWSTR lpPreviousAuthentInfoType,
+       LPVOID lpPreviousAuthentInfo,
+       LPWSTR lpStationName,
+       LPVOID StationHandle,
+       LPWSTR *lpLogonScript)
+{
+    char uname[MAX_USERNAME_LENGTH+1]="";
+    char password[MAX_PASSWORD_LENGTH+1]="";
+    char logonDomain[MAX_DOMAIN_LENGTH+1]="";
+
+    MSV1_0_INTERACTIVE_LOGON *IL;
+
+    DWORD code = 0;
+
+    char *reason;
+    char *ctemp;
+
+    BOOLEAN interactive = TRUE;
+    HWND hwndOwner = (HWND)StationHandle;
+    BOOLEAN lowercased_name = TRUE;
+
+    /* Can we load KFW binaries? */
+    if ( !KFW_is_available() )
+        return 0;
+
+    DebugEvent0("NPLogonNotify start");
+
+    /* Remote Desktop / Terminal Server connections to existing sessions 
+     * are interactive logons.  Unfortunately, because the session already
+     * exists the logon script does not get executed and this prevents 
+     * us from being able to execute the rundll32 entrypoint 
+     * LogonEventHandlerA which would process the credential cache this
+     * routine will produce.  Therefore, we must cleanup orphaned cache
+     * files from this routine.  We will take care of it before doing
+     * anything else.
+     */
+    KFW_cleanup_orphaned_caches();
+
+    /* Are we interactive? */
+    if (lpStationName)
+        interactive = (wcsicmp(lpStationName, L"WinSta0") == 0);
+
+    if ( !interactive ) {
+       char station[64]="station";
+        DWORD rv;
+
+        SetLastError(0);
+       rv = WideCharToMultiByte(CP_UTF8, 0, lpStationName, -1, 
+                           station, sizeof(station), NULL, NULL);
+        DebugEvent("Skipping NPLogonNotify- LoginId(%d,%d) - Interactive(%d:%s) - gle %d", 
+                    lpLogonId->HighPart, lpLogonId->LowPart, interactive, rv != 0 ? station : "failure", GetLastError());
+        return 0;
+    } else
+        DebugEvent("NPLogonNotify - LoginId(%d,%d)", lpLogonId->HighPart, lpLogonId->LowPart);
+
+    /* Initialize Logon Script to none */
+    *lpLogonScript=NULL;
+    
+    /* MSV1_0_INTERACTIVE_LOGON and KERB_INTERACTIVE_LOGON are equivalent for
+     * our purposes */
+
+    if ( wcsicmp(lpAuthentInfoType,L"MSV1_0:Interactive") && 
+         wcsicmp(lpAuthentInfoType,L"Kerberos:Interactive") )
+    {
+       char msg[64];
+       WideCharToMultiByte(CP_ACP, 0, lpAuthentInfoType, -1, 
+                           msg, sizeof(msg), NULL, NULL);
+       msg[sizeof(msg)-1]='\0';
+        DebugEvent("NPLogonNotify - Unsupported Authentication Info Type: %s", msg);
+        return 0;
+    }
+
+    IL = (MSV1_0_INTERACTIVE_LOGON *) lpAuthentInfo;
+
+    /* Convert from Unicode to ANSI */
+
+    /*TODO: Use SecureZeroMemory to erase passwords */
+    if (!UnicodeStringToANSI(IL->UserName, uname, MAX_USERNAME_LENGTH) ||
+       !UnicodeStringToANSI(IL->Password, password, MAX_PASSWORD_LENGTH) ||
+       !UnicodeStringToANSI(IL->LogonDomainName, logonDomain, MAX_DOMAIN_LENGTH))
+       return 0;
+
+    /* Make sure AD-DOMAINS sent from login that is sent to us is stripped */
+    ctemp = strchr(uname, '@');
+    if (ctemp) *ctemp = 0;
+
+    /* is the name all lowercase? */
+    for ( ctemp = uname; *ctemp ; ctemp++) {
+        if ( !islower(*ctemp) ) {
+            lowercased_name = FALSE;
+            break;
+        }
+    }
+
+    code = KFW_get_cred(uname, password, 0, &reason);
+    DebugEvent("NPLogonNotify - KFW_get_cred  uname=[%s] code=[%d]",uname, code);
+    
+    /* remove any kerberos 5 tickets currently held by the SYSTEM account
+     * for this user 
+     */
+    if (!code) {
+       char filename[MAX_PATH+1] = "";
+       char acctname[MAX_USERNAME_LENGTH+MAX_DOMAIN_LENGTH+3]="";
+       PSID pUserSid = NULL;
+       LPTSTR pReferencedDomainName = NULL;
+       DWORD dwSidLen = 0, dwDomainLen = 0, count;
+       SID_NAME_USE eUse;
+
+       if (_snprintf(acctname, sizeof(acctname), "%s\\%s", logonDomain, uname) < 0) {
+           code = -1;
+           goto cleanup;
+       }
+
+       count = GetTempPath(sizeof(filename), filename);
+        if (count == 0 || count > (sizeof(filename)-1)) {
+            code = -1;
+            goto cleanup;
+        }
+
+       if (_snprintf(filename, sizeof(filename), "%s\\kfwlogon-%x.%x",
+                      filename, lpLogonId->HighPart, lpLogonId->LowPart) < 0) 
+       {
+           code = -1;
+           goto cleanup;
+       }
+
+       KFW_copy_cache_to_system_file(uname, filename);
+
+       /* Need to determine the SID */
+
+       /* First get the size of the required buffers */
+       LookupAccountName (NULL,
+                          acctname,
+                          pUserSid,
+                          &dwSidLen,
+                          pReferencedDomainName,
+                          &dwDomainLen,
+                          &eUse);
+       if(dwSidLen){
+           pUserSid = (PSID) malloc (dwSidLen);
+           memset(pUserSid,0,dwSidLen);
+       }
+
+       if(dwDomainLen){
+           pReferencedDomainName = (LPTSTR) malloc (dwDomainLen * sizeof(TCHAR));
+           memset(pReferencedDomainName,0,dwDomainLen * sizeof(TCHAR));
+       }
+       //Now get the SID and the domain name
+       if (pUserSid && LookupAccountName( NULL,
+                                          acctname,
+                                          pUserSid,
+                                          &dwSidLen,
+                                          pReferencedDomainName,
+                                          &dwDomainLen,
+                                          &eUse)) 
+       {
+           DebugEvent("LookupAccountName obtained user %s sid in domain %s", acctname, pReferencedDomainName);
+           code = KFW_set_ccache_dacl_with_user_sid(filename, pUserSid);
+
+#ifdef USE_WINLOGON_EVENT
+           /* If we are on Vista, setup a LogonScript 
+            * that will execute the LogonEventHandler entry point via rundll32.exe 
+            */
+           if (is_windows_vista()) {
+               ConfigureLogonScript(lpLogonScript, filename);
+               if (*lpLogonScript)
+                   DebugEvent0("LogonScript assigned");
+               else
+                   DebugEvent0("No Logon Script");
+           }
+#else
+           ConfigureLogonScript(lpLogonScript, filename);
+           if (*lpLogonScript)
+                   DebugEvent0("LogonScript assigned");
+           else        
+                   DebugEvent0("No Logon Script");
+#endif
+       } else {
+           DebugEvent0("LookupAccountName failed");
+           DeleteFile(filename);
+           code = -1;
+       }
+
+      cleanup:
+       if (pUserSid)
+           free(pUserSid);
+       if (pReferencedDomainName)
+           free(pReferencedDomainName);
+    }
+
+    KFW_destroy_tickets_for_principal(uname);
+
+    if (code) {
+        char msg[128];
+        HANDLE h;
+        char *ptbuf[1];
+
+        StringCbPrintf(msg, sizeof(msg), "Kerberos ticket acquisition failed: %s", reason);
+
+        h = RegisterEventSource(NULL, KFW_LOGON_EVENT_NAME);
+        ptbuf[0] = msg;
+        ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1008, NULL, 1, 0, ptbuf, NULL);
+        DeregisterEventSource(h);
+        SetLastError(code);
+    }
+
+    if (code)
+       DebugEvent0("NPLogonNotify failure");
+    else
+       DebugEvent0("NPLogonNotify success");
+
+    return code;
+}       
+
+
+DWORD APIENTRY NPPasswordChangeNotify(
+       LPCWSTR lpAuthentInfoType,
+       LPVOID lpAuthentInfo,
+       LPCWSTR lpPreviousAuthentInfoType,
+       LPVOID lpPreviousAuthentInfo,
+       LPWSTR lpStationName,
+       LPVOID StationHandle,
+       DWORD dwChangeInfo)
+{
+    return 0;
+}
+
+#include <userenv.h>
+#include <Winwlx.h>
+
+#ifdef COMMENT
+typedef struct _WLX_NOTIFICATION_INFO {  
+    ULONG Size;  
+    ULONG Flags;  
+    PWSTR UserName;  
+    PWSTR Domain;  
+    PWSTR WindowStation;  
+    HANDLE hToken;  
+    HDESK hDesktop;  
+    PFNMSGECALLBACK pStatusCallback;
+} WLX_NOTIFICATION_INFO, *PWLX_NOTIFICATION_INFO;
+#endif
+
+VOID KFW_Startup_Event( PWLX_NOTIFICATION_INFO pInfo )
+{
+    DebugEvent0("KFW_Startup_Event");
+}
+
+static BOOL
+GetSecurityLogonSessionData(HANDLE hToken, PSECURITY_LOGON_SESSION_DATA * ppSessionData)
+{
+    NTSTATUS Status = 0;
+#if 0
+    HANDLE  TokenHandle;
+#endif
+    TOKEN_STATISTICS Stats;
+    DWORD   ReqLen;
+    BOOL    Success;
+
+    if (!ppSessionData)
+        return FALSE;
+    *ppSessionData = NULL;
+
+#if 0
+    Success = OpenProcessToken( HANDLE GetCurrentProcess(), TOKEN_QUERY, &TokenHandle );
+    if ( !Success )
+        return FALSE;
+#endif
+
+    Success = GetTokenInformation( hToken, TokenStatistics, &Stats, sizeof(TOKEN_STATISTICS), &ReqLen );
+#if 0
+    CloseHandle( TokenHandle );
+#endif
+    if ( !Success )
+        return FALSE;
+
+    Status = LsaGetLogonSessionData( &Stats.AuthenticationId, ppSessionData );
+    if ( FAILED(Status) || !ppSessionData )
+        return FALSE;
+
+    return TRUE;
+}
+
+VOID KFW_Logon_Event( PWLX_NOTIFICATION_INFO pInfo )
+{
+#ifdef USE_WINLOGON_EVENT
+    WCHAR szUserW[128] = L"";
+    char  szUserA[128] = "";
+    char szPath[MAX_PATH] = "";
+    char szLogonId[128] = "";
+    DWORD count;
+    char filename[MAX_PATH] = "";
+    char newfilename[MAX_PATH] = "";
+    char commandline[MAX_PATH+256] = "";
+    STARTUPINFO startupinfo;
+    PROCESS_INFORMATION procinfo;
+    HANDLE hf = NULL;
+
+    LUID LogonId = {0, 0};
+    PSECURITY_LOGON_SESSION_DATA pLogonSessionData = NULL;
+
+    HKEY hKey1 = NULL, hKey2 = NULL;
+
+    DebugEvent0("KFW_Logon_Event - Start");
+
+    GetSecurityLogonSessionData( pInfo->hToken, &pLogonSessionData );
+
+    if ( pLogonSessionData ) {
+        LogonId = pLogonSessionData->LogonId;
+        DebugEvent("KFW_Logon_Event - LogonId(%d,%d)", LogonId.HighPart, LogonId.LowPart);
+
+        _snprintf(szLogonId, sizeof(szLogonId), "kfwlogon-%d.%d",LogonId.HighPart, LogonId.LowPart);
+        LsaFreeReturnBuffer( pLogonSessionData );
+    } else {
+        DebugEvent0("KFW_Logon_Event - Unable to determine LogonId");
+        return;
+    }
+
+    count = GetEnvironmentVariable("TEMP", filename, sizeof(filename));
+    if ( count > sizeof(filename) || count == 0 ) {
+        GetWindowsDirectory(filename, sizeof(filename));
+    }
+
+    if ( strlen(filename) + strlen(szLogonId) + 2 > sizeof(filename) ) {
+        DebugEvent0("KFW_Logon_Event - filename too long");
+       return;
+    }
+
+    strcat(filename, "\\");
+    strcat(filename, szLogonId);    
+
+    hf = CreateFile(filename, FILE_ALL_ACCESS, 0, NULL, OPEN_EXISTING, 
+                   FILE_ATTRIBUTE_NORMAL, NULL);
+    if (hf == INVALID_HANDLE_VALUE) {
+        DebugEvent0("KFW_Logon_Event - file cannot be opened");
+       return;
+    }
+    CloseHandle(hf);
+
+    if (KFW_set_ccache_dacl(filename, pInfo->hToken)) {
+        DebugEvent0("KFW_Logon_Event - unable to set dacl");
+       DeleteFile(filename);
+       return;
+    }
+
+    if (KFW_obtain_user_temp_directory(pInfo->hToken, newfilename, sizeof(newfilename))) {
+        DebugEvent0("KFW_Logon_Event - unable to obtain temp directory");
+       return;
+    }
+
+    if ( strlen(newfilename) + strlen(szLogonId) + 2 > sizeof(newfilename) ) {
+        DebugEvent0("KFW_Logon_Event - new filename too long");
+       return;
+    }
+
+    strcat(newfilename, "\\");
+    strcat(newfilename, szLogonId);    
+
+    if (!MoveFileEx(filename, newfilename, 
+                    MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) {
+        DebugEvent("KFW_Logon_Event - MoveFileEx failed GLE = 0x%x", GetLastError());
+       return;
+    }
+
+    _snprintf(commandline, sizeof(commandline), "kfwcpcc.exe \"%s\"", newfilename);
+
+    GetStartupInfo(&startupinfo);
+    if (CreateProcessAsUser( pInfo->hToken,
+                             "kfwcpcc.exe",
+                             commandline,
+                             NULL,
+                             NULL,
+                             FALSE,
+                             CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS,
+                             NULL,
+                             NULL,
+                             &startupinfo,
+                             &procinfo)) 
+    {
+       DebugEvent("KFW_Logon_Event - CommandLine %s", commandline);
+
+       WaitForSingleObject(procinfo.hProcess, 30000);
+
+       CloseHandle(procinfo.hThread);
+       CloseHandle(procinfo.hProcess);
+    } else {
+       DebugEvent0("KFW_Logon_Event - CreateProcessFailed");
+    }
+
+    DeleteFile(newfilename);
+
+    DebugEvent0("KFW_Logon_Event - End");
+#endif /* USE_WINLOGON_EVENT */
+}
+
+
+/* Documentation on the use of RunDll32 entrypoints can be found 
+ * at http://support.microsoft.com/kb/164787 
+ */
+void CALLBACK
+LogonEventHandlerA(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow)
+{
+    HANDLE hf = NULL;
+    char commandline[MAX_PATH+256] = "";
+    STARTUPINFO startupinfo;
+    PROCESS_INFORMATION procinfo;
+
+    DebugEvent0("LogonEventHandler - Start");
+
+    /* Validate lpszCmdLine as a file */
+    hf = CreateFile(lpszCmdLine, GENERIC_READ | DELETE, 0, NULL, OPEN_EXISTING, 
+                   FILE_ATTRIBUTE_NORMAL, NULL);
+    if (hf == INVALID_HANDLE_VALUE) {
+        DebugEvent("LogonEventHandler - \"%s\" cannot be opened", lpszCmdLine);
+       return;
+    }
+    CloseHandle(hf);
+
+
+    _snprintf(commandline, sizeof(commandline), "kfwcpcc.exe \"%s\"", lpszCmdLine);
+
+    GetStartupInfo(&startupinfo);
+    SetLastError(0);
+    if (CreateProcess( NULL,
+                      commandline,
+                      NULL,
+                      NULL,
+                      FALSE,
+                      CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS,
+                      NULL,
+                      NULL,
+                      &startupinfo,
+                      &procinfo)) 
+    {
+       DebugEvent("KFW_Logon_Event - CommandLine %s", commandline);
+
+       WaitForSingleObject(procinfo.hProcess, 30000);
+
+       CloseHandle(procinfo.hThread);
+       CloseHandle(procinfo.hProcess);
+    } else {
+       DebugEvent("KFW_Logon_Event - CreateProcessFailed \"%s\" GLE 0x%x", 
+                     commandline, GetLastError());
+        DebugEvent("KFW_Logon_Event PATH %s", getenv("PATH"));
+    }
+
+    DeleteFile(lpszCmdLine);
+
+    DebugEvent0("KFW_Logon_Event - End");
+}
index cfbe4fd3815edd958615256e432850bd91334b06..2f1a62b6ed52c1455ea104e5eb1de9310379264e 100644 (file)
-/*\r
-\r
-Copyright 2005,2006 by the Massachusetts Institute of Technology\r
-Copyright 2007 by Secure Endpoints Inc.\r
-\r
-All rights reserved.\r
-\r
-Permission to use, copy, modify, and distribute this software and its\r
-documentation for any purpose and without fee is hereby granted,\r
-provided that the above copyright notice appear in all copies and that\r
-both that copyright notice and this permission notice appear in\r
-supporting documentation, and that the name of the Massachusetts\r
-Institute of Technology (M.I.T.) not be used in advertising or publicity\r
-pertaining to distribution of the software without specific, written\r
-prior permission.\r
-\r
-M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
-ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
-M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
-ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
-WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
-ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
-SOFTWARE.\r
-\r
-*/\r
-\r
-/* We only support VC 1200 and above anyway */\r
-#pragma once\r
-\r
-/* _WIN32_WINNT must be 0x0501 or greater to pull in definition of\r
- * all required LSA data types when the Vista SDK NtSecAPI.h is used. \r
- */\r
-#ifndef _WIN32_WINNT\r
-#define _WIN32_WINNT 0x0501\r
-#else\r
-#if _WIN32_WINNT < 0x0501\r
-#undef _WIN32_WINNT \r
-#define _WIN32_WINNT 0x0501\r
-#endif\r
-#endif\r
-\r
-#include <windows.h>\r
-#include <npapi.h>\r
-#define SECURITY_WIN32\r
-#include <security.h>\r
-#include <ntsecapi.h>\r
-#include <tchar.h>\r
-#include <strsafe.h>\r
-\r
-typedef int errcode_t;\r
-\r
-#include <loadfuncs-lsa.h>\r
-#include <krb5.h>\r
-#include <loadfuncs-com_err.h>\r
-#include <loadfuncs-krb5.h>\r
-#include <loadfuncs-profile.h>\r
-#include <loadfuncs-leash.h>\r
-\r
-// service definitions\r
-#define SERVICE_DLL   "advapi32.dll"\r
-typedef SC_HANDLE (WINAPI *FP_OpenSCManagerA)(char *, char *, DWORD);\r
-typedef SC_HANDLE (WINAPI *FP_OpenServiceA)(SC_HANDLE, char *, DWORD);\r
-typedef BOOL (WINAPI *FP_QueryServiceStatus)(SC_HANDLE, LPSERVICE_STATUS);\r
-typedef BOOL (WINAPI *FP_CloseServiceHandle)(SC_HANDLE);\r
-\r
-/* In order to avoid including the private CCAPI headers */\r
-typedef int cc_int32;\r
-\r
-#define CC_API_VER_1 1\r
-#define CC_API_VER_2 2\r
-\r
-#define CCACHE_API cc_int32\r
-\r
-/*\r
-** The Official Error Codes\r
-*/\r
-#define CC_NOERROR           0\r
-#define CC_BADNAME           1\r
-#define CC_NOTFOUND          2\r
-#define CC_END               3\r
-#define CC_IO                4\r
-#define CC_WRITE             5\r
-#define CC_NOMEM             6\r
-#define CC_FORMAT            7\r
-#define CC_LOCKED            8\r
-#define CC_BAD_API_VERSION   9\r
-#define CC_NO_EXIST          10\r
-#define CC_NOT_SUPP          11\r
-#define CC_BAD_PARM          12\r
-#define CC_ERR_CACHE_ATTACH  13\r
-#define CC_ERR_CACHE_RELEASE 14\r
-#define CC_ERR_CACHE_FULL    15\r
-#define CC_ERR_CRED_VERSION  16\r
-\r
-enum {\r
-    CC_CRED_VUNKNOWN = 0,       // For validation\r
-    CC_CRED_V4 = 1,\r
-    CC_CRED_V5 = 2,\r
-    CC_CRED_VMAX = 3            // For validation\r
-};\r
-\r
-typedef struct opaque_dll_control_block_type* apiCB;\r
-typedef struct _infoNC {\r
-    char*     name;\r
-    char*     principal;\r
-    cc_int32  vers;\r
-} infoNC;\r
-\r
-TYPEDEF_FUNC(\r
-CCACHE_API,\r
-CALLCONV_C,\r
-cc_initialize,\r
-    (\r
-    apiCB** cc_ctx,           // <  DLL's primary control structure.\r
-                              //    returned here, passed everywhere else\r
-    cc_int32 api_version,     // >  ver supported by caller (use CC_API_VER_1)\r
-    cc_int32*  api_supported, // <  if ~NULL, max ver supported by DLL\r
-    const char** vendor       // <  if ~NULL, vendor name in read only C string\r
-    )\r
-);\r
-\r
-TYPEDEF_FUNC(\r
-CCACHE_API,\r
-CALLCONV_C,\r
-cc_shutdown,\r
-    (\r
-    apiCB** cc_ctx            // <> DLL's primary control structure. NULL after\r
-    )\r
-);\r
-\r
-TYPEDEF_FUNC(\r
-CCACHE_API,\r
-CALLCONV_C,\r
-cc_get_NC_info,\r
-    (\r
-    apiCB* cc_ctx,          // >  DLL's primary control structure\r
-    struct _infoNC*** ppNCi // <  (NULL before call) null terminated,\r
-                            //    list of a structs (free via cc_free_infoNC())\r
-    )\r
-);\r
-\r
-TYPEDEF_FUNC(\r
-CCACHE_API,\r
-CALLCONV_C,\r
-cc_free_NC_info,\r
-    (\r
-    apiCB* cc_ctx,\r
-    struct _infoNC*** ppNCi // <  free list of structs returned by\r
-                            //    cc_get_cache_names().  set to NULL on return\r
-    )\r
-);\r
-/* End private ccapiv2 headers */\r
-\r
-#define CCAPI_DLL   "krbcc32.dll"\r
-\r
-\r
-/* */\r
-#define MAX_USERNAME_LENGTH 256\r
-#define MAX_PASSWORD_LENGTH 256\r
-#define MAX_DOMAIN_LENGTH 256\r
-\r
-#define KFW_LOGON_EVENT_NAME TEXT("MIT Kerberos")\r
-\r
-BOOLEAN APIENTRY DllEntryPoint(HANDLE dll, DWORD reason, PVOID reserved);\r
-\r
-DWORD APIENTRY NPGetCaps(DWORD index);\r
-\r
-DWORD APIENTRY NPLogonNotify(\r
-       PLUID lpLogonId,\r
-       LPCWSTR lpAuthentInfoType,\r
-       LPVOID lpAuthentInfo,\r
-       LPCWSTR lpPreviousAuthentInfoType,\r
-       LPVOID lpPreviousAuthentInfo,\r
-       LPWSTR lpStationName,\r
-       LPVOID StationHandle,\r
-       LPWSTR *lpLogonScript);\r
-\r
-DWORD APIENTRY NPPasswordChangeNotify(\r
-       LPCWSTR lpAuthentInfoType,\r
-       LPVOID lpAuthentInfo,\r
-       LPCWSTR lpPreviousAuthentInfoType,\r
-       LPVOID lpPreviousAuthentInfo,\r
-       LPWSTR lpStationName,\r
-       LPVOID StationHandle,\r
-       DWORD dwChangeInfo);\r
-\r
-#ifdef __cplusplus\r
-extern "C" {\r
-#endif\r
-\r
-void UnloadFuncs(FUNC_INFO [], HINSTANCE);\r
-\r
-int  LoadFuncs(const char*, FUNC_INFO [], HINSTANCE*, int*, int, int, int);\r
-\r
-void DebugEvent0(char *a);\r
-void DebugEvent(char *b,...);\r
-\r
-DWORD MapAuthError(DWORD code);\r
-\r
-static BOOL WINAPI UnicodeStringToANSI(UNICODE_STRING uInputString, LPSTR lpszOutputString, int nOutStringLen);\r
-\r
-int KFW_is_available(void);\r
-int KFW_get_cred( char * username, char * password, int lifetime, char ** reasonP );\r
-void KFW_copy_cache_to_system_file(const char * user, const char * filename);\r
-int KFW_destroy_tickets_for_principal(char * user);\r
-int KFW_set_ccache_dacl(char *filename, HANDLE hUserToken);\r
-int KFW_set_ccache_dacl_with_user_sid(char *filename, PSID pUserSID);\r
-int KFW_obtain_user_temp_directory(HANDLE hUserToken, char *newfilename, int size);\r
-void KFW_cleanup_orphaned_caches(void);\r
-\r
-void CALLBACK LogonEventHandlerA(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);\r
-\r
-#ifdef __cplusplus\r
-}\r
-#endif\r
+/*
+
+Copyright 2005,2006 by the Massachusetts Institute of Technology
+Copyright 2007 by Secure Endpoints Inc.
+
+All rights reserved.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of the Massachusetts
+Institute of Technology (M.I.T.) not be used in advertising or publicity
+pertaining to distribution of the software without specific, written
+prior permission.
+
+M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+*/
+
+/* We only support VC 1200 and above anyway */
+#pragma once
+
+/* _WIN32_WINNT must be 0x0501 or greater to pull in definition of
+ * all required LSA data types when the Vista SDK NtSecAPI.h is used. 
+ */
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501
+#else
+#if _WIN32_WINNT < 0x0501
+#undef _WIN32_WINNT 
+#define _WIN32_WINNT 0x0501
+#endif
+#endif
+
+#include <windows.h>
+#include <npapi.h>
+#define SECURITY_WIN32
+#include <security.h>
+#include <ntsecapi.h>
+#include <tchar.h>
+#include <strsafe.h>
+
+typedef int errcode_t;
+
+#include <loadfuncs-lsa.h>
+#include <krb5.h>
+#include <loadfuncs-com_err.h>
+#include <loadfuncs-krb5.h>
+#include <loadfuncs-profile.h>
+#include <loadfuncs-leash.h>
+
+// service definitions
+#define SERVICE_DLL   "advapi32.dll"
+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);
+
+/* 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
+    )
+);
+/* End private ccapiv2 headers */
+
+#define CCAPI_DLL   "krbcc32.dll"
+
+
+/* */
+#define MAX_USERNAME_LENGTH 256
+#define MAX_PASSWORD_LENGTH 256
+#define MAX_DOMAIN_LENGTH 256
+
+#define KFW_LOGON_EVENT_NAME TEXT("MIT Kerberos")
+
+BOOLEAN APIENTRY DllEntryPoint(HANDLE dll, DWORD reason, PVOID reserved);
+
+DWORD APIENTRY NPGetCaps(DWORD index);
+
+DWORD APIENTRY NPLogonNotify(
+       PLUID lpLogonId,
+       LPCWSTR lpAuthentInfoType,
+       LPVOID lpAuthentInfo,
+       LPCWSTR lpPreviousAuthentInfoType,
+       LPVOID lpPreviousAuthentInfo,
+       LPWSTR lpStationName,
+       LPVOID StationHandle,
+       LPWSTR *lpLogonScript);
+
+DWORD APIENTRY NPPasswordChangeNotify(
+       LPCWSTR lpAuthentInfoType,
+       LPVOID lpAuthentInfo,
+       LPCWSTR lpPreviousAuthentInfoType,
+       LPVOID lpPreviousAuthentInfo,
+       LPWSTR lpStationName,
+       LPVOID StationHandle,
+       DWORD dwChangeInfo);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void UnloadFuncs(FUNC_INFO [], HINSTANCE);
+
+int  LoadFuncs(const char*, FUNC_INFO [], HINSTANCE*, int*, int, int, int);
+
+void DebugEvent0(char *a);
+void DebugEvent(char *b,...);
+
+DWORD MapAuthError(DWORD code);
+
+static BOOL WINAPI UnicodeStringToANSI(UNICODE_STRING uInputString, LPSTR lpszOutputString, int nOutStringLen);
+
+int KFW_is_available(void);
+int KFW_get_cred( char * username, char * password, int lifetime, char ** reasonP );
+void KFW_copy_cache_to_system_file(const char * user, const char * filename);
+int KFW_destroy_tickets_for_principal(char * user);
+int KFW_set_ccache_dacl(char *filename, HANDLE hUserToken);
+int KFW_set_ccache_dacl_with_user_sid(char *filename, PSID pUserSID);
+int KFW_obtain_user_temp_directory(HANDLE hUserToken, char *newfilename, int size);
+void KFW_cleanup_orphaned_caches(void);
+
+void CALLBACK LogonEventHandlerA(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);
+
+#ifdef __cplusplus
+}
+#endif
index c7af75c03c85d9f3650dfcbf92f414e5de01dc92..fc8f4c6d83d5843f4e1d1992a690d558306bbc94 100644 (file)
@@ -1,33 +1,33 @@
-/*\r
- * winlevel.h\r
- *\r
- * Copyright (C) 2006 by the Massachusetts Institute of Technology.\r
- * All rights reserved.\r
- *\r
- * Export of this software from the United States of America may\r
- *   require a specific license from the United States Government.\r
- *   It is the responsibility of any person or organization contemplating\r
- *   export to obtain such a license before exporting.\r
- *\r
- * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and\r
- * distribute this software and its documentation for any purpose and\r
- * without fee is hereby granted, provided that the above copyright\r
- * notice appear in all copies and that both that copyright notice and\r
- * this permission notice appear in supporting documentation, and that\r
- * the name of M.I.T. not be used in advertising or publicity pertaining\r
- * to distribution of the software without specific, written prior\r
- * permission.  Furthermore if you modify this software you must label\r
- * your software as modified software and not distribute it in such a\r
- * fashion that it might be confused with the original M.I.T. software.\r
- * M.I.T. makes no representations about the suitability of\r
- * this software for any purpose.  It is provided "as is" without express\r
- * or implied warranty.\r
- */\r
-\r
-/*\r
- * This is the slave file for Windows version stamping purposes. \r
-/* This value should be an ever increasing number that is \r
- * updated for each alpha, beta, final release.   This will ensure\r
- * that file identifiers are unique\r
- */ \r
-#define KRB5_BUILDLEVEL                0\r
+/*
+ * winlevel.h
+ *
+ * Copyright (C) 2006 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * This is the slave file for Windows version stamping purposes. 
+/* This value should be an ever increasing number that is 
+ * updated for each alpha, beta, final release.   This will ensure
+ * that file identifiers are unique
+ */ 
+#define KRB5_BUILDLEVEL                0