NIM: New Default View and miscellaneous fixes
authorJeffrey Altman <jaltman@secure-endpoints.com>
Tue, 20 Mar 2007 20:41:52 +0000 (20:41 +0000)
committerJeffrey Altman <jaltman@secure-endpoints.com>
Tue, 20 Mar 2007 20:41:52 +0000 (20:41 +0000)
================================
KfW 3.1 Alpha (NetIDMgr 1.1.11.0)

-- nidmgr32.dll

- Only one action in a menu is allowed to have KHUI_ACTIONREF_DEFAULT
  flag set.  This marks the action as being the default action for the
  menu and will be rendered as such.

- Newly created identities start off with the KCDB_IDENT_FLAG_EMPTY
  flag set.  Once credentials are associated with the identity and the
  identity is refreshed, the flag will be cleared.

- When creating actions, enforce the name length.

- khm_value_exists() now handles shadowed configuration spaces.

- Add new action KHUI_ACTION_LAYOUT_MINI which toggles between
  'Advanced' and 'Basic' views.

- Add support for F11 and F12 keys in khui_get_cmd_accel_string().

- New option for alerts to indicate that instead of just setting the
  response field in the alert, the UI should dispatch the command
  that the user has selected.

-- krb5common.obj

- khm_krb5_initialize() can return a handle to a krb5_ccache that has
  already been closed.  Now it doesn't.

- Also import 'krb5_string_to_deltat()'.

- Work around conditioned symbol definitions in ntsecapi.h in the
  Vista Platform SDK that affect Win 2000.

-- krb5cred.dll

- Don't clear the prompts when the options for an identity changes.
  The prompter code relies on the prompts being around so that the
  values that the user has entered can be retained if the new set of
  prompts is the same as the old one.

- Use the same code in the new credentials acquisition and the
  identity configuration code to obtain krb5 parameters for an
  identity.

- Reset the 'IMPORTED' flag when we get new credentials using a
  password.

- If the validity of a principal is not known, then we restrict the
  options that can be specified when calling
  krb5_get_init_creds_password() so that we can reliably determine if
  the principal is valid.  If we need to get new credentials for the
  principal, we need to make another call using the correct options.

- The return codes from the prompter need to indicate that the
  password read operation was cancelled instead of arbiraty non-zero
  values.

- When reading identity settings, if a particular setting is not
  defined in the registry, then default to reading the settings out of
  krb5.ini.

- Refer to credentials as 'credentials' or 'tickets' instead of
  'creds'.

- If an identity has imported credentials, don't import for the same
  identity again.

- When importing an identity, create the identity configuration in the
  registry if we don't already have any settings there.

- Work around conditioned symbol definitions in ntsecapi.h in the
  Vista Platform SDK that affect Win 2000.

- Rearrange declarations for clarity.

- Use the correct APIs to parse configuration values from krb5.ini.

-- krb4cred.dll

- The dialog layout was updated to accomodate a localized string that
  no longer fit in its control.

- Remove a spurious inclusion of ntsecapi.h and work around
  conditioned symbol definition in the Vista Platform SDK.

-- netidmgr.exe

- Fix the menu creation code to correctly tag the default action so
  that it will be rendered properly.

- Update the menu enumeration code to use documented functions instead
  of accessing acton lists directly.

- Pool of per-identity actions now include a set of actions for
  obtaining credentials for specific identities.

- The default action performed when the notification icon is clicked
  is now configurable.  When displaying the context menu in the
  notification area, the default action is highlighted.

- Remove unnecessary handlers from the notifcation event handler.

- Only handle NIN_SELECT instead of both NIN_SELECT and WM_LBUTTONUP
  in the notification event handler.  When the user clicks the
  notication icon, both events are generated. NIN_SELECT is canonical.

- When the handling NIN_BALLOONUSERCLICK in the notification event
  handler, reset balloon_alert before displaying any new alerts so
  that we won't overwrite it later.

- Reset the notification alert icon after displaying an alert.

- If a renewal fails, the displayed alert contains a button that the
  user can click to initiate the process of acquiring new credentials
  for the identity.

- Alerts can optionally dispatch the commands that were added to it
  using the KHUI_ALERT_FLAG_DISPATCH_CMD flag.

- Increase the size of the About dialog.

- Correct the action text for the IDS_ACTION_OPEN_APP and
  IDS_ACTION_CLOSE_APP to say 'Show' and 'Hide' instead of 'Open' and
  'Close'.  These actions only control the visible state of the NIM
  window.

- Add additional notification which signals that the commandline has
  finished processing.

- Add an 'acquire' action to the per-identity actions.

- The per identity actions (renew, destroy, acquire) now have useful
  captions, names and tooltips.

- Use WM_NEXTDLGCTL message when changing the focus of dialog
  controls.  SetFocus() is insufficient.

- If we get a request to show a new credential acquisition dialog and
  we are already showing one, bring that one to the foreground instead
  of trying to display a new one or waiting quietly.

- New configuration schema for the UI that include definitions for the
  new default view.

- The alerter window can now show more than one alert at once.

- If we are about to show queued alerts, then check if the alerts that
  are waiting are related and if they can be grouped together.  If so,
  show them in a single alert window instead of multiple ones.

- If new alerts are issued while a set of alerts are being displayed
  and if the new alert is related to the alerts that are being
  displayed, then add the new alert to the list being displayed.

- Make sure we have a lock on the alert when we are manipulating or
  accessing it.

- Set the focus to the correct control when displaying an alert.

- When adding alerts from the alert queue, make sure we iterate
  through the queue properly.

- Allow keyboard navigation inside the alert window and support scroll
  bars.

- Check if we have a valid code pointer before invoking a UI callback.

- Make sure the main window is in the normal configuration before
  switching to a layout that rquires it.

- When moving the main window around, if it comes close to an edge of
  the working area of the display, snap to it.

- Maintain two sets of settings for the main window placement.  One
  for the mini mode and one for the normal mode.

- When processing saved window placement information from the
  configuration, handle docking hints which note which edges of the
  screen the main window should be adjacent to, if any.

- Switching to the 'Basic' view disables the layout and column
  selection menus.

- Position the new credentials dialog above the main window if the
  main window is visible.

- The alert that is displayed to indicate that an identity has
  expired, now contains a command button that can be used to invoke
  the new credentials dialog for that identity.

-- source

- Update the documentation to reflect the change in behavior regarding
  KHUI_ACTIONREF_DEFAULT in khui_menu_insert_action() and
  khui_menu_insert_paction().

- Remove notes about menu access functions being not thread safe.
  This is no longer true.

- Update the documentation for khui_alert_show() to document new
  behavior regarding KHUI_ALERT_FLAG_DISPATCH_CMD.

- Update documentation to indicate which KHUI_ALERT_FLAG_* flags are
  internal and document the new KHUI_ALERT_FLAG_DISPATCH_CMD flag.

- Augment the queue handling macros to support additional operations.
  Also add new tree data structure with an ordered list of children.

- Code reorganization to reuse code for obtaining the caption and
  tooltip for a system defined action in netidmgr.exe.

ticket: new
component: windows

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@19238 dc483132-0cff-0310-8789-dd5450dbe970

44 files changed:
src/windows/identity/apiversion.txt
src/windows/identity/config/Makefile.w2k
src/windows/identity/config/Makefile.w32
src/windows/identity/include/khlist.h
src/windows/identity/include/khmsgtypes.h
src/windows/identity/kconfig/api.c
src/windows/identity/kconfig/kconfig.h
src/windows/identity/kcreddb/identity.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/krb4/krb4funcs.c
src/windows/identity/plugins/krb4/krb4funcs.h
src/windows/identity/plugins/krb4/lang/en_us/langres.rc
src/windows/identity/plugins/krb5/krb5configdlg.c
src/windows/identity/plugins/krb5/krb5funcs.c
src/windows/identity/plugins/krb5/krb5funcs.h
src/windows/identity/plugins/krb5/krb5newcreds.c
src/windows/identity/plugins/krb5/krbcred.h
src/windows/identity/plugins/krb5/lang/en_us/langres.rc
src/windows/identity/ui/Makefile
src/windows/identity/ui/cfg_general_wnd.c
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/lang/en_us/khapp.rc
src/windows/identity/ui/main.c
src/windows/identity/ui/mainmenu.c
src/windows/identity/ui/mainmenu.h
src/windows/identity/ui/mainwnd.c
src/windows/identity/ui/mainwnd.h
src/windows/identity/ui/newcredwnd.c
src/windows/identity/ui/notifier.c
src/windows/identity/ui/notifier.h
src/windows/identity/ui/resource.h
src/windows/identity/ui/timer.c
src/windows/identity/ui/uiconfig.csv
src/windows/identity/uilib/accel.csv
src/windows/identity/uilib/action.c
src/windows/identity/uilib/actions.csv
src/windows/identity/uilib/khaction.h
src/windows/identity/uilib/khactiondef.h
src/windows/identity/uilib/khalerts.h

index 4ff092b8d5d8a54f8c7a09e92e5e3bc25381f1a4..2f3ef6df631842c32d71c5819847e9aede7d3494 100644 (file)
@@ -346,3 +346,8 @@ Date=(TBD)
 ! KMQ_RCPTTYPE_HWND\r
 ! KMQ_QUEUE_FLAG_DELETED\r
 # Macros not internal\r
+\r
+! khc_value_exists()\r
+# Behavior change with respect to shadowed handles.  If a value\r
+# doesn't exist in the top level handle, then each shadowed handle\r
+# will be tried in turn until the value is found.\r
index ffd1fc87221d6e1bff256cf49c38d58d9d3d75ca..a0632065c64d5ee374bdbfbad6677e73cee818c0 100644 (file)
@@ -47,7 +47,7 @@ KHIMAIRA_WIN32_CONFIG=1
 # Version info\r
 NETIDMGR_VERSION_MAJOR=1\r
 NETIDMGR_VERSION_MINOR=1\r
-NETIDMGR_VERSION_PATCH=10\r
+NETIDMGR_VERSION_PATCH=11\r
 NETIDMGR_VERSION_AUX=0\r
 NETIDMGR_RELEASEDESC=\r
 \r
index d2281b6aed1f9faf103a82c31b864ea1061bc54a..3a87ba6ff144cbaaa4eec9cc3fd6776ce76cba01 100644 (file)
@@ -47,7 +47,7 @@ KHIMAIRA_WIN32_CONFIG=1
 # Version info\r
 NETIDMGR_VERSION_MAJOR=1\r
 NETIDMGR_VERSION_MINOR=1\r
-NETIDMGR_VERSION_PATCH=10\r
+NETIDMGR_VERSION_PATCH=11\r
 NETIDMGR_VERSION_AUX=0\r
 NETIDMGR_RELEASEDESC=\r
 \r
index 44e0ffb68eade376eb2fda56c7200cc13c745ae4..8bf43695b93ed98d84692d8e2e972d11e426af21 100644 (file)
 #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
 \r
 #define TQINIT(pe)                              \\r
     do {                                        \\r
+    LINIT(pe);                                  \\r
     QINIT(pe);                                  \\r
     (pe)->parent = NULL; } while(0)\r
 \r
-#define TQADDCHILD(pt,pe)                       \\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
index f1d42c2172cea3000e0593aaa5c58b2b1b9d08ac..77397f90b52b48695e1925b849e579b75820e6ca 100644 (file)
 #define KMSG_ACT_ACTIVATE   6\r
 \r
 /*! \brief Internal */\r
-#define KMSG_ACT_BEGIN_CMDLINE 128\r
+#define KMSG_ACT_BEGIN_CMDLINE       128\r
 \r
 /*! \brief Internal */\r
-#define KMSG_ACT_CONTINUE_CMDLINE 129\r
+#define KMSG_ACT_CONTINUE_CMDLINE    129\r
 \r
 /*! \brief Internal */\r
-#define KMSG_ACT_SYNC_CFG 130\r
+#define KMSG_ACT_SYNC_CFG            130\r
+\r
+/*! \brief Internal */\r
+#define KMSG_ACT_END_CMDLINE         131\r
 \r
 /*@}*/\r
 \r
index d944a4ce76b98b5c737d40ee80e58fa6241700e5..60984358fbda1625f191a0e0db8311106335b6ad 100644 (file)
@@ -2018,26 +2018,35 @@ khc_value_exists(khm_handle conf, const wchar_t * value) {
     if(!khc_is_handle(conf))\r
         return KHM_ERROR_INVALID_PARAM;\r
 \r
-    c = khc_space_from_handle(conf);\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
+        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
+        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
+        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
+\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
index 518dc4f46a9bd4e9d4a5c662a1ca6491516c00e4..b550d135846549905783e4263a67e49568325651 100644 (file)
@@ -792,6 +792,12 @@ khc_get_type(khm_handle conf, const wchar_t * value_name);
     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
index 326d0258a35ca79650999672f065a47fa700d970..5d76384d9e35f7c5f2c8dc4909961a6073c78bf2 100644 (file)
@@ -231,7 +231,7 @@ kcdb_identity_create(const wchar_t *name,
     StringCbCopy(id->name, namesize, name);\r
 \r
     id->flags = (flags & KCDB_IDENT_FLAGMASK_RDWR);\r
-    id->flags |= KCDB_IDENT_FLAG_ACTIVE;\r
+    id->flags |= KCDB_IDENT_FLAG_ACTIVE | KCDB_IDENT_FLAG_EMPTY;\r
     LINIT(id);\r
 \r
     EnterCriticalSection(&cs_ident);\r
index b3d764476052dfc770074dd21c8d390a7cd787d9..d891af12240138534e90f78b1bc2df92fd672907 100644 (file)
@@ -144,6 +144,7 @@ DECL_FUNC_PTR(krb5_free_host_realm);
 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
@@ -160,6 +161,7 @@ DECL_FUNC_PTR(profile_release);
 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
@@ -289,6 +291,7 @@ FUNC_INFO k5_fi[] = {
     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
@@ -305,6 +308,7 @@ FUNC_INFO profile_fi[] = {
     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
index a9561bc86a3b00b530bc3664b8a4746f2344367e..0f2cc237d9fa5aae3f1962853f102df65ad5e206 100644 (file)
 /* 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
@@ -256,6 +267,7 @@ extern DECL_FUNC_PTR(krb5_get_host_realm);
 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
@@ -272,6 +284,7 @@ extern DECL_FUNC_PTR(profile_release);
 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
index 5ba59df4e7d3dffac935380c700ff81ccac80e03..759641ff7154029353c8d69a83c370a3614c26a2 100644 (file)
@@ -164,8 +164,10 @@ khm_krb5_initialize(khm_handle ident,
             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
+            if (*cache != NULL) {\r
                 (*pkrb5_cc_close)(*ctx, *cache);\r
+                *cache = NULL;\r
+            }\r
         }\r
         return rc;\r
     }\r
index 306437a007f9ecc42ffdb04cd894469917836398..b2b5fef4e7924b83e8ae22c86d94fefdcec66f8e 100644 (file)
@@ -32,7 +32,6 @@ modified and adapted for NetIDMgr */
 \r
 #define SECURITY_WIN32\r
 #include <security.h>\r
-#include <ntsecapi.h>\r
 \r
 #include <string.h>\r
 #include <time.h>\r
index 742d136264d438d3322af88a3e21deea2d84419e..5ec11cc63318e67412693ba0fa2a1ed404f89caf 100644 (file)
 #include <windows.h>\r
 #define SECURITY_WIN32\r
 #include <security.h>\r
-#include <ntsecapi.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
index 374f6ced5bb1c05dd459db1481f8dbcff693c37f..f7a849e24b4e7f911d336c30e52fb7b09ba62a26 100644 (file)
@@ -95,7 +95,7 @@ EXSTYLE WS_EX_CONTROLPARENT
 FONT 8, "MS Shell Dlg", 400, 0, 0x1\r
 BEGIN\r
     CONTROL         "Obtain Kerberos v4 credentials for this identity",IDC_CFG_GETTIX,\r
-                    "Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,7,7,147,10\r
+                    "Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,7,7,165,10\r
 END\r
 \r
 \r
index 9c8af5dc4ef2305fefdf4ae957aa0e49602df694..2d1e3259cb065431252cad2e6d87eb061e029352 100644 (file)
@@ -120,35 +120,6 @@ typedef struct tag_k5_config_data {
 #define K5_CDFLAG_MOD_INC_REALMS     0x00000100
 #define K5_CDFLAG_MOD_REALMS         0x00001000
 
-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
-k5_parse_boolean(const char *s)
-{
-    const char *const *p;
-
-    for(p=conf_yes; *p; p++) {
-        if (!_stricmp(*p,s))
-            return 1;
-    }
-
-    for(p=conf_no; *p; p++) {
-        if (!_stricmp(*p,s))
-            return 0;
-    }
-
-    /* Default to "no" */
-    return 0;
-}
-
 void
 k5_init_config_data(k5_config_data * d) {
     ZeroMemory(d, sizeof(*d));
@@ -325,7 +296,12 @@ k5_read_config_data(k5_config_data * d) {
         rv = pprofile_get_string(profile, "libdefaults", "dns_lookup_kdc",
                                  NULL, NULL, &boolv);
         if (!rv && boolv) {
-            d->dns_lookup_kdc = k5_parse_boolean(boolv);
+            khm_boolean b;
+
+            if (!khm_krb5_parse_boolean(boolv, &b))
+                d->dns_lookup_kdc = b;
+            else
+                d->dns_lookup_kdc = FALSE;
             pprofile_release_string(boolv);
         } else
             d->dns_lookup_kdc = FALSE;
@@ -333,7 +309,12 @@ k5_read_config_data(k5_config_data * d) {
         rv = pprofile_get_string(profile, "libdefaults", "dns_lookup_realm",
                                  NULL, NULL, &boolv);
         if (!rv && boolv) {
-            d->dns_lookup_realm = k5_parse_boolean(boolv);
+            khm_boolean b;
+
+            if (!khm_krb5_parse_boolean(boolv, &b))
+                d->dns_lookup_realm = b;
+            else
+                d->dns_lookup_realm = FALSE;
             pprofile_release_string(boolv);
         } else
             d->dns_lookup_realm = FALSE;
@@ -341,7 +322,12 @@ k5_read_config_data(k5_config_data * d) {
         rv = pprofile_get_string(profile, "libdefaults", "dns_fallback",
                                  NULL, NULL, &boolv);
         if (!rv && boolv) {
-            d->dns_fallback = k5_parse_boolean(boolv);
+            khm_boolean b;
+
+            if (!khm_krb5_parse_boolean(boolv, &b))
+                d->dns_fallback = b;
+            else
+                d->dns_fallback = FALSE;
             pprofile_release_string(boolv);
         } else
             d->dns_fallback = FALSE;
@@ -349,7 +335,12 @@ k5_read_config_data(k5_config_data * d) {
         rv = pprofile_get_string(profile, "libdefaults", "noaddresses",
                                  NULL, NULL, &boolv);
         if (!rv && boolv) {
-            d->noaddresses = k5_parse_boolean(boolv);
+            khm_boolean b;
+
+            if (!khm_krb5_parse_boolean(boolv, &b))
+                d->noaddresses = b;
+            else
+                d->noaddresses = TRUE;
             pprofile_release_string(boolv);
         } else
             d->noaddresses = TRUE;
@@ -641,8 +632,8 @@ k5_write_config_data(k5_config_data * d) {
 
             rv = pprofile_add_relation(profile, sec_libdefaults,
                                        (d->dns_lookup_kdc)?
-                                       conf_yes[0]:
-                                       conf_no[0]);
+                                       KRB5_CONF_YES:
+                                       KRB5_CONF_NO);
             d->flags &= ~K5_CDFLAG_MOD_DNS_LOOKUP_KDC;
         }
 
@@ -654,8 +645,8 @@ k5_write_config_data(k5_config_data * d) {
 
             rv = pprofile_add_relation(profile, sec_libdefaults,
                                        (d->dns_lookup_realm)?
-                                       conf_yes[0]:
-                                       conf_no[0]);
+                                       KRB5_CONF_YES:
+                                       KRB5_CONF_NO);
 
             d->flags &= ~K5_CDFLAG_MOD_DNS_LOOKUP_RLM;
         }
@@ -668,8 +659,8 @@ k5_write_config_data(k5_config_data * d) {
 
             rv = pprofile_add_relation(profile, sec_libdefaults,
                                        (d->dns_fallback)?
-                                       conf_yes[0]:
-                                       conf_no[0]);
+                                       KRB5_CONF_YES:
+                                       KRB5_CONF_NO);
 
             d->flags &= ~K5_CDFLAG_MOD_DNS_FALLBACK;
         }
@@ -682,8 +673,8 @@ k5_write_config_data(k5_config_data * d) {
 
             rv = pprofile_add_relation(profile, sec_libdefaults,
                                        (d->noaddresses)?
-                                       conf_yes[0]:
-                                       conf_no[0]);
+                                       KRB5_CONF_YES:
+                                       KRB5_CONF_NO);
 
             d->flags &= ~K5_CDFLAG_MOD_NOADDRESSES;
         }
index 8cf2b86fdfb5b2414ae79e7f9bf935ac03d18769..bab9ba4cd04cd9a49941d719c248a8de3d07e067 100644 (file)
@@ -33,7 +33,6 @@
 \r
 #define SECURITY_WIN32\r
 #include <security.h>\r
-#include <ntsecapi.h>\r
 \r
 #include <string.h>\r
 #include <time.h>\r
@@ -1004,7 +1003,6 @@ khm_krb5_renew_ident(khm_handle identity)
     krb5_creds          my_creds;\r
     krb5_data           *realm = NULL;\r
     wchar_t             idname[KCDB_IDENT_MAXCCH_NAME];\r
-    char                cidname[KCDB_IDENT_MAXCCH_NAME];\r
     khm_size            cb;\r
 \r
     memset(&my_creds, 0, sizeof(krb5_creds));\r
@@ -1016,9 +1014,11 @@ khm_krb5_renew_ident(khm_handle identity)
     kcdb_identity_get_name(identity, idname, &cb);\r
 \r
     if (khm_krb5_get_identity_flags(identity) & K5IDFLAG_IMPORTED) {\r
+#ifdef REIMPORT_MSLSA_CREDS\r
         /* we are trying to renew the identity that was imported from\r
            MSLSA: */\r
         BOOL imported;\r
+        char                cidname[KCDB_IDENT_MAXCCH_NAME];\r
 \r
         UnicodeStrToAnsi(cidname, sizeof(cidname), idname);\r
 \r
@@ -1029,6 +1029,11 @@ khm_krb5_renew_ident(khm_handle identity)
 \r
         /* if the import failed, then we try to renew the identity via\r
            the usual procedure. */\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
     code = khm_krb5_initialize(identity, &ctx, &cc);\r
@@ -2002,11 +2007,11 @@ khm_krb5_ms2mit(char * match_princ, BOOL match_realm, BOOL save_creds)
 \r
             wname[0] = L'\0';\r
 \r
-            kcdb_identity_get_config(ident, 0, &idconfig);\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, 0, &k5config);\r
+            khc_open_space(idconfig, CSNAME_KRB5CRED, KHM_FLAG_CREATE, &k5config);\r
             if (k5config == NULL)\r
                 goto _done_checking_config;\r
 \r
@@ -2612,3 +2617,487 @@ khm_krb5_get_temp_ccache(krb5_context ctx,
 \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 done;\r
+\r
+        rv = khc_open_space(csp_i, CSNAME_KRB5CRED, flags, &csp_ik5);\r
+        if (KHM_FAILED(rv))\r
+            goto done;\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_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
+\r
+    } else {\r
+\r
+        /* No valid identity specified. We default to the parameters key. */\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
+    if (csp_realm)\r
+        khc_close_space(csp_realm);\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
+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
+\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
+    /* 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 = pprofile_get_string(profile, "libdefaults", "ticket_lifetime", 0, 0, &value);\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
+                    pprofile_release_string(value);\r
+                }\r
+            }\r
+\r
+            if (!(regf & K5PARAM_F_RLIFE)) {\r
+                char * value = NULL;\r
+                retval = pprofile_get_string(profile, "libdefaults", "renew_lifetime", 0, 0, &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
+                    pprofile_release_string(value);\r
+                }\r
+            }\r
+\r
+            if (!(regf & K5PARAM_F_FORW)) {\r
+                char * value = NULL;\r
+                retval = pprofile_get_string(profile, "libdefaults", "forwardable", 0, 0, &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
+                    pprofile_release_string(value);\r
+                    proff |= K5PARAM_F_FORW;\r
+                }\r
+            }\r
+\r
+            if (!(regf & K5PARAM_F_RENEW)) {\r
+                char * value = NULL;\r
+                retval = pprofile_get_string(profile, "libdefaults", "renewable", 0, 0, &value);\r
+\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
+                    pprofile_release_string(value);\r
+                    proff |= K5PARAM_F_RENEW;\r
+                }\r
+            }\r
+\r
+            if (!(regf & K5PARAM_F_ADDL)) {\r
+                char * value = NULL;\r
+                retval = pprofile_get_string(profile, "libdefaults", "noaddresses", 0, 0, &value);\r
+\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
+                    pprofile_release_string(value);\r
+                    proff |= K5PARAM_F_ADDL;\r
+                }\r
+            }\r
+\r
+            if (!(regf & K5PARAM_F_PROX)) {\r
+                char * value = NULL;\r
+                retval = pprofile_get_string(profile, "libdefaults", "proxiable", 0, 0, &value);\r
+\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
+                    pprofile_release_string(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
index ece4f79087d7422d0f74d9b53cf1ceba6d7eb6b7..ce3989682806fd603f9224e1581dbd74d635ca06 100644 (file)
 #include <windows.h>\r
 #define SECURITY_WIN32\r
 #include <security.h>\r
-#include <ntsecapi.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
 \r
 #define KRB5_MAXCCH_CCNAME           1024\r
 \r
-// Function Prototypes.\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
@@ -92,36 +145,23 @@ khm_krb5_renew_cred(khm_handle cred);
 int \r
 khm_krb5_renew_ident(khm_handle identity);\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
 long \r
 khm_krb5_list_tickets(krb5_context *krbv5Context);\r
 \r
-long \r
-khm_krb4_list_tickets(void);\r
-\r
-wchar_t * \r
-khm_get_realm_from_princ(wchar_t * princ);\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_canon_cc_name(wchar_t * wcc_name,\r
-                       size_t cb_cc_name);\r
+khm_krb5_get_temp_ccache(krb5_context ctx,\r
+                         krb5_ccache * cc);\r
 \r
-int \r
-khm_krb5_cc_name_cmp(const wchar_t * cc_name_1,\r
-                     const wchar_t * cc_name_2);\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
@@ -129,8 +169,19 @@ khm_krb5_get_profile_file(LPSTR confname, UINT szConfname);
 BOOL \r
 khm_krb5_get_temp_profile_file(LPSTR confname, UINT szConfname);\r
 \r
-khm_int32 KHMAPI\r
-khm_krb5_creds_is_equal(khm_handle vcred1, khm_handle vcred2, void * dummy);\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
@@ -140,7 +191,26 @@ khm_krb5_set_identity_flags(khm_handle identity,
 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_get_temp_ccache(krb5_context ctx,\r
-                         krb5_ccache * cc);\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
index 34cc8bd20753239adcc726789a10db7de74a2ea0..d52e934cc6e74a1564b60189810ca60d55b4b257 100644 (file)
@@ -635,19 +635,41 @@ k5_kinit_fiber_proc(PVOID lpParameter)
             _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.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
@@ -946,7 +968,20 @@ k5_kinit_prompter(krb5_context context,
 #endif\r
 \r
     /* we got prompts?  Then we assume that the principal is valid */\r
-    g_fjob.valid_principal = TRUE;\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
@@ -1198,7 +1233,7 @@ k5_kinit_prompter(krb5_context context,
        actual acquisition of credentials. */\r
     if(g_fjob.command != FIBER_CMD_CONTINUE &&\r
        g_fjob.command != FIBER_CMD_KINIT) {\r
-        code = -2;\r
+        code = KRB5_LIBOS_PWDINTR;\r
         goto _exit;\r
     }\r
 \r
@@ -1241,164 +1276,32 @@ k5_kinit_prompter(krb5_context context,
 \r
     /* entering a NULL password is equivalent to cancelling out */\r
     if (g_fjob.null_password)\r
-        return -2;\r
+        return KRB5_LIBOS_PWDINTR;\r
     else\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
-k5_open_config_handle(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 done;\r
-\r
-        rv = khc_open_space(csp_i, CSNAME_KRB5CRED, flags, &csp_ik5);\r
-        if (KHM_FAILED(rv))\r
-            goto done;\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_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
-\r
-    } else {\r
-\r
-        /* No valid identity specified. We default to the parameters key. */\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
+void \r
+k5_read_dlg_params(k5_dlg_data * d, khm_handle identity)\r
+{\r
+    k5_params p;\r
 \r
-    *ret_csp = csp_rv;\r
+    khm_krb5_get_identity_params(identity, &p);\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
-    if (csp_realm)\r
-        khc_close_space(csp_realm);\r
-    if (csp_plugins)\r
-        khc_close_space(csp_plugins);\r
-    if (csp_krbcfg)\r
-        khc_close_space(csp_krbcfg);\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
-    return rv;\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
-void \r
-k5_read_dlg_params(khm_handle conf, \r
-                   k5_dlg_data * d)\r
-{\r
-    khm_int32 i;\r
-\r
-    khc_read_int32(conf, L"Renewable", &i);\r
-    d->renewable = i;\r
-    khc_read_int32(conf, L"Forwardable", &i);\r
-    d->forwardable = i;\r
-    khc_read_int32(conf, L"Proxiable", &i);\r
-    d->proxiable = i;\r
-    khc_read_int32(conf, L"Addressless", &i);\r
-    d->addressless = i;\r
-    khc_read_int32(conf, L"PublicIP", &i);\r
-    d->publicIP = i;\r
-\r
-    khc_read_int32(conf, L"DefaultLifetime", &i);\r
-    d->tc_lifetime.current = i;\r
-    khc_read_int32(conf, L"MaxLifetime", &i);\r
-    d->tc_lifetime.max = i;\r
-    khc_read_int32(conf, L"MinLifetime", &i);\r
-    d->tc_lifetime.min = i;\r
-\r
-    khc_read_int32(conf, L"DefaultRenewLifetime", &i);\r
-    d->tc_renew.current = i;\r
-    khc_read_int32(conf, L"MaxRenewLifetime", &i);\r
-    d->tc_renew.max = i;\r
-    khc_read_int32(conf, L"MinRenewLifetime", &i);\r
-    d->tc_renew.min = i;\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
@@ -1431,28 +1334,32 @@ k5_read_dlg_params(khm_handle conf,
 }\r
 \r
 void \r
-k5_write_dlg_params(khm_handle conf, \r
-                    k5_dlg_data * d)\r
+k5_write_dlg_params(k5_dlg_data * d, khm_handle identity)\r
 {\r
-    khc_write_int32(conf, L"Renewable", d->renewable);\r
-    khc_write_int32(conf, L"Forwardable", d->forwardable);\r
-    khc_write_int32(conf, L"Proxiable", d->proxiable);\r
-    khc_write_int32(conf, L"Addressless", d->addressless);\r
-    khc_write_int32(conf, L"PublicIP", d->publicIP);\r
-\r
-    khc_write_int32(conf, L"DefaultLifetime",\r
-                    (khm_int32) d->tc_lifetime.current);\r
-    khc_write_int32(conf, L"MaxLifetime",\r
-                    (khm_int32) d->tc_lifetime.max);\r
-    khc_write_int32(conf, L"MinLifetime",\r
-                    (khm_int32) d->tc_lifetime.min);\r
-\r
-    khc_write_int32(conf, L"DefaultRenewLifetime", \r
-                    (khm_int32) d->tc_renew.current);\r
-    khc_write_int32(conf, L"MaxRenewLifetime", \r
-                    (khm_int32) d->tc_renew.max);\r
-    khc_write_int32(conf, L"MinRenewLifetime", \r
-                    (khm_int32) d->tc_renew.min);\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
@@ -1528,6 +1435,13 @@ k5_prep_kinit_job(khui_new_creds * nc)
     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
@@ -1891,7 +1805,7 @@ k5_msg_cred_dialog(khm_int32 msg_type,
             }\r
 \r
             if (nc->subtype == KMSG_CRED_NEW_CREDS) {\r
-                k5_read_dlg_params(csp_params, d);\r
+                k5_read_dlg_params(d, NULL);\r
             }\r
 \r
             PostMessage(hwnd, KHUI_WM_NC_NOTIFY, \r
@@ -1926,22 +1840,10 @@ k5_msg_cred_dialog(khm_int32 msg_type,
             if(/* !d->dirty && */ nc->n_identities > 0 &&\r
                nc->subtype == KMSG_CRED_NEW_CREDS) {\r
 \r
-                khm_handle h_idcfg = NULL;\r
-\r
-                do {\r
-                    if (KHM_FAILED\r
-                        (k5_open_config_handle(nc->identities[0],\r
-                                               0, &h_idcfg)))\r
-                        break;\r
-\r
-                    k5_read_dlg_params(h_idcfg, d);\r
-\r
-                    PostMessage(nct->hwnd_panel, KHUI_WM_NC_NOTIFY, \r
-                                MAKEWPARAM(0,WMNC_DIALOG_SETUP), 0);\r
-                } while(FALSE);\r
+                k5_read_dlg_params(d, nc->identities[0]);\r
 \r
-                if(h_idcfg)\r
-                    khc_close_space(h_idcfg);\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
@@ -2015,12 +1917,14 @@ k5_msg_cred_dialog(khm_int32 msg_type,
             if (d->pwd_change)\r
                 return KHM_ERROR_SUCCESS;\r
 \r
-            /* At this point, we assume that the current set of\r
-               prompts is no longer valid.  And since we might not be\r
-               able to come up with a set of prompts until the KDC\r
-               replies (unless we have cached prompts), we remove the\r
-               ones that are already shown. */\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
@@ -2259,7 +2163,19 @@ k5_msg_cred_dialog(khm_int32 msg_type,
                                     NULL)))) {\r
                    _reportf(L"No password entered, but a valid TGT exists. Continuing");\r
                     g_fjob.code = 0;\r
-               }\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
@@ -2296,7 +2212,6 @@ k5_msg_cred_dialog(khm_int32 msg_type,
 \r
                 } else if (nc->result == KHUI_NC_RESULT_PROCESS &&\r
                            g_fjob.state == FIBER_STATE_NONE) {\r
-                    khm_handle csp_idcfg = NULL;\r
                     krb5_context ctx = NULL;\r
 \r
                    _reportf(L"Tickets successfully acquired");\r
@@ -2311,16 +2226,7 @@ k5_msg_cred_dialog(khm_int32 msg_type,
                     assert(nc->n_identities > 0);\r
                     assert(nc->identities[0]);\r
 \r
-                    if (KHM_SUCCEEDED\r
-                        (k5_open_config_handle(nc->identities[0],\r
-                                               KHM_FLAG_CREATE |\r
-                                               KCONF_FLAG_WRITEIFMOD,\r
-                                               &csp_idcfg))) {\r
-                        k5_write_dlg_params(csp_idcfg, d);\r
-                    }\r
-\r
-                    if(csp_idcfg != NULL)\r
-                        khc_close_space(csp_idcfg);\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
@@ -2631,16 +2537,8 @@ k5_msg_cred_dialog(khm_int32 msg_type,
                             /* since this was just a password change,\r
                                we need to load new credentials options\r
                                from the configuration store. */\r
-                                                                    \r
-                            if (KHM_SUCCEEDED\r
-                                (k5_open_config_handle(nc->identities[0],\r
-                                                       KHM_FLAG_CREATE |\r
-                                                       KCONF_FLAG_WRITEIFMOD,\r
-                                                       &csp_idcfg))) {\r
-                                k5_read_dlg_params(csp_idcfg, d);\r
-                                khc_close_space(csp_idcfg);\r
-                                csp_idcfg = NULL;\r
-                            }\r
+\r
+                            k5_read_dlg_params(d, nc->identities[0]);\r
                         }\r
 \r
                         /* the password change phase is now done */\r
@@ -2669,14 +2567,9 @@ k5_msg_cred_dialog(khm_int32 msg_type,
 \r
                         /* save the settings that we used for\r
                            obtaining the ticket. */\r
-                        if (nc->subtype == KMSG_CRED_NEW_CREDS &&\r
-                            KHM_SUCCEEDED\r
-                            (k5_open_config_handle(nc->identities[0],\r
-                                                   KHM_FLAG_CREATE |\r
-                                                   KCONF_FLAG_WRITEIFMOD,\r
-                                                   &csp_idcfg))) {\r
-                            k5_write_dlg_params(csp_idcfg, d);\r
-                            khc_close_space(csp_idcfg);\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
index 44b7733e343d602787b65bf6571550487f4da498..d552fd0c8e555ebfb8e9863e32bafefa4571eb4f 100644 (file)
@@ -176,6 +176,7 @@ typedef struct _fiber_job_t {
 \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
@@ -184,8 +185,9 @@ extern fiber_job g_fjob;   /* global fiber job object */
 #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_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
index d93b4415e480e4d9e9beeb6d54d90c3c0465ff63..4d98fd3141a6a9c6a1f31a8ede62b0160a8cfda5 100644 (file)
@@ -338,7 +338,7 @@ IDI_MODIFIED            ICON                    "..\\..\\images\\modified.ico"
 STRINGTABLE \r
 BEGIN\r
     IDS_UNK_ADDR_FMT        "Unknown address type %d"\r
-    IDS_KRB5_CREDTEXT_0     "<p><a id=""SwitchPanel"" param=""Krb5Cred""><b>Krb5</b></a><tab>: Creds for realm %s</p>"\r
+    IDS_KRB5_CREDTEXT_0     "<p><a id=""SwitchPanel"" param=""Krb5Cred""><b>Krb5</b></a><tab>: Tickets for realm %s</p>"\r
     IDS_KRB5_CCNAME_SHORT_DESC "Kerberos v5 CCache"\r
     IDS_KEY_ENCTYPE_SHORT_DESC "Session EncType"\r
     IDS_TKT_ENCTYPE_SHORT_DESC "Service EncType"\r
@@ -501,3 +501,4 @@ END
 /////////////////////////////////////////////////////////////////////////////\r
 #endif    // not APSTUDIO_INVOKED\r
 \r
+\r
index c3355041dce0d8fbcab892e2a68c0a2ed31ec3fe..e328866a1c956fb2622b19a6d0192bb74560e0d7 100644 (file)
@@ -70,7 +70,8 @@ SDKLIBFILES=                  \
        shell32.lib             \
        htmlhelp.lib            \
        iphlpapi.lib            \
-       shlwapi.lib
+       shlwapi.lib             \
+       msimg32.lib
 
 $(OBJ)\uiconfig.c: uiconfig.csv $(CONFDIR)\csvschema.cfg
        $(CCSV) $** $@
index 71edb2688d88cb96dbfb0211c0688168ba0c127f..fa7ac6fd4bb95a9f292e6fa05bab54fcb1439787 100644 (file)
@@ -36,6 +36,7 @@ typedef struct tag_cfg_data {
     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
@@ -81,6 +82,9 @@ read_params(dlg_data * dd) {
     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
@@ -144,6 +148,11 @@ write_params(dlg_data * dd) {
         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
@@ -165,7 +174,8 @@ check_for_modification(dlg_data * dd) {
         !!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->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
@@ -180,9 +190,22 @@ check_for_modification(dlg_data * dd) {
     }\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
@@ -199,6 +222,44 @@ refresh_view(HWND hwnd, dlg_data * d) {
     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
@@ -212,6 +273,8 @@ refresh_view(HWND hwnd, dlg_data * d) {
 \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
@@ -226,6 +289,14 @@ refresh_data(HWND hwnd, dlg_data * d) {
                           == 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
@@ -323,6 +394,9 @@ khm_cfg_general_proc(HWND hwnd,
                 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
index 7b70322126b3830c0d068f099210c3f3ac2feb69..2f1ac8033df58b07bd4b05631b2c0b1e7d658011 100644 (file)
@@ -34,6 +34,7 @@ static HANDLE in_dialog_evt = NULL;
 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
@@ -65,9 +66,28 @@ khm_cred_begin_dialog(void) {
 \r
     EnterCriticalSection(&cs_dialog);\r
 \r
-    if (in_dialog)\r
+    if (in_dialog) {\r
         rv = FALSE;\r
-    else {\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
@@ -87,6 +107,10 @@ khm_cred_end_dialog(khui_new_creds * nc) {
         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
@@ -367,6 +391,22 @@ kmsg_cred_completion(kmq_message *m)
                 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
@@ -763,6 +803,7 @@ void khm_cred_change_password(wchar_t * title)
 \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
@@ -813,6 +854,31 @@ void khm_cred_change_password(wchar_t * title)
     }\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
@@ -824,6 +890,7 @@ void khm_cred_obtain_new_creds(wchar_t * title)
 \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
@@ -1190,6 +1257,8 @@ khm_cred_process_startup_actions(void) {
            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
index b3c88faa4b4716b936ce18bd9666faa2956008b1..761cbf5062deef9507270b644765ae3c2877d492 100644 (file)
@@ -53,6 +53,9 @@ khm_cred_renew_creds(void);
 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
index a555ebeaa3c02f79392293268cf8a77b44c675ca..474bfa9aa00b91783efed429503e44c19ac4fd55 100644 (file)
@@ -403,9 +403,16 @@ cw_load_view(khui_credwnd_tbl * tbl, wchar_t * view, HWND hwnd) {
     int i;\r
     HDC hdc;\r
     LOGFONT log_font;\r
+    khm_int32 t;\r
+    const wchar_t * viewval;\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
@@ -415,7 +422,7 @@ cw_load_view(khui_credwnd_tbl * tbl, wchar_t * view, HWND hwnd) {
 \r
     if(!view) {\r
         cbsize = sizeof(buf);\r
-        if(KHM_FAILED(khc_read_string(hc_cw, L"DefaultView", buf, &cbsize)))\r
+        if(KHM_FAILED(khc_read_string(hc_cw, viewval, buf, &cbsize)))\r
             goto _exit;\r
         view = buf;\r
 \r
@@ -434,10 +441,13 @@ cw_load_view(khui_credwnd_tbl * tbl, wchar_t * view, HWND hwnd) {
         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 if (!wcscmp(view, L"CompactIdentity"))\r
+            khui_check_radio_action(khui_find_menu(KHUI_MENU_LAYOUT),\r
+                                    KHUI_ACTION_LAYOUT_MINI);\r
 \r
-        kmq_post_message(KMSG_ACT, KMSG_ACT_REFRESH, 0, 0);                                \r
+        kmq_post_message(KMSG_ACT, KMSG_ACT_REFRESH, 0, 0);\r
     } else {\r
-        khc_write_string(hc_cw, L"DefaultView", view);\r
+        khc_write_string(hc_cw, viewval, view);\r
     }\r
 \r
     if(KHM_FAILED(khc_open_space(hc_vs, view, KHM_PERM_READ, &hc_v)))\r
@@ -466,6 +476,18 @@ cw_load_view(khui_credwnd_tbl * tbl, wchar_t * view, HWND hwnd) {
 \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
@@ -594,6 +616,8 @@ _skip_col:
     tbl->cr_hdr_outline = RGB(0,0,0);\r
     tbl->cr_hdr_normal = RGB(0,0,0);\r
     tbl->cr_hdr_sel = RGB(255,255,255);\r
+    tbl->cr_hdr_gray = RGB(128,128,128);\r
+    tbl->cr_hdr_gray_sel = RGB(240,240,240);\r
 \r
     tbl->ilist = khui_create_ilist(KHUI_SMICON_CX, KHUI_SMICON_CY-1, 20, 8, 0);\r
     {\r
@@ -652,6 +676,71 @@ _exit:
        We keep that open until the view is unloaded. */\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
+\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
+        cwi->ident = ident;\r
+        kcdb_identity_hold(ident);\r
+        cb = sizeof(cwi->name);\r
+        kcdb_identity_get_name(ident, cwi->name, &cb);\r
+        kcdb_identity_get_type(&cwi->credtype);\r
+        cb = sizeof(cwi->credtype_name);\r
+        kcdb_credtype_describe(cwi->credtype, cwi->credtype_name, &cb, KCDB_TS_SHORT);\r
+        cwi->credcount = 0;\r
+    }\r
+\r
+    cwi = &tbl->idents[i];\r
+\r
+    /* this is the first time we are seeing this */\r
+    if (cwi->credcount == 0) {\r
+        kcdb_identity_get_flags(cwi->ident, &cwi->ident_flags);\r
+    }\r
+\r
+    cwi->credcount++;\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
@@ -751,6 +840,13 @@ cw_update_creds(khui_credwnd_tbl * tbl)
         }\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
+    }\r
+\r
+    kcdb_credset_apply(tbl->credset, cw_credset_iter_func, (void *) tbl);\r
+\r
     if (fields)\r
         PFREE(fields);\r
 }\r
@@ -957,6 +1053,9 @@ cw_set_tbl_row_header(khui_credwnd_tbl * tbl,
     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
+    if ((tbl->flags & KHUI_CW_TBL_EXPIDENT) &&\r
+        tbl->cols[col].attr_id == KCDB_ATTR_ID_NAME)\r
+        tbl->rows[row].flags |= KHUI_CW_ROW_EXPVIEW;\r
 }\r
 \r
 static int \r
@@ -1230,6 +1329,12 @@ cw_update_outline(khui_credwnd_tbl * tbl)
             ol->flags &= ~KHUI_CW_O_SHOWFLAG;\r
             ol->flags &= ~KHUI_CW_O_STICKY;\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
@@ -1256,9 +1361,15 @@ cw_update_outline(khui_credwnd_tbl * tbl)
                     ol->flags |= KHUI_CW_O_SHOWFLAG;\r
                    expstate |= flags;\r
                 }\r
+\r
+                /* if we aren't showing any creds under this outline\r
+                   level, we should also show any flags. */\r
+                else if (grouping[j] == tbl->n_cols - 1) {\r
+                    ol->flags |= KHUI_CW_O_SHOWFLAG;\r
+                }\r
             }\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
@@ -1266,7 +1377,7 @@ cw_update_outline(khui_credwnd_tbl * tbl)
 \r
         flags = cw_get_buf_exp_flags(tbl, thiscred);\r
 \r
-        if(visible) {\r
+        if(visible && 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
@@ -1492,6 +1603,21 @@ cw_unload_view(khui_credwnd_tbl * tbl)
     }\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
@@ -1589,7 +1715,19 @@ cw_update_extents(khui_credwnd_tbl * tbl,
         tbl->cell_height = height + tbl->vpad * 2;\r
     }\r
 \r
-    ext_y = (int) tbl->n_rows * tbl->cell_height;\r
+    if (tbl->flags & KHUI_CW_TBL_EXPIDENT) {\r
+        ext_y = 0;\r
+\r
+        for (i=0; i < (int) tbl->n_rows; i++) {\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
+    } else {\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
@@ -1824,26 +1962,36 @@ cw_draw_header(HDC hdc,
     if (o->flags & KHUI_CW_O_STICKY) {\r
         /* khui_ilist_draw_id(tbl->ilist, IDB_TK_NEW_SM, hdc, \r
            r->left, r->bottom - KHUI_SMICON_CY, 0); */\r
-    } else 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->bottom - KHUI_SMICON_CY, 0);\r
-        } else {\r
-            khui_ilist_draw_id(tbl->ilist, IDB_WDG_COLLAPSE_HI,\r
-                               hdc, r->left, r->bottom - KHUI_SMICON_CY, 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->bottom - KHUI_SMICON_CY, 0);\r
+    } else if (!(o->flags & KHUI_CW_O_NOOUTLINE)) {\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->bottom - (KHUI_SMICON_CY +\r
+                                                (r->bottom - (r->top + KHUI_SMICON_CY)) / 2), 0);\r
+            } else {\r
+                khui_ilist_draw_id(tbl->ilist, IDB_WDG_COLLAPSE_HI,\r
+                                   hdc, r->left,\r
+                                   r->bottom - (KHUI_SMICON_CY +\r
+                                                (r->bottom - (r->top + KHUI_SMICON_CY)) / 2), 0);\r
+            }\r
         } else {\r
-            khui_ilist_draw_id(tbl->ilist, IDB_WDG_COLLAPSE,\r
-                               hdc, r->left, r->bottom - KHUI_SMICON_CY, 0);\r
+            if(o->flags & KHUI_CW_O_EXPAND) {\r
+                khui_ilist_draw_id(tbl->ilist, IDB_WDG_EXPAND,\r
+                                   hdc, r->left,\r
+                                   r->bottom - (KHUI_SMICON_CY +\r
+                                                (r->bottom - (r->top + KHUI_SMICON_CY)) / 2), 0);\r
+            } else {\r
+                khui_ilist_draw_id(tbl->ilist, IDB_WDG_COLLAPSE,\r
+                                   hdc, r->left,\r
+                                   r->bottom - (KHUI_SMICON_CY +\r
+                                                (r->bottom - (r->top + KHUI_SMICON_CY)) / 2), 0);\r
+            }\r
         }\r
-    }\r
 \r
-    r->left += KHUI_SMICON_CX * 3 / 2;\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
@@ -1858,7 +2006,9 @@ cw_draw_header(HDC hdc,
                              IDB_WDG_STUCK:\r
                              IDB_WDG_STICK)),\r
                            hdc,\r
-                           r->left, r->bottom - KHUI_SMICON_CY,\r
+                           r->left,\r
+                           r->bottom - (KHUI_SMICON_CY +\r
+                                        (r->bottom - (r->top + KHUI_SMICON_CY)) / 2),\r
                            0);\r
 \r
         r->left += KHUI_SMICON_CX * 3 / 2;\r
@@ -1868,38 +2018,125 @@ cw_draw_header(HDC hdc,
                             IDB_ID_DIS_SM:\r
                             IDB_ID_SM), \r
                            hdc, \r
-                           r->left, r->bottom - KHUI_SMICON_CY, \r
+                           r->left,\r
+                           r->bottom - (KHUI_SMICON_CY +\r
+                                        (r->bottom - (r->top + KHUI_SMICON_CY)) / 2), \r
                            0);\r
         r->left += KHUI_SMICON_CX * 3 / 2 ;\r
     }\r
 \r
-    /* ok, now o->header contains the string representation of the\r
-       outline value */\r
-    /* for now just write out the value */\r
-    SetTextAlign(hdc, TA_BOTTOM | TA_LEFT);\r
 \r
-    if(selected)\r
-        SetTextColor(hdc, tbl->cr_hdr_sel);\r
-    else\r
-        SetTextColor(hdc, tbl->cr_hdr_normal);\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_sel);\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
+\r
+        /* expanded view */\r
+#ifdef DEBUG\r
+        assert(colattr == KCDB_ATTR_ID_NAME);\r
+#endif\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_sel);\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
+\r
+        if ((idf & KCDB_IDENT_FLAG_DEFAULT)) {\r
+            SIZE size;\r
+            int cx_id;\r
+            int cx_def;\r
+            wchar_t defstr[64];\r
+\r
+            LoadString(khm_hInstance, IDS_CW_DEFAULT,\r
+                       defstr, ARRAYLENGTH(defstr));\r
+\r
+            GetTextExtentPoint32(hdc, o->header, (int) len, &size);\r
+            cx_id = size.cx;\r
+\r
+            len = (int) wcslen(defstr);\r
+            GetTextExtentPoint32(hdc, defstr, (int) len, &size);\r
+            cx_def = size.cx + KHUI_SMICON_CX / 2;\r
 \r
-    TextOut(hdc, r->left, r->bottom - tbl->vpad, o->header, (int) wcslen(o->header));\r
+            tr.left = max(tr.right - cx_def, tr.left + cx_id + KHUI_SMICON_CX * 2);\r
+            if (selected)\r
+                SetTextColor(hdc, tbl->cr_hdr_gray_sel);\r
+            else\r
+                SetTextColor(hdc, tbl->cr_hdr_gray);\r
+            DrawText(hdc, defstr, 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 (colattr == KCDB_ATTR_ID_NAME &&\r
-        (idf & KCDB_IDENT_FLAG_DEFAULT)) {\r
-        wchar_t defstr[64];\r
-        SIZE size;\r
+        if (0) {\r
+            khm_size i;\r
+            wchar_t buf[128];\r
 \r
-        LoadString(khm_hInstance, IDS_CW_DEFAULT,\r
-                   defstr, ARRAYLENGTH(defstr));\r
+            for (i = 0; i < tbl->n_idents; i++) {\r
+                if (kcdb_identity_is_equal(o->data, tbl->idents[i].ident))\r
+                    break;\r
+            }\r
 \r
-        GetTextExtentPoint32(hdc, o->header, (int) wcslen(o->header),\r
-                             &size);\r
+            if (i < tbl->n_idents) {\r
+                khui_credwnd_ident * cwi;\r
+\r
+                cwi = &tbl->idents[i];\r
+\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
+            }\r
 \r
-        r->left += size.cx + KHUI_SMICON_CX * 2;\r
+            len = (int) wcslen(buf);\r
 \r
-        TextOut(hdc, r->left, r->bottom - tbl->vpad, \r
-                defstr, (int) wcslen(defstr));\r
+            if (selected)\r
+                SetTextColor(hdc, tbl->cr_hdr_gray_sel);\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
@@ -2334,6 +2571,7 @@ cw_wm_paint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
     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
@@ -2380,11 +2618,9 @@ cw_wm_paint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 \r
         /* We *NEED* all the meta columns to be on the left */\r
 \r
-        row_s = tbl->scr_top / tbl->cell_height;\r
-        ys = row_s * tbl->cell_height;\r
-        row_e = (tbl->scr_top + (r.bottom - r.top)) / tbl->cell_height + 1;\r
-        if(row_e > (int) tbl->n_rows)\r
-            row_e = (int) tbl->n_rows;\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
@@ -2422,6 +2658,7 @@ cw_wm_paint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
         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
@@ -2439,7 +2676,7 @@ cw_wm_paint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
                 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 + tbl->cell_height;\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
@@ -2456,7 +2693,7 @@ cw_wm_paint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 \r
             x = xs;\r
             rh.top = y;\r
-            rh.bottom = y + tbl->cell_height;\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
@@ -2539,7 +2776,7 @@ cw_wm_paint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
                 SelectFont(hdc, tbl->hf_normal);\r
             }\r
 \r
-            y += tbl->cell_height;\r
+            y += rowheight;\r
 \r
         }\r
 \r
@@ -3292,7 +3529,24 @@ cw_wm_mouse(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
     x += tbl->scr_left;\r
     y += tbl->scr_top - tbl->header_height;\r
 \r
-    row = y / tbl->cell_height;\r
+    if (tbl->flags & KHUI_CW_TBL_EXPIDENT) {\r
+        int ty = 0;\r
+\r
+        for (i=0; i < tbl->n_rows; i++) {\r
+            if (tbl->rows[i].flags & KHUI_CW_ROW_EXPVIEW)\r
+                ty += tbl->cell_height * CW_EXP_ROW_MULT;\r
+            else\r
+                ty += tbl->cell_height;\r
+\r
+            if (ty > y)\r
+                break;\r
+        }\r
+\r
+        row = i;\r
+    } else {\r
+        row = y / tbl->cell_height;\r
+    }\r
+\r
     col = -1;\r
     nm_state = CW_MOUSE_NONE;\r
     nm_row = nm_col = -1;\r
@@ -3313,22 +3567,41 @@ cw_wm_mouse(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
         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[tbl->rows[row].col].x;\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
+            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[tbl->rows[row].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
@@ -3408,12 +3681,50 @@ cw_wm_mouse(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
             InvalidateRect(tbl->hwnd, &r, TRUE);\r
         }\r
         if(tbl->mouse_state & CW_MOUSE_WSTICKY) {\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
+            if (tbl->flags & KHUI_CW_TBL_EXPIDENT) {\r
+                int ty = 0;\r
+\r
+                for (i=0; i < tbl->n_rows && i < tbl->mouse_row; i++) {\r
+                    if (tbl->rows[i].flags & KHUI_CW_ROW_EXPVIEW)\r
+                        ty += tbl->cell_height * CW_EXP_ROW_MULT;\r
+                    else\r
+                        ty += tbl->cell_height;\r
+                }\r
+\r
+                r.top = ty;\r
+\r
+                if (tbl->mouse_row < tbl->n_rows &&\r
+                    (tbl->rows[tbl->mouse_row].flags & KHUI_CW_ROW_EXPVIEW)) {\r
+                    r.bottom = r.top + tbl->cell_height * CW_EXP_ROW_MULT;\r
+                } else {\r
+                    r.bottom = r.top + tbl->cell_height;\r
+                }\r
+\r
+                if (tbl->mouse_row < tbl->n_rows &&\r
+                    (tbl->rows[tbl->mouse_row].flags & KHUI_CW_ROW_HEADER)) {\r
+                    khui_credwnd_outline * o;\r
+\r
+                    o = (khui_credwnd_outline *) tbl->rows[tbl->mouse_row].data;\r
+\r
+                    if (o->flags & KHUI_CW_O_NOOUTLINE) {\r
+                        r.left = tbl->cols[tbl->mouse_col].x - tbl->scr_left;\r
+                    } else {\r
+                        r.left = KHUI_SMICON_CX * 3 / 2 + tbl->cols[tbl->mouse_col].x - tbl->scr_left;\r
+                    }\r
+                    r.right = r.left + KHUI_SMICON_CX;\r
+                } else {\r
+#ifdef DEBUG\r
+                    assert(FALSE);\r
+#endif\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
@@ -3436,12 +3747,50 @@ cw_wm_mouse(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
             InvalidateRect(tbl->hwnd, &r, TRUE);\r
         }\r
         if(tbl->mouse_state & CW_MOUSE_WSTICKY) {\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
+            if (tbl->flags & KHUI_CW_TBL_EXPIDENT) {\r
+                int ty = 0;\r
+\r
+                for (i=0; i < tbl->n_rows && i < tbl->mouse_row; i++) {\r
+                    if (tbl->rows[i].flags & KHUI_CW_ROW_EXPVIEW)\r
+                        ty += tbl->cell_height * CW_EXP_ROW_MULT;\r
+                    else\r
+                        ty += tbl->cell_height;\r
+                }\r
+\r
+                r.top = ty;\r
+\r
+                if (tbl->mouse_row < tbl->n_rows &&\r
+                    (tbl->rows[tbl->mouse_row].flags & KHUI_CW_ROW_EXPVIEW)) {\r
+                    r.bottom = r.top + tbl->cell_height * CW_EXP_ROW_MULT;\r
+                } else {\r
+                    r.bottom = r.top + tbl->cell_height;\r
+                }\r
+\r
+                if (tbl->mouse_row < tbl->n_rows &&\r
+                    (tbl->rows[tbl->mouse_row].flags & KHUI_CW_ROW_HEADER)) {\r
+                    khui_credwnd_outline * o;\r
+\r
+                    o = (khui_credwnd_outline *) tbl->rows[tbl->mouse_row].data;\r
+\r
+                    if (o->flags & KHUI_CW_O_NOOUTLINE) {\r
+                        r.left = tbl->cols[tbl->mouse_col].x - tbl->scr_left;\r
+                    } else {\r
+                        r.left = KHUI_SMICON_CX * 3 / 2 + tbl->cols[tbl->mouse_col].x - tbl->scr_left;\r
+                    }\r
+                    r.right = r.left + KHUI_SMICON_CX;\r
+                } else {\r
+#ifdef DEBUG\r
+                    assert(FALSE);\r
+#endif\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
@@ -4285,6 +4634,22 @@ cw_wm_command(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
         }\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_extents(tbl, FALSE);\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
@@ -4513,7 +4878,24 @@ cw_wm_contextmenu(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
         return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
     }\r
 \r
-    row = y / tbl->cell_height;\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
index 50b7a114f6f16445716fd3ebefd6301d1e9db53e..3d39ebc4f1d796f0e60e1ff7e96300520f4a7bf1 100644 (file)
@@ -67,6 +67,7 @@ typedef struct khui_credwnd_outline_t {
 #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
 \r
 typedef struct khui_credwnd_row_t {\r
     khm_int32   flags;\r
@@ -80,6 +81,7 @@ typedef struct khui_credwnd_row_t {
 #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
 \r
 /* row allocation */\r
 /* initial number of rows to be allocated */\r
@@ -119,6 +121,22 @@ typedef struct khui_credwnd_col_t {
 \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;\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
@@ -171,6 +189,8 @@ typedef struct khui_credwnd_tbl_t {
     COLORREF cr_sel;        /* selected text color */\r
     COLORREF cr_hdr_normal; /* normal header text color */\r
     COLORREF cr_hdr_sel;    /* selected header text color */\r
+    COLORREF cr_hdr_gray;   /* gray header text color */\r
+    COLORREF cr_hdr_gray_sel;   /* selected gray header text color */\r
     HBRUSH hb_hdr_bg;       /* header background color (normal) */\r
     HBRUSH hb_hdr_bg_exp;   /* header background color (expired) */\r
     HBRUSH hb_hdr_bg_warn;  /* header background color (warn) */\r
@@ -198,6 +218,11 @@ typedef struct khui_credwnd_tbl_t {
 \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
@@ -209,6 +234,8 @@ typedef struct khui_credwnd_tbl_t {
 #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
index 6fadebb945ff1cdb8ce179f96d5c6441a4ad356a..b8e2e3d2a64f139c193a181cf5da16708f7fb2d4 100644 (file)
@@ -240,23 +240,25 @@ IDD_CFG_GENERAL DIALOGEX 0, 0, 255, 182
 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU\r
 FONT 8, "MS Shell Dlg", 400, 0, 0x1\r
 BEGIN\r
-    GROUPBOX        "Startup / Shutdown",IDC_CFG_STARTUP_GROUP,7,7,241,50\r
+    GROUPBOX        "Startup / Shutdown",IDC_CFG_STARTUP_GROUP,7,7,241,44\r
     CONTROL         "&Obtain new credentials at startup (if none are present)",IDC_CFG_AUTOINIT,\r
-                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,22,196,10\r
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,20,196,10\r
     CONTROL         "&Destroy all credentials on exit",IDC_CFG_DESTROYALL,\r
-                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,39,111,10\r
-    CONTROL         "&Start Network Identity Manager during Windows logon",IDC_CFG_AUTOSTART,\r
-                    "Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,16,48,135,10\r
-    GROUPBOX        "Other",IDC_CFG_OTHER,7,63,241,85\r
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,36,111,10\r
+    GROUPBOX        "Other",IDC_CFG_OTHER,7,55,241,120\r
     CONTROL         "&Run Network Identity Manager in system tray after window close",IDC_CFG_KEEPRUNNING,\r
-                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,78,230,10\r
-    CONTROL         "Monitor network connectivity",IDC_CFG_NETDETECT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,96,106,10\r
-    CONTROL         "Log trace events to trace log at the following location:",IDC_CFG_LOGTOFILE,\r
-                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,113,225,10\r
-    EDITTEXT        IDC_CFG_LOGPATH,16,127,173,14,ES_AUTOHSCROLL | ES_READONLY\r
-    PUSHBUTTON      "Show log ...",IDC_CFG_SHOWLOG,193,127,50,14\r
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,68,230,10\r
+    LTEXT           "Clicking on the &notification icon",IDC_CFG_NOTACT_STATIC,26,87,99,8\r
+    COMBOBOX        IDC_CFG_NOTACTION,133,85,110,48,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP\r
+    CONTROL         "&Monitor network connectivity",IDC_CFG_NETDETECT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,104,106,10\r
+    CONTROL         "&Log trace events to trace log at the following location:",IDC_CFG_LOGTOFILE,\r
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,121,225,10\r
+    EDITTEXT        IDC_CFG_LOGPATH,16,135,173,14,ES_AUTOHSCROLL | ES_READONLY\r
+    PUSHBUTTON      "&Show log",IDC_CFG_SHOWLOG,193,135,50,14\r
     CONTROL         "A&utomatically import Windows logon identity",IDC_CFG_AUTOIMPORT,\r
-                    "Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,16,158,165,10\r
+                    "Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,16,156,165,10\r
+    CONTROL         "&Start Network Identity Manager during Windows logon",IDC_CFG_AUTOSTART,\r
+                    "Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,113,165,135,10\r
 END\r
 \r
 IDD_CFG_IDENTITIES DIALOGEX 0, 0, 255, 182\r
@@ -342,18 +344,18 @@ BEGIN
     PUSHBUTTON      "Remove identity ...",IDC_CFG_REMOVE,139,122,78,14\r
 END\r
 \r
-IDD_ABOUT DIALOGEX 0, 0, 268, 170\r
+IDD_ABOUT DIALOGEX 0, 0, 324, 238\r
 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
 CAPTION "About Network Identity Manager"\r
 FONT 8, "MS Shell Dlg", 400, 0, 0x1\r
 BEGIN\r
-    DEFPUSHBUTTON   "OK",IDOK,211,7,50,14\r
-    LTEXT           "Productname",IDC_PRODUCT,41,7,163,13,NOT WS_GROUP\r
-    LTEXT           "© 2005-2007 Massachusetts Institute of Technology\n© 2006-2007 Secure Endpoints Inc.",IDC_COPYRIGHT,41,23,220,18,NOT WS_GROUP\r
-    LTEXT           "BuildInfo",IDC_BUILDINFO,41,43,220,15,NOT WS_GROUP\r
-    ICON            IDI_MAIN_APP,IDC_STATIC,6,7,20,20\r
-    CONTROL         "",IDC_MODULES,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,41,72,220,91\r
-    LTEXT           "Loaded modules",IDC_STATIC,41,60,52,8\r
+    DEFPUSHBUTTON   "OK",IDOK,267,7,50,14\r
+    LTEXT           "Productname",IDC_PRODUCT,41,7,225,13,NOT WS_GROUP\r
+    LTEXT           "© 2005-2007 Massachusetts Institute of Technology\n© 2006-2007 Secure Endpoints Inc.",IDC_COPYRIGHT,41,23,276,23,NOT WS_GROUP\r
+    LTEXT           "BuildInfo",IDC_BUILDINFO,41,49,276,20,NOT WS_GROUP\r
+    ICON            IDI_MAIN_APP,IDC_STATIC,6,7,21,20\r
+    CONTROL         "",IDC_MODULES,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,41,88,276,143\r
+    LTEXT           "Loaded modules",IDC_STATIC,41,76,52,8\r
 END\r
 \r
 IDD_CFG_APPEAR DIALOGEX 0, 0, 255, 182\r
@@ -507,11 +509,11 @@ BEGIN
     IDD_ABOUT, DIALOG\r
     BEGIN\r
         LEFTMARGIN, 6\r
-        RIGHTMARGIN, 261\r
+        RIGHTMARGIN, 317\r
         VERTGUIDE, 41\r
-        VERTGUIDE, 204\r
+        VERTGUIDE, 266\r
         TOPMARGIN, 7\r
-        BOTTOMMARGIN, 163\r
+        BOTTOMMARGIN, 231\r
     END\r
 \r
     IDD_CFG_APPEAR, DIALOG\r
@@ -623,7 +625,7 @@ BEGIN
     IDS_PROP_COL_VALUE      "Value"\r
     IDS_NC_NEW_IDENT        "( New identity ... )"\r
     IDS_NC_CREDTEXT_ID_CHECKING "<font color=""grey"">%s (Checking...)</font>"\r
-    IDS_ACTION_OPEN_APP     "Open Network Identity Manager"\r
+    IDS_ACTION_OPEN_APP     "Show Network Identity Manager window"\r
     IDS_CTX_NEW_IDENT       "Obaining new identity"\r
     IDS_CTX_NEW_CREDS       "Obtaining new credentials"\r
     IDS_CTX_RENEW_CREDS     "Renewing credentials"\r
@@ -634,7 +636,7 @@ STRINGTABLE
 BEGIN\r
     IDS_CTX_PROC_NEW_CREDS  "Obtaining new credentials for %1!s!"\r
     IDS_CTX_PROC_RENEW_CREDS "Renewing credentials for %1!s!"\r
-    IDS_ACTION_CLOSE_APP    "Close Network Identity Manager window"\r
+    IDS_ACTION_CLOSE_APP    "Hide Network Identity Manager window"\r
     IDS_NC_FAILED_TITLE     "Failed to acquire credentials"\r
     IDS_CFG_IDENTITIES_SHORT "Identities"\r
     IDS_CFG_IDENTITIES_LONG "Global Identity settings"\r
@@ -774,20 +776,37 @@ END
 \r
 STRINGTABLE \r
 BEGIN\r
-    IDS_NC_REN_FAILED_TITLE_I "Failed to renew creds for %s"\r
+    IDS_NC_REN_FAILED_TITLE_I "Failed to renew credentials for %s"\r
     IDS_CFG_IDNAME_NON      "No identity selected.  Please select an identity and try again."\r
     IDS_MENU_DESTROY_CRED   "Destroy"\r
     IDS_MENU_RENEW_CRED     "Renew"\r
-    IDS_ACTION_DESTROY_ALL  "All identities"\r
-    IDS_ACTION_RENEW_ALL    "All identities"\r
-    IDS_IDACTION_RENEW      "Renew credentials for %s"\r
-    IDS_IDACTION_DESTROY    "Destroy credentials for %s"\r
+    IDS_ACTION_DESTROY_ALL  "Destroy all identities"\r
+    IDS_ACTION_RENEW_ALL    "Renew all identities"\r
+    IDS_IDACTION_RENEW      "Renew %s"\r
+    IDS_IDACTION_DESTROY    "Destroy %s"\r
     IDS_CTX_DESTROY_ID      "Destroying identity %1!s!"\r
     IDS_NCN_IDENT_INVALID   "Identity %s is invalid."\r
     IDS_NCN_IDENT_CHECKING  "Checking identity %s ..."\r
     IDS_NCN_IDENT_UNKNOWN   "Validity of identity %s coudn't be determined."\r
     IDS_REMOTE_FAIL         "The instance of Network Identity Manager that is already running is not responding to the remote request properly.  Please check if you are running the latest version of Network Identity Manger software included with MIT Kerberos for Windows."\r
     IDS_REMOTE_FAIL_TITLE   "Failed to communicate with Network Identity Manager"\r
+    IDS_IDACTION_NEW        "Initialize %s"\r
+    IDS_IDACTIONT_NEW       "New credentials for %s"\r
+END\r
+\r
+STRINGTABLE \r
+BEGIN\r
+    IDS_IDACTIONT_RENEW     "Renew credentials for %s"\r
+    IDS_IDACTIONT_DESTROY   "Destroy credentials for %s"\r
+    IDS_ALERTTYPE_PLUGIN    "Failed to load plug-in"\r
+    IDS_ALERTTYPE_EXPIRE    "Credential expiration warning"\r
+    IDS_ALERTTYPE_RENEWFAIL "Failed to renew credentials"\r
+    IDS_ALERTTYPE_ACQUIREFAIL "Failed to acquire credentials"\r
+    IDS_ALERTTYPE_CHPW      "Failed to change password"\r
+    IDS_ACTION_LAYOUT_MINI  "&Advanced"\r
+    IDS_IDEXPDISP_NOCRED    "(This identity has no credentials)"\r
+    IDS_IDEXPDISP_1CRED     "(This identity has 1 credential)"\r
+    IDS_IDEXPDISP_NCRED     "(This identity has %d credentials)"\r
 END\r
 \r
 #endif    // English (U.S.) resources\r
index 37ebe125fad42b967bdeab6fff38c03138ed8153..82d0fb75d01a682aa35228a6722686367faff08e 100644 (file)
@@ -202,9 +202,7 @@ void khm_add_dialog(HWND dlg) {
     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
-        /* we set .active=FALSE for now.  We don't need this to have a\r
-           meaningful value until we enter a modal loop */\r
-        khui_dialogs[n_khui_dialogs].active = FALSE;\r
+        khui_dialogs[n_khui_dialogs].active = TRUE;\r
         n_khui_dialogs++;\r
     } else {\r
 #if DEBUG\r
index e4267035e05c2711069dcff9b691474752515888..70913f472202cf4875b40d7f054a56df64167780 100644 (file)
@@ -76,6 +76,50 @@ int khui_get_icon_index(int id) {
     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
@@ -93,13 +137,7 @@ void add_action_to_menu(HMENU hm, khui_action * act,
     } else {\r
         khui_menu_def * def;\r
 \r
-        if (act->caption) {\r
-            StringCbCopy(buf, sizeof(buf), act->caption);\r
-        } else {\r
-            LoadString(khm_hInstance, \r
-                       act->is_caption, \r
-                       buf, ARRAYLENGTH(buf));\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
@@ -141,8 +179,10 @@ void add_action_to_menu(HMENU hm, khui_action * act,
             }\r
         }\r
 \r
-        if(flags & KHUI_ACTIONREF_DEFAULT)\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
@@ -208,33 +248,27 @@ static void refresh_menu_item(HMENU hm, khui_action * act,
 \r
 static void refresh_menu(HMENU hm, khui_menu_def * def) {\r
     khui_action_ref * act;\r
-    int i;\r
+    khm_size i, n;\r
 \r
-    act = def->items;\r
-    i = 0;\r
-    while ((def->n_items == -1 && act->action != KHUI_MENU_END) ||\r
-           (def->n_items >= 0 && i < (int) def->n_items)) {\r
-        refresh_menu_item(hm, khui_find_action(act->action), i, act->flags);\r
-        act++; i++;\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
 \r
 static HMENU mm_create_menu_from_def(khui_menu_def * def, BOOL main) {\r
     HMENU hm;\r
     khui_action_ref * act;\r
-    int i;\r
+    khm_size i, n;\r
 \r
     if (main)\r
         hm = CreateMenu();\r
     else\r
         hm = CreatePopupMenu();\r
 \r
-    act = def->items;\r
-    i = 0;\r
-    while((!(def->state & KHUI_MENUSTATE_ALLOCD) && act->action != KHUI_MENU_END) ||\r
-          ((def->state & KHUI_MENUSTATE_ALLOCD) && i < (int) def->n_items)) {\r
-        add_action_to_menu(hm,khui_find_action(act->action),i,act->flags);\r
-        act++; i++;\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
@@ -415,12 +449,8 @@ LRESULT khm_menu_handle_select(WPARAM wParam, LPARAM lParam) {
         if(act == NULL || (act->is_tooltip == 0 && act->tooltip == NULL))\r
             khm_statusbar_set_part(KHUI_SBPART_INFO, NULL, NULL);\r
         else {\r
-            if (act->tooltip)\r
-                StringCbCopy(buf, sizeof(buf), act->tooltip);\r
-            else\r
-                LoadString(khm_hInstance, \r
-                           act->is_tooltip, \r
-                           buf, ARRAYLENGTH(buf));\r
+            khm_get_action_tooltip(act->cmd, buf, sizeof(buf));\r
+\r
             khm_statusbar_set_part(KHUI_SBPART_INFO, NULL, buf);\r
         }\r
     }\r
@@ -562,6 +592,7 @@ struct identity_action_map {
     khm_handle identity;\r
     khm_int32  renew_cmd;\r
     khm_int32  destroy_cmd;\r
+    khm_int32  new_cmd;\r
     int        refreshcycle;\r
 };\r
 \r
@@ -579,7 +610,9 @@ create_identity_cmd_map(khm_handle ident) {
     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
@@ -607,26 +640,55 @@ create_identity_cmd_map(khm_handle ident) {
     actmap->identity = ident;\r
     kcdb_identity_hold(ident);\r
 \r
-    fmt[0] = L'\0';\r
-    LoadString(khm_hInstance, IDS_IDACTION_RENEW,\r
-               fmt, ARRAYLENGTH(fmt));\r
-    StringCbPrintf(tooltip, sizeof(tooltip), fmt, idname);\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(NULL, idname, tooltip, NULL,\r
+        khui_action_create(actionname, caption, tooltip, NULL,\r
                            KHUI_ACTIONTYPE_TRIGGER, NULL);\r
 \r
-    fmt[0] = L'\0';\r
-    LoadString(khm_hInstance, IDS_IDACTION_DESTROY,\r
-               fmt, ARRAYLENGTH(fmt));\r
-    StringCbPrintf(tooltip, sizeof(tooltip), fmt, idname);\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(NULL, idname, tooltip, NULL,\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
@@ -669,8 +731,8 @@ get_identity_cmd_map(khm_handle ident) {
     }\r
 }\r
 \r
-static khm_int32\r
-get_identity_renew_command(khm_handle ident) {\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
@@ -681,8 +743,8 @@ get_identity_renew_command(khm_handle ident) {
         return 0;\r
 }\r
 \r
-static khm_int32\r
-get_identity_destroy_command(khm_handle ident) {\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
@@ -693,6 +755,18 @@ get_identity_destroy_command(khm_handle ident) {
         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
@@ -781,11 +855,11 @@ khm_refresh_identity_menus(void) {
         }\r
 \r
         khui_menu_insert_action(renew_def, 1000,\r
-                                get_identity_renew_command(identity),\r
+                                khm_get_identity_renew_action(identity),\r
                                 0);\r
 \r
         khui_menu_insert_action(dest_def, 1000,\r
-                                get_identity_destroy_command(identity),\r
+                                khm_get_identity_destroy_action(identity),\r
                                 0);\r
     }\r
 \r
@@ -834,6 +908,12 @@ khm_check_identity_menu_action(khm_int32 act_id) {
                 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
index a0f64a0a6ea92a30622b8ad45ebf570847779769..e8da77f39f0fe9cf06236704c9b29e196bdf8306 100644 (file)
@@ -49,6 +49,12 @@ LRESULT khm_menu_draw_item(WPARAM wParam, LPARAM lparam);
 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
index 05e615786cbbd6a754906d7c21b98bc685ee9106..494ef4bb64d84aba8c46647d7364ddcb08c92a8c 100644 (file)
@@ -36,6 +36,8 @@ HWND khm_hwnd_main;
 HWND khm_hwnd_rebar;\r
 HWND khm_hwnd_main_cred;\r
 \r
+int  khm_main_wnd_mode = KHM_MAIN_WND_NORMAL;\r
+\r
 #define MW_RESIZE_TIMER 1\r
 #define MW_RESIZE_TIMEOUT 2000\r
 #define MW_REFRESH_TIMER 2\r
@@ -193,7 +195,14 @@ khm_ui_cb(LPARAM lParam) {
 #endif\r
 \r
     /* make the call */\r
-    pcbdata->rv = (*pcbdata->cb)(khm_hwnd_main, pcbdata->rock);\r
+    if (!IsBadCodePtr(pcbdata->cb))\r
+        pcbdata->rv = (*pcbdata->cb)(khm_hwnd_main, pcbdata->rock);\r
+    else {\r
+#ifdef DEBUG\r
+        assert(FALSE);\r
+#endif\r
+        pcbdata->rv = KHM_ERROR_INVALID_PARAM;\r
+    }\r
 }\r
 \r
 LRESULT CALLBACK \r
@@ -368,6 +377,25 @@ khm_main_wnd_proc(HWND hwnd,
             khm_ui_cb(lParam);\r
             return 0;\r
 \r
+            /* layout control */\r
+        case KHUI_ACTION_LAYOUT_MINI:\r
+            if (khm_main_wnd_mode == KHM_MAIN_WND_MINI) {\r
+                khm_set_main_window_mode(KHM_MAIN_WND_NORMAL);\r
+            } else {\r
+                khm_set_main_window_mode(KHM_MAIN_WND_MINI);\r
+            }\r
+            return SendMessage(khm_hwnd_main_cred, uMsg, \r
+                               wParam, lParam);\r
+\r
+        case KHUI_ACTION_LAYOUT_ID:\r
+        case KHUI_ACTION_LAYOUT_TYPE:\r
+        case KHUI_ACTION_LAYOUT_LOC:\r
+        case KHUI_ACTION_LAYOUT_CUST:\r
+        case KHUI_ACTION_LAYOUT_RELOAD:\r
+            khm_set_main_window_mode(KHM_MAIN_WND_NORMAL);\r
+            return SendMessage(khm_hwnd_main_cred, uMsg, \r
+                               wParam, lParam);\r
+\r
             /* menu commands */\r
         case KHUI_PACTION_MENU:\r
             if(HIWORD(lParam) == 1)\r
@@ -423,11 +451,6 @@ khm_main_wnd_proc(HWND hwnd,
         case KHUI_PACTION_DELETE:\r
 \r
         case KHUI_PACTION_SELALL:\r
-        case KHUI_ACTION_LAYOUT_ID:\r
-        case KHUI_ACTION_LAYOUT_TYPE:\r
-        case KHUI_ACTION_LAYOUT_LOC:\r
-        case KHUI_ACTION_LAYOUT_CUST:\r
-        case KHUI_ACTION_LAYOUT_RELOAD:\r
             /* otherwise fallthrough and bounce to the creds window */\r
             return SendMessage(khm_hwnd_main_cred, uMsg, \r
                                wParam, lParam);\r
@@ -525,11 +548,27 @@ khm_main_wnd_proc(HWND hwnd,
         }\r
         break;\r
 \r
+    case WM_MOVING:\r
+        {\r
+            RECT * r;\r
+\r
+            r = (RECT *) lParam;\r
+            khm_adjust_window_dimensions_for_display(r,\r
+                                                     KHM_DOCK_AUTO | KHM_DOCKF_XBORDER);\r
+        }\r
+        return TRUE;\r
+\r
     case WM_TIMER:\r
         if (wParam == MW_RESIZE_TIMER) {\r
             RECT r;\r
             khm_handle csp_cw;\r
             khm_handle csp_mw;\r
+            const wchar_t * wconfig;\r
+\r
+            if (khm_main_wnd_mode == KHM_MAIN_WND_MINI)\r
+                wconfig = L"Windows\\MainMini";\r
+            else\r
+                wconfig = L"Windows\\Main";\r
 \r
             KillTimer(hwnd, wParam);\r
 \r
@@ -540,9 +579,11 @@ khm_main_wnd_proc(HWND hwnd,
                                              KHM_PERM_WRITE,\r
                                              &csp_cw))) {\r
                 if (KHM_SUCCEEDED(khc_open_space(csp_cw,\r
-                                                 L"Windows\\Main",\r
+                                                 wconfig,\r
                                                  KHM_PERM_WRITE,\r
                                                  &csp_mw))) {\r
+                    khm_int32 t;\r
+\r
                     khc_write_int32(csp_mw, L"XPos", r.left);\r
                     khc_write_int32(csp_mw, L"YPos", r.top);\r
                     khc_write_int32(csp_mw, L"Width",\r
@@ -550,6 +591,11 @@ khm_main_wnd_proc(HWND hwnd,
                     khc_write_int32(csp_mw, L"Height",\r
                                     r.bottom - r.top);\r
 \r
+                    if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"Dock", &t)) &&\r
+                        t != KHM_DOCK_NONE) {\r
+                        khc_write_int32(csp_mw, L"Dock", KHM_DOCK_AUTO);\r
+                    }\r
+\r
                     khc_close_space(csp_mw);\r
                 }\r
                 khc_close_space(csp_cw);\r
@@ -584,6 +630,9 @@ khm_main_wnd_proc(HWND hwnd,
             } else if (m->type == KMSG_ACT &&\r
                        m->subtype == KMSG_ACT_CONTINUE_CMDLINE) {\r
                 khm_cred_process_startup_actions();\r
+            } else if (m->type == KMSG_ACT &&\r
+                       m->subtype == KMSG_ACT_END_CMDLINE) {\r
+                /* nothing yet */\r
             } else if (m->type == KMSG_ACT &&\r
                        m->subtype == KMSG_ACT_SYNC_CFG) {\r
                 khm_refresh_config();\r
@@ -618,6 +667,7 @@ khm_main_wnd_proc(HWND hwnd,
                        m->subtype == KMSG_KMM_I_DONE) {\r
                 kmq_post_message(KMSG_ACT, KMSG_ACT_BEGIN_CMDLINE, 0, 0);\r
             }\r
+\r
             return kmq_wm_end(m, rv);\r
         }\r
         return 0;\r
@@ -860,7 +910,7 @@ khm_create_main_window_controls(HWND hwnd_main) {
 }\r
 \r
 void\r
-khm_adjust_window_dimensions_for_display(RECT * pr) {\r
+khm_adjust_window_dimensions_for_display(RECT * pr, int dock) {\r
 \r
     HMONITOR hmon;\r
     RECT     rm;\r
@@ -908,14 +958,57 @@ khm_adjust_window_dimensions_for_display(RECT * pr) {
     if (height > (rm.bottom - rm.top))\r
         height = rm.bottom - rm.top;\r
 \r
-    if (x < rm.left)\r
+    switch (dock & KHM_DOCKF_DOCKHINT) {\r
+    case KHM_DOCK_TOPLEFT:\r
         x = rm.left;\r
-    if (x + width > rm.right)\r
+        y = rm.top;\r
+        break;\r
+\r
+    case KHM_DOCK_TOPRIGHT:\r
         x = rm.right - width;\r
-    if (y < rm.top)\r
         y = rm.top;\r
-    if (y + height > rm.bottom)\r
+        break;\r
+\r
+    case KHM_DOCK_BOTTOMRIGHT:\r
+        x = rm.right - width;\r
         y = rm.bottom - height;\r
+        break;\r
+\r
+    case KHM_DOCK_BOTTOMLEFT:\r
+        x = rm.left;\r
+        y = rm.bottom - height;\r
+        break;\r
+\r
+    case KHM_DOCK_AUTO:\r
+        {\r
+            int cxt, cyt;\r
+\r
+            cxt = GetSystemMetrics(SM_CXDRAG);\r
+            cyt = GetSystemMetrics(SM_CYDRAG);\r
+\r
+            if (x > rm.left && (x - rm.left) < cxt)\r
+                x = rm.left;\r
+            else if ((x + width) < rm.right && (rm.right - (x + width)) < cxt)\r
+                x = rm.right - width;\r
+\r
+            if (y > rm.top && (y - rm.top) < cyt)\r
+                y = rm.top;\r
+            else if ((y + height) < rm.bottom && (rm.bottom - (y + height)) < cyt)\r
+                y = rm.bottom - height;\r
+        }\r
+        break;\r
+    }\r
+\r
+    if (!(dock & KHM_DOCKF_XBORDER)) {\r
+        if (x < rm.left)\r
+            x = rm.left;\r
+        if (x + width > rm.right)\r
+            x = rm.right - width;\r
+        if (y < rm.top)\r
+            y = rm.top;\r
+        if (y + height > rm.bottom)\r
+            y = rm.bottom - height;\r
+    }\r
 \r
  done_with_monitor:\r
     pr->left = x;\r
@@ -925,13 +1018,109 @@ khm_adjust_window_dimensions_for_display(RECT * pr) {
 \r
 }\r
 \r
+void\r
+khm_get_main_window_rect(RECT * pr) {\r
+    khm_handle csp_mw = NULL;\r
+    int x,y,width,height,dock;\r
+    RECT r;\r
+    const wchar_t * wconfig;\r
+\r
+    x = CW_USEDEFAULT;\r
+    y = CW_USEDEFAULT;\r
+    width = CW_USEDEFAULT;\r
+    height = CW_USEDEFAULT;\r
+    dock = KHM_DOCK_NONE;\r
+\r
+    if (khm_main_wnd_mode == KHM_MAIN_WND_MINI)\r
+        wconfig = L"CredWindow\\Windows\\MainMini";\r
+    else\r
+        wconfig = L"CredWindow\\Windows\\Main";\r
+\r
+    if (KHM_SUCCEEDED(khc_open_space(NULL,\r
+                                     wconfig,\r
+                                     KHM_PERM_READ,\r
+                                     &csp_mw))) {\r
+        khm_int32 t;\r
+\r
+        if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"XPos", &t)))\r
+            x = t;\r
+        if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"YPos", &t)))\r
+            y = t;\r
+        if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"Width", &t)))\r
+            width = t;\r
+        if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"Height", &t)))\r
+            height = t;\r
+        if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"Dock", &t)))\r
+            dock = t;\r
+\r
+        khc_close_space(csp_mw);\r
+    }\r
+\r
+    /* If there were no default values, we default to using 1/4 of the\r
+       work area centered on the primary monitor.  If there were any\r
+       docking hints, then the next call to\r
+       khm_adjust_window_dimensions_for_display() will reposition the\r
+       window. */\r
+    if (width == CW_USEDEFAULT || x == CW_USEDEFAULT) {\r
+        RECT wr;\r
+\r
+        SetRectEmpty(&wr);\r
+        SystemParametersInfo(SPI_GETWORKAREA, 0, &wr, 0);\r
+\r
+        if (width == CW_USEDEFAULT) {\r
+            width = (wr.right - wr.left) / 2;\r
+            height = (wr.bottom - wr.top) / 2;\r
+        }\r
+\r
+        if (x == CW_USEDEFAULT) {\r
+            x = (wr.left + wr.right) / 2 - width / 2;\r
+            y = (wr.top + wr.bottom) / 2 - height / 2;\r
+        }\r
+    }\r
+\r
+    /* The saved dimensions might not actually be visible if the user\r
+       has changed the resolution of the display or if it's a multiple\r
+       monitor system where the monitor on which the Network Identity\r
+       Manager window was on previously is no longer connected.  We\r
+       have to check for that and adjust the dimensions if needed. */\r
+    SetRect(&r, x, y, x + width, y + height);\r
+    khm_adjust_window_dimensions_for_display(&r, dock);\r
+\r
+    *pr = r;\r
+}\r
+\r
+void\r
+khm_set_main_window_mode(int mode) {\r
+\r
+    RECT r;\r
+\r
+    if (mode == khm_main_wnd_mode)\r
+        return;\r
+\r
+    khui_check_action(KHUI_ACTION_LAYOUT_MINI,\r
+                      ((mode == KHM_MAIN_WND_MINI)? FALSE : TRUE));\r
+    khui_enable_action(KHUI_MENU_LAYOUT,\r
+                       ((mode == KHM_MAIN_WND_MINI)? FALSE : TRUE));\r
+    khui_enable_action(KHUI_MENU_COLUMNS,\r
+                       ((mode == KHM_MAIN_WND_MINI)? FALSE : TRUE));\r
+\r
+    khm_main_wnd_mode = mode;\r
+    if (khm_hwnd_main) {\r
+        khm_get_main_window_rect(&r);\r
+\r
+        SetWindowPos(khm_hwnd_main,\r
+                     NULL,\r
+                     r.left, r.top,\r
+                     r.right - r.left, r.bottom - r.top,\r
+                     SWP_NOACTIVATE | SWP_NOOWNERZORDER |\r
+                     SWP_NOZORDER);\r
+    }\r
+}\r
 \r
 void \r
 khm_create_main_window(void) {\r
     wchar_t buf[1024];\r
     khm_handle csp_cw = NULL;\r
-    khm_handle csp_mw = NULL;\r
-    int x,y,width,height;\r
     RECT r;\r
 \r
     LoadString(khm_hInstance, IDS_MAIN_WINDOW_TITLE, \r
@@ -951,41 +1140,19 @@ khm_create_main_window(void) {
     if (!khm_hwnd_null)\r
         return;\r
 \r
-    x = CW_USEDEFAULT;\r
-    y = CW_USEDEFAULT;\r
-    width = CW_USEDEFAULT;\r
-    height = CW_USEDEFAULT;\r
-\r
     if (KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow",\r
                                      KHM_PERM_READ,\r
                                      &csp_cw))) {\r
-        if (KHM_SUCCEEDED(khc_open_space(csp_cw,\r
-                                         L"Windows\\Main",\r
-                                         KHM_PERM_READ,\r
-                                         &csp_mw))) {\r
-            khm_int32 t;\r
-\r
-            if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"XPos", &t)))\r
-                x = t;\r
-            if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"YPos", &t)))\r
-                y = t;\r
-            if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"Width", &t)))\r
-                width = t;\r
-            if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"Height", &t)))\r
-                height = t;\r
-\r
-            khc_close_space(csp_mw);\r
+        khm_int32 t;\r
+\r
+        if (KHM_SUCCEEDED(khc_read_int32(csp_cw, L"DefaultWindowMode", &t))) {\r
+            khm_set_main_window_mode(t);\r
         }\r
+\r
         khc_close_space(csp_cw);\r
     }\r
 \r
-    /* The saved dimensions might not actually be visible if the user\r
-       has changed the resolution of the display or if it's a multiple\r
-       monitor system where the monitor on which the Network Identity\r
-       Manager window was on previously is no longer connected.  We\r
-       have to check for that and adjust the dimensions if needed. */\r
-    SetRect(&r, x, y, x + width, y + height);\r
-    khm_adjust_window_dimensions_for_display(&r);\r
+    khm_get_main_window_rect(&r);\r
 \r
     khm_hwnd_main = \r
         CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,\r
index 221bf4246d5cb01954c0de9273453aa5594ed706..263ffeba4bed6bc439158094b623b8726bbfc96e 100644 (file)
@@ -34,16 +34,23 @@ extern ATOM khm_main_window_class;
 extern HWND khm_hwnd_main;\r
 extern HWND khm_hwnd_rebar;\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
@@ -62,4 +69,13 @@ khm_main_wnd_proc(HWND hwnd,
 #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
index 8265aff75a68b2f414948259eeda302de926b2d9..72c58a0a1cde56d0c97f00ace84637121e4ba897 100644 (file)
@@ -1326,12 +1326,6 @@ nc_layout_new_cred_window(khui_nc_wnd_data * ncd) {
 \r
 #define CW_PARAM DWLP_USER\r
 \r
-static void\r
-nc_add_control_row(khui_nc_wnd_data * d, \r
-                   HWND label,\r
-                   HWND input,\r
-                   khui_control_size size);\r
-\r
 static LRESULT \r
 nc_handle_wm_create(HWND hwnd,\r
                     UINT uMsg,\r
@@ -1522,7 +1516,7 @@ nc_handle_wm_create(HWND hwnd,
 \r
     MapDialogRect(ncd->dlg_main, &r);\r
 \r
-    /* center the new creds window over the main NetIDMgr window */\r
+    /* position the new credentials dialog */\r
     width = r.right - r.left;\r
     height = r.bottom - r.top;\r
 \r
@@ -1540,7 +1534,18 @@ nc_handle_wm_create(HWND hwnd,
         height += (wr.bottom - wr.top) - (cr.bottom - cr.top);\r
     }\r
 \r
-    GetWindowRect(lpc->hwndParent, &r);\r
+    /* if the parent window is visible, we center the new credentials\r
+       dialog over the parent.  Otherwise, we center it on the primary\r
+       display. */\r
+\r
+    if (IsWindowVisible(lpc->hwndParent)) {\r
+        GetWindowRect(lpc->hwndParent, &r);\r
+    } else {\r
+        if(!SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID) &r, 0)) {\r
+            /* failover to the window coordinates */\r
+            GetWindowRect(lpc->hwndParent, &r);\r
+        }\r
+    }\r
     x = (r.right + r.left)/2 - width / 2;\r
     y = (r.top + r.bottom)/2 - height / 2;\r
 \r
@@ -2042,9 +2047,12 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd,
                                              L"CredWindow\\Windows\\NewCred\\ForceToTop",\r
                                              &t)) &&\r
 \r
-                t != 0 &&\r
-\r
-                !khm_is_dialog_active()) {\r
+                t != 0) {\r
+                /* it used to be that the above condition also called\r
+                   !khm_is_dialog_active() to find out whether there\r
+                   was a dialog active.  If there was, we wouldn't try\r
+                   to bring the new cred window to the foreground. But\r
+                   that was not the behavior we want. */\r
 \r
                 /* if the main window is not visible, then the SetWindowPos()\r
                    call is sufficient to bring the new creds window to the\r
@@ -2378,7 +2386,9 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd,
             if (d->nc->n_prompts > 0 &&\r
                 d->nc->prompts[0]->hwnd_edit) {\r
 \r
-                SetFocus(d->nc->prompts[0]->hwnd_edit);\r
+                PostMessage(d->dlg_main, WM_NEXTDLGCTL,\r
+                            (WPARAM) d->nc->prompts[0]->hwnd_edit,\r
+                            MAKELPARAM(TRUE, 0));\r
 \r
             }\r
 \r
index 2d020bde9909376257057e0f692bc1ae6a05d030..530c22b5654fce3c57a63e9238f7b9e07581acd8 100644 (file)
 \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
@@ -66,14 +77,23 @@ alert_show_list(alert_list * alist);
 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
@@ -345,43 +365,7 @@ notifier_wnd_proc(HWND hwnd,
                 break;\r
 \r
             case KMSG_ALERT_SHOW_QUEUED:\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
-                    check_for_queued_alerts();\r
-                }\r
+                show_queued_alerts();\r
                 break;\r
 \r
             case KMSG_ALERT_SHOW_MODAL:\r
@@ -427,57 +411,99 @@ notifier_wnd_proc(HWND hwnd,
             {\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
-                GetCursorPos(&pt);\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
-                if (khm_is_main_window_visible())\r
+                def_cmd = get_default_notifier_action();\r
+\r
+                if (khm_is_main_window_visible()) {\r
                     menu_id = KHUI_MENU_ICO_CTX_NORMAL;\r
-                else\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 WM_LBUTTONDOWN:\r
-            /* we actually wait for the WM_LBUTTONUP before doing\r
-               anything */\r
-            break;\r
-\r
-        case WM_LBUTTONUP:\r
-            /* fall through */\r
         case NIN_SELECT:\r
             /* fall through */\r
         case NIN_KEYSELECT:\r
-            khm_show_main_window();\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
-                khui_alert_lock(balloon_alert);\r
+                a = balloon_alert;\r
+                balloon_alert = NULL;\r
 \r
-                if ((balloon_alert->flags & KHUI_ALERT_FLAG_DEFACTION) &&\r
-                    (balloon_alert->flags & KHUI_ALERT_FLAG_REQUEST_BALLOON) &&\r
-                    balloon_alert->n_alert_commands > 0) {\r
+                khui_alert_lock(a);\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(balloon_alert->alert_commands[0], \r
+                                MAKEWPARAM(a->alert_commands[0], \r
                                            0),\r
                                 0);\r
-                } else if (balloon_alert->flags & \r
+                } else if (a->flags & \r
                            KHUI_ALERT_FLAG_REQUEST_WINDOW) {\r
                     khm_show_main_window();\r
-                    alert_show_normal(balloon_alert);\r
+                    alert_show_normal(a);\r
                 }\r
 \r
-                khui_alert_unlock(balloon_alert);\r
-                khui_alert_release(balloon_alert);\r
-                balloon_alert = NULL;\r
+                khui_alert_unlock(a);\r
+                khui_alert_release(a);\r
             } else {\r
 #ifdef DEBUG\r
                 assert(FALSE);\r
@@ -575,6 +601,12 @@ typedef struct tag_alerter_alert_data {
     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
@@ -596,9 +628,11 @@ typedef struct tag_alerter_wnd_data {
                                       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;\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
@@ -633,20 +667,14 @@ typedef struct tag_alerter_wnd_data {
 #define NTF_TITLE_WIDTH     (NTF_WIDTH - NTF_MARGIN*2)\r
 #define NTF_TITLE_HEIGHT    10\r
 \r
-#define NTF_ICON_WIDTH      20\r
-#define NTF_ICON_HEIGHT     20\r
-\r
-#define NTF_TEXT_X          (NTF_MARGIN + NTF_ICON_WIDTH + NTF_MARGIN)\r
-#define NTF_TEXT_WIDTH      ((NTF_WIDTH - NTF_MARGIN) - NTF_TEXT_X)\r
 #define NTF_TEXT_PAD        2\r
 \r
-#define NTF_BUTTON_WIDTH    ((NTF_TEXT_WIDTH - (KHUI_MAX_ALERT_COMMANDS - 1)*NTF_MARGIN) / KHUI_MAX_ALERT_COMMANDS)\r
-#define NTF_BUTTON_HEIGHT   15\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)\r
+#define ALERT_WINDOW_STYLES   (WS_DLGFRAME | WS_POPUPWINDOW | WS_CLIPCHILDREN | DS_NOIDLEMSG)\r
 \r
 /* Control ids */\r
 #define IDC_NTF_ALERTBIN 998\r
@@ -661,26 +689,51 @@ typedef struct tag_alerter_wnd_data {
    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 * adata;\r
+    alerter_alert_data * aiter;\r
+    khm_boolean exists = 0;\r
 \r
-    adata = PMALLOC(sizeof(*adata));\r
-    ZeroMemory(adata, sizeof(*adata));\r
+    khui_alert_lock(a);\r
 \r
-    adata->alert = a;\r
-    khui_alert_hold(a);\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
-    khui_alert_lock(a);\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
-    a->displayed = TRUE;\r
+    if (!exists)\r
+        a->displayed = TRUE;\r
 \r
     khui_alert_unlock(a);\r
 \r
-    QPUT(d, adata);\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
@@ -714,31 +767,33 @@ create_alerter_wnd_data(HWND hwnd, alert_list * l) {
     d->cx_wnd = DLG2SCNX(NTF_WIDTH);\r
     d->cy_max_wnd = DLG2SCNY(NTF_MAXHEIGHT);\r
 \r
-    d->s_button.cx = DLG2SCNX(NTF_BUTTON_WIDTH);\r
-    d->s_button.cy = DLG2SCNY(NTF_BUTTON_HEIGHT);\r
-\r
     d->s_margin.cx = DLG2SCNX(NTF_MARGIN);\r
     d->s_margin.cy = DLG2SCNY(NTF_MARGIN);\r
 \r
-    d->r_text.left = DLG2SCNX(NTF_TEXT_X);\r
-    d->r_text.right = DLG2SCNX(NTF_TEXT_WIDTH + NTF_TEXT_X);\r
-    d->r_text.top = 0;\r
-    d->r_text.bottom = 0;\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_icon.cx = DLG2SCNX(NTF_ICON_WIDTH);\r
-    d->s_icon.cy = DLG2SCNY(NTF_ICON_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
@@ -762,6 +817,9 @@ layout_alert(HDC hdc, alerter_wnd_data * d,
 \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
@@ -845,10 +903,19 @@ layout_alert(HDC hdc, alerter_wnd_data * d,
 \r
     if (ALERT_HAS_CMDS(adata->alert)) {\r
         khm_int32 i;\r
-        int x;\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
@@ -856,9 +923,38 @@ layout_alert(HDC hdc, alerter_wnd_data * d,
 #endif\r
 \r
         for (i=0; i < adata->alert->n_alert_commands; i++) {\r
-            SetRect(&adata->r_buttons[i], x, y, x + d->s_button.cx, y + d->s_button.cy);\r
 \r
-            x += d->s_button.cx + d->s_margin.cx;\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
@@ -871,6 +967,100 @@ layout_alert(HDC hdc, alerter_wnd_data * d,
 \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
@@ -879,6 +1069,8 @@ estimate_alerter_wnd_sizes(alerter_wnd_data * d) {
 \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
@@ -928,11 +1120,17 @@ layout_command_buttons(alerter_wnd_data * d) {
             goto done;\r
 \r
         for (i=0; i < adata->n_cmd_buttons; i++) {\r
-            if (IsRectEmpty(&adata->r_buttons[i]) ||\r
-                adata->hwnd_buttons[i] == NULL) {\r
-#ifdef DEBUG\r
-                assert(FALSE);\r
-#endif\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
@@ -962,6 +1160,7 @@ setup_alerter_window_controls(alerter_wnd_data * d) {
     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
@@ -984,7 +1183,7 @@ setup_alerter_window_controls(alerter_wnd_data * d) {
         r_alerts.bottom = d->cy_max_wnd;\r
 \r
         CopyRect(&r_client, &r_alerts);\r
-        r_client.bottom += d->s_margin.cy * 2 + d->s_button.cy;\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
@@ -1015,7 +1214,7 @@ setup_alerter_window_controls(alerter_wnd_data * d) {
     if (d->hw_bin == NULL) {\r
         d->hw_bin = CreateWindowEx(WS_EX_CONTROLPARENT,\r
                                    MAKEINTATOM(atom_alert_bin),\r
-                                   L"",\r
+                                   L"Alert Container",\r
                                    WS_CHILD | WS_CLIPCHILDREN |\r
                                    WS_VISIBLE |\r
                                    ((scrollbar)? WS_VSCROLL : 0),\r
@@ -1068,9 +1267,13 @@ setup_alerter_window_controls(alerter_wnd_data * d) {
             if (adata->has_commands) {\r
                 int i;\r
                 wchar_t caption[KHUI_MAXCCH_SHORT_DESC];\r
-                khui_action * action;\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
@@ -1079,6 +1282,16 @@ setup_alerter_window_controls(alerter_wnd_data * d) {
 \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
@@ -1093,29 +1306,14 @@ setup_alerter_window_controls(alerter_wnd_data * d) {
 \r
                         last_window = adata->hwnd_buttons[i];\r
 \r
-                        continue;\r
-                    }\r
-\r
-                    action = khui_find_action(adata->alert->alert_commands[i]);\r
+                        if (hw_focus == NULL)\r
+                            hw_focus = adata->hwnd_buttons[i];\r
 \r
-                    if (action == NULL) {\r
-#ifdef DEBUG\r
-                        assert(FALSE);\r
-#endif\r
                         continue;\r
                     }\r
 \r
-                    if (action->caption)\r
-                        StringCbCopy(caption, sizeof(caption), action->caption);\r
-                    else if (action->is_caption)\r
-                        LoadString(khm_hInstance, action->is_caption, caption,\r
-                                   ARRAYLENGTH(caption));\r
-                    else {\r
-#ifdef DEBUG\r
-                        assert(FALSE);\r
-#endif\r
-                        caption[0] = L'\0';\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
@@ -1124,7 +1322,7 @@ setup_alerter_window_controls(alerter_wnd_data * d) {
                         CreateWindowEx(0,\r
                                        L"BUTTON",\r
                                        caption,\r
-                                       WS_CHILD | WS_TABSTOP,\r
+                                       WS_CHILD | WS_TABSTOP | BS_NOTIFY,\r
                                        r.left, r.top,\r
                                        r.right - r.left,\r
                                        r.bottom - r.top,\r
@@ -1135,18 +1333,71 @@ setup_alerter_window_controls(alerter_wnd_data * d) {
 #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, 0),\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 (hw_focus == NULL)\r
+                    hw_focus = adata->hwnd_marker;\r
             }\r
 \r
             y += adata->r_alert.bottom;\r
             adata = QNEXT(adata);\r
+            idx++;\r
         }\r
 \r
         d->n_cmd_buttons = n_buttons;\r
@@ -1154,29 +1405,14 @@ setup_alerter_window_controls(alerter_wnd_data * d) {
 \r
     if (close_button) {\r
         if (d->hw_close == NULL) {\r
-            khui_action * close_action;\r
             wchar_t caption[256];\r
 \r
-            close_action = khui_find_action(KHUI_PACTION_CLOSE);\r
-#ifdef DEBUG\r
-            assert(close_action);\r
-#endif\r
-            if (close_action->caption)\r
-                StringCbCopy(caption, sizeof(caption), close_action->caption);\r
-            else if (close_action->is_caption)\r
-                LoadString(khm_hInstance, close_action->is_caption, caption,\r
-                           ARRAYLENGTH(caption));\r
-            else {\r
-#ifdef DEBUG\r
-                assert(FALSE);\r
-#endif\r
-                caption[0] = L'\0';\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,\r
+                                         WS_CHILD | BS_DEFPUSHBUTTON | WS_TABSTOP | BS_NOTIFY,\r
                                          0,0,100,100,\r
                                          d->hwnd,\r
                                          (HMENU) IDC_NTF_CLOSE,\r
@@ -1204,6 +1440,15 @@ setup_alerter_window_controls(alerter_wnd_data * d) {
                          SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER |\r
                          SWP_SHOWWINDOW);\r
         }\r
+\r
+        if (hw_focus == NULL)\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
@@ -1230,6 +1475,242 @@ setup_alerter_window_controls(alerter_wnd_data * d) {
                      SWP_SHOWWINDOW | SWP_NOOWNERZORDER);\r
 \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
+    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
@@ -1245,8 +1726,6 @@ destroy_alerter_wnd_data(alerter_wnd_data * d) {
 \r
             khui_alert_lock(adata->alert);\r
 \r
-            adata->alert->flags &= ~KHUI_ALERT_FLAG_DISPLAY_WINDOW;\r
-\r
             adata->alert->displayed = FALSE;\r
 \r
             khui_alert_unlock(adata->alert);\r
@@ -1320,6 +1799,32 @@ alert_can_consolidate(khui_alert * ref,
         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
@@ -1356,10 +1861,10 @@ alert_consolidate(alert_list * alist,
 \r
         if (n_added == 0 && add_from_queue) {\r
             khui_alert * q;\r
-            int s, i;\r
+            int i;\r
 \r
-            s = alert_queue_get_size();\r
-            for (i=0; i < s && n_added == 0; i++) {\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
@@ -1414,7 +1919,12 @@ alert_consolidate(alert_list * alist,
                 alert_queue_delete_alert(a);\r
                 alert_list_add_alert(alist, a);\r
                 n_added ++;\r
-                queue_size = alert_queue_get_size();\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
@@ -1657,6 +2167,49 @@ alert_show(khui_alert * a) {
         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
+        check_for_queued_alerts();\r
+    }\r
+}\r
+\r
+\r
 static void\r
 check_for_queued_alerts(void) {\r
     if (!is_alert_queue_empty()) {\r
@@ -1684,6 +2237,12 @@ check_for_queued_alerts(void) {
             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
@@ -1763,29 +2322,6 @@ alerter_wnd_proc(HWND hwnd,
         }\r
         break;\r
 \r
-#if 0\r
-    case WM_PAINT:\r
-        {\r
-            RECT r_update;\r
-            PAINTSTRUCT ps;\r
-            HDC hdc;\r
-            alerter_wnd_data * d;\r
-\r
-            if(!GetUpdateRect(hwnd, &r_update, TRUE))\r
-                return FALSE;\r
-\r
-            d = (alerter_wnd_data *)(LONG_PTR)\r
-                GetWindowLongPtr(hwnd, NTF_PARAM);\r
-\r
-            hdc = BeginPaint(hwnd, &ps);\r
-\r
-            EndPaint(hwnd, &ps);\r
-\r
-            return FALSE;\r
-        }\r
-        break; /* not reached */\r
-#endif\r
-\r
     case WM_COMMAND:\r
         {\r
             alerter_wnd_data * d;\r
@@ -1801,20 +2337,25 @@ alerter_wnd_proc(HWND hwnd,
 \r
                     DestroyWindow(hwnd);\r
 \r
-#ifdef FORLATER\r
-                    if (LOWORD(wParam) == KHUI_PACTION_NEXT) {\r
-                        kmq_post_message(KMSG_ALERT, KMSG_ALERT_SHOW_QUEUED, 0, 0);\r
-                    }\r
-#endif\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
-    //return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
 }\r
 \r
 static LRESULT CALLBACK \r
@@ -1841,6 +2382,11 @@ alert_bin_wnd_proc(HWND hwnd,
         }\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
@@ -1854,6 +2400,7 @@ alert_bin_wnd_proc(HWND hwnd,
             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
@@ -1871,6 +2418,7 @@ alert_bin_wnd_proc(HWND hwnd,
             assert(d->hfont);\r
 #endif\r
 \r
+#ifdef ALERT_STATIC_BACKGROUND\r
             if (in_printclient || ps.fErase) {\r
                 HBRUSH hb_background;\r
 \r
@@ -1879,29 +2427,78 @@ alert_bin_wnd_proc(HWND hwnd,
                 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
-\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 the alert has a title and it's different from\r
-                   the original caption for the alert dialog, we have\r
-                   to display the title. */\r
-                if (a->title &&\r
-                    wcscmp(a->title, d->caption)) {\r
+                if (!IsRectEmpty(&adata->r_title)) {\r
 \r
                     CopyRect(&r, &adata->r_title);\r
                     OffsetRect(&r, 0, y);\r
@@ -1980,6 +2577,7 @@ alert_bin_wnd_proc(HWND hwnd,
                 khui_alert_unlock(a);\r
 \r
                 y += adata->r_alert.bottom;\r
+                idx++;\r
 \r
                 adata = QNEXT(adata);\r
             }\r
@@ -1994,16 +2592,108 @@ alert_bin_wnd_proc(HWND hwnd,
 \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_DESTROY:\r
         {\r
+            /* nothing needs to be done here */\r
         }\r
         return 0;\r
     }\r
@@ -2090,11 +2780,11 @@ void khm_notify_icon_add(void) {
 \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
-    DestroyIcon(ni.hIcon);\r
 }\r
 \r
 void \r
@@ -2222,6 +2912,76 @@ void khm_notify_icon_remove(void) {
     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 (!is_alert_queue_empty() && !ALERT_DISPLAYED()) {\r
+\r
+        khm_show_main_window();\r
+        show_queued_alerts();\r
+\r
+    } else if (balloon_alert != NULL && khui_alert_windows == NULL) {\r
+\r
+        khui_alert * a;\r
+\r
+        a = balloon_alert;\r
+        balloon_alert = NULL;\r
+\r
+        khui_alert_lock(a);\r
+        if (balloon_alert->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW) {\r
+            alert_show_normal(a);\r
+        }\r
+        khui_alert_unlock(a);\r
+        khui_alert_release(a);\r
+\r
+    } else {\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
@@ -2294,3 +3054,29 @@ void khm_exit_notifier(void)
 \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
+\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
index 140d02c449b58009a1e389bba665c053aaedfec0..a42e63f1fe2e0e95415d118ca40538295aa871bd 100644 (file)
@@ -36,6 +36,9 @@ enum khm_notif_expstate {
     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
@@ -54,4 +57,7 @@ khm_notify_icon_balloon(khm_int32 severity,
 void\r
 khm_notify_icon_expstate(enum khm_notif_expstate expseverity);\r
 \r
+void\r
+khm_notify_icon_activate(void);\r
+\r
 #endif\r
index 69d35b31602d870dc72916a24974fbf9c9aab42a..d7c9f27a60e99891bd8d6c66d874bef0b6240d9f 100644 (file)
 #define IDS_NCN_IDENT_UNKNOWN           299\r
 #define IDS_REMOTE_FAIL                 300\r
 #define IDS_REMOTE_FAIL_TITLE           301\r
+#define IDS_NOTIFICATION_ACTIONS        302\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 IDC_NC_USERNAME                 1007\r
 #define IDC_NC_PASSWORD                 1008\r
 #define IDC_NC_CREDTEXT_LABEL           1009\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 IDA_ACTIVATE_MENU               40003\r
 #define IDA_UP                          40004\r
 #define IDA_DOWN                        40005\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         1140\r
+#define _APS_NEXT_CONTROL_VALUE         1143\r
 #define _APS_NEXT_SYMED_VALUE           101\r
 #endif\r
 #endif\r
index 3adf9e9a4e1dc400f8ed7c7d66e98ef6df343083..77fa6c16117e3da465218178125e0c1d2789d9c8 100644 (file)
@@ -258,6 +258,18 @@ tmr_fire_timer(void) {
         khui_alert_create_simple(wtitle, wmsg, KHERR_WARNING, &alert);\r
         khui_alert_set_flags(alert, KHUI_ALERT_FLAG_REQUEST_BALLOON,\r
                              KHUI_ALERT_FLAG_REQUEST_BALLOON);\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
index 1cde800559d89fb44acd3b1ccc2ccfd0820cdeaa..7056fce6e67721298055f4e85479b718f1c07b6a 100644 (file)
@@ -6,6 +6,7 @@ CredWindow,KC_SPACE,0,Options for the credentials window
   AutoDetectNet,KC_INT32,1,Automatically detect network connectivity changes\r
   KeepRunning,KC_INT32,1,Keep running after closing Khimaira\r
   DefaultView,KC_STRING,ByIdentity,\r
+  DefaultViewMini,KC_STRING,CompactIdentity,\r
   ViewList,KC_STRING,"ByIdentity,ByLocation,ByType",\r
   PaddingHorizontal,KC_INT32,4,\r
   PaddingVertical,KC_INT32,2,\r
@@ -26,15 +27,23 @@ CredWindow,KC_SPACE,0,Options for the credentials window
   MinThreshold,KC_INT32,10,Min value for a threshold (0)\r
   LogToFile,KC_INT32,0,Boolean. If true logs trace events to a nidmdbg.log in the temp folder\r
   DestroyCredsOnExit,KC_INT32,0,Boolean.  If true; all credentials will be destroyed when NetIDMgr exits.\r
+  NotificationAction,KC_INT32,50025,Action to perform when the user clicks on the notification icon.\r
+  DefaultWindowMode,KC_INT32,1,(0-normal; 1-mini)\r
   Windows,KC_SPACE,0,Window parameters\r
     _Schema,KC_SPACE,0,Schema\r
       Width,KC_INT32,0,\r
       Height,KC_INT32,0,\r
       XPos,KC_INT32,0,\r
       YPos,KC_INT32,0,\r
+      Dock,KC_INT32,0,Dock on window corner (0-none; 1-top left; 2-top right; 3-bottom right; 4-bottom left)\r
     _Schema,KC_ENDSPACE,0,\r
     Main,KC_SPACE,0,Main window\r
     Main,KC_ENDSPACE,0,\r
+    MainMini,KC_SPACE,0,Main window (mini mode)\r
+      Width,KC_INT32,470,\r
+      Height,KC_INT32,500,\r
+      Dock,KC_INT32,3,\r
+    MainMini,KC_ENDSPACE,0,\r
     NewCred,KC_SPACE,0,New credentials window\r
       ForceToTop,KC_INT32,1,Force new creds window to the top\r
       AnimateSizeChanges,KC_INT32,1,Animate the new creds window when the size needs changing.\r
@@ -169,6 +178,19 @@ CredWindow,KC_SPACE,0,Options for the credentials window
       TimeLeft,KC_ENDSPACE,0\r
     Columns,KC_ENDSPACE,0\r
    ByLocation,KC_ENDSPACE,0\r
+   CompactIdentity,KC_SPACE,0,Default Compact View by Identity\r
+     Description,KC_STRING,Compact view of identities\r
+     ColumnList,KC_STRING,"IdentityName",\r
+     ExpandedIdentity,KC_INT32,1,Use expanded display of identity headers\r
+     NoHeader,KC_INT32,1,Suppress the column header\r
+     Columns,KC_SPACE,0,\r
+       IdentityName,KC_SPACE,0,\r
+         Width,KC_INT32,415,\r
+         SortIndex,KC_INT32,0,\r
+         Flags,KC_INT32,11\r
+       IdentityName,KC_ENDSPACE,0,\r
+     Columns,KC_ENDSPACE,0,\r
+   CompactIdentity,KC_ENDSPACE,0\r
   Views,KC_ENDSPACE,0\r
   Notices,KC_SPACE,0,Notices and alerts\r
     MinimizeWarning,KC_INT32,1,Show the minimize warning?\r
index f95e89bc82df464af0af8ee861d744bf12fb2dd1..2984eba61252ee085f836857de4d5ab91bef5a3d 100644 (file)
@@ -22,4 +22,5 @@ KHUI_ACTION_NEW_CRED,FCONTROL|FVIRTKEY,\'N\',KHUI_ACCEL_SCOPE_GLOBAL
 KHUI_ACTION_RENEW_CRED,FCONTROL|FVIRTKEY,\'R\',KHUI_ACCEL_SCOPE_GLOBAL\r
 KHUI_ACTION_IMPORT,FCONTROL|FVIRTKEY,\'I\',KHUI_ACCEL_SCOPE_GLOBAL\r
 KHUI_ACTION_DESTROY_CRED,FCONTROL|FVIRTKEY,\'D\',KHUI_ACCEL_SCOPE_GLOBAL\r
+KHUI_ACTION_LAYOUT_MINI,FVIRTKEY,VK_F7,KHUI_ACCEL_SCOPE_GLOBAL\r
 KHUI_PACTION_SELALL,FCONTROL|FVIRTKEY,\'A\',KHUI_ACCEL_SCOPE_GLOBAL\r
index 7c42889de9a801e00dd6d553ba8397c919692042..8ec98f8cde077d52cf683fcdc2aef75096896d56 100644 (file)
@@ -80,6 +80,7 @@ khui_action_ref khui_menu_toolbars[] = {
 };\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
@@ -288,7 +289,8 @@ khui_action_create(const wchar_t * name,
     int i;\r
     size_t s;\r
 \r
-    if (!caption ||\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
@@ -591,6 +593,7 @@ menu_const_to_allocd(khui_menu_def * d)
 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
@@ -616,12 +619,22 @@ khui_menu_insert_action(khui_menu_def * d, khm_size idx, khm_int32 action, khm_i
 \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
@@ -647,6 +660,15 @@ khui_menu_insert_paction(khui_menu_def * d, khm_size idx, khui_action * paction,
 \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
@@ -1065,6 +1087,14 @@ khui_get_cmd_accel_string(khm_int32 cmd,
             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
index 88ac4405236b29f6444b3fcfd5728c73aa5596c6..36a43dfdd991399d9abea76a77b22f0660eb2fe6 100644 (file)
@@ -21,6 +21,7 @@ KHUI_ACTION_LAYOUT_ID,KHUI_ACTIONTYPE_TRIGGER | KHUI_ACTIONTYPE_TOGGLE,,0,0,0,0,
 KHUI_ACTION_LAYOUT_TYPE,KHUI_ACTIONTYPE_TRIGGER | KHUI_ACTIONTYPE_TOGGLE,,0,0,0,0,0,IDS_ACTION_LAYOUT_TYPE,0,0,0\r
 KHUI_ACTION_LAYOUT_LOC,KHUI_ACTIONTYPE_TRIGGER | KHUI_ACTIONTYPE_TOGGLE,,0,0,0,0,0,IDS_ACTION_LAYOUT_LOC,0,0,0\r
 KHUI_ACTION_LAYOUT_CUST,KHUI_ACTIONTYPE_TRIGGER | KHUI_ACTIONTYPE_TOGGLE,,0,0,0,0,0,IDS_ACTION_LAYOUT_CUST,0,0,0\r
+KHUI_ACTION_LAYOUT_MINI,KHUI_ACTIONTYPE_TRIGGER | KHUI_ACTIONTYPE_TOGGLE,,0,0,0,0,0,IDS_ACTION_LAYOUT_MINI,0,0,0\r
 KHUI_ACTION_TB_STANDARD,KHUI_ACTIONTYPE_TRIGGER | KHUI_ACTIONTYPE_TOGGLE,,0,0,0,0,0,IDS_ACTION_TB_STANDARD,0,0,KHUI_ACTIONSTATE_CHECKED|KHUI_ACTIONSTATE_DISABLED\r
 KHUI_ACTION_DEBUG_WINDOW,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_ACTION_DEBUG_WINDOW,0,IDH_ACTION_DEBUG_WINDOW,KHUI_ACTIONSTATE_DISABLED\r
 KHUI_ACTION_VIEW_REFRESH,KHUI_ACTIONTYPE_TRIGGER,,IDB_VW_REFRESH,0,0,IDB_VW_REFRESH_SM,0,IDS_ACTION_VIEW_REFRESH,IDS_ACTIONT_VIEW_REFRESH,IDH_ACTION_VIEW_REFRESH,0\r
index b36ea098ce98fa530aed88ccc7810ecb50ca53d2..92fa7b0fa692e15f67a75bafa924b1c39f5ac98e 100644 (file)
@@ -165,6 +165,11 @@ typedef struct tag_khui_action_ref {
     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
@@ -386,11 +391,10 @@ khui_menu_delete(khui_menu_def * d);
         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.\r
-\r
-    \note The ::khui_menu_def structure is not thread safe.  Multiple\r
-        threads modifying the same ::khui_menu_def structure may cause\r
-        thread safety issues.\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
@@ -423,11 +427,10 @@ khui_menu_insert_action(khui_menu_def * d, khm_size idx, khm_int32 cmd, khm_int3
         ::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.\r
-\r
-    \note The ::khui_menu_def structure is not thread safe.  Multiple\r
-        threads modifying the same ::khui_menu_def structure may cause\r
-        thread safety issues.\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
@@ -463,9 +466,6 @@ khui_menu_get_size(khui_menu_def * d);
     If the specified index is out of bounds, then the function returns\r
     NULL.\r
 \r
-    \note The ::khui_menu_def structure is not thread safe.  Multiple\r
-        threads modifying the same ::khui_menu_def structure may cause\r
-        thread safety issues.\r
  */\r
 KHMEXP khui_action_ref *\r
 khui_menu_get_action(khui_menu_def * d, khm_size idx);\r
index 31866ba2751b5bc00111927dc0505749286b19f0..87e2355f1e0aec73eae54005b32db3e470733123 100644 (file)
@@ -69,6 +69,7 @@
 #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
index 2bbc7d0da1f8206a41877c79684378ac2b15faf9..7f0e624b4494e48b076b35b92b6ccd15701aeb4b 100644 (file)
@@ -85,11 +85,19 @@ enum khui_alert_flags {
       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
-    /*!< There is a valid target for the alert */\r
+    /*!< Internal. There is a valid target for the alert */\r
 \r
     KHUI_ALERT_FLAG_VALID_ERROR     =0x00020000,\r
-    /*!< There is a valid error context associated with the alert */\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
@@ -104,9 +112,9 @@ enum khui_alert_flags {
     /*!< The alert should be displayed in a balloon */\r
 \r
     KHUI_ALERT_FLAG_MODAL           =0x10000000,\r
-    /*!< Modal alert.  Do not set direclty. */\r
+    /*!< Internal. Modal alert.  Do not set direclty. */\r
 \r
-    KHUI_ALERT_FLAGMASK_RDWR        =0x0C000010,\r
+    KHUI_ALERT_FLAGMASK_RDWR        =0x0C000030,\r
     /*!< Bit mask of flags that can be set by khui_alert_set_flags() */\r
 };\r
 \r
@@ -266,8 +274,8 @@ khui_alert_get_response(khui_alert * alert);
     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 have the same effect as\r
-    choosing the first command in the action.\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
@@ -277,6 +285,32 @@ khui_alert_get_response(khui_alert * alert);
     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