From: Jeffrey Altman Date: Tue, 20 Mar 2007 20:41:52 +0000 (+0000) Subject: NIM: New Default View and miscellaneous fixes X-Git-Tag: krb5-1.7-alpha1~1248 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=25e6fa5ec31981a3ec7d732fad6e46a997d29654;p=krb5.git NIM: New Default View and miscellaneous fixes ================================ 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 --- diff --git a/src/windows/identity/apiversion.txt b/src/windows/identity/apiversion.txt index 4ff092b8d..2f3ef6df6 100644 --- a/src/windows/identity/apiversion.txt +++ b/src/windows/identity/apiversion.txt @@ -346,3 +346,8 @@ Date=(TBD) ! KMQ_RCPTTYPE_HWND ! KMQ_QUEUE_FLAG_DELETED # Macros not internal + +! khc_value_exists() +# Behavior change with respect to shadowed handles. If a value +# doesn't exist in the top level handle, then each shadowed handle +# will be tried in turn until the value is found. diff --git a/src/windows/identity/config/Makefile.w2k b/src/windows/identity/config/Makefile.w2k index ffd1fc872..a0632065c 100644 --- a/src/windows/identity/config/Makefile.w2k +++ b/src/windows/identity/config/Makefile.w2k @@ -47,7 +47,7 @@ KHIMAIRA_WIN32_CONFIG=1 # Version info NETIDMGR_VERSION_MAJOR=1 NETIDMGR_VERSION_MINOR=1 -NETIDMGR_VERSION_PATCH=10 +NETIDMGR_VERSION_PATCH=11 NETIDMGR_VERSION_AUX=0 NETIDMGR_RELEASEDESC= diff --git a/src/windows/identity/config/Makefile.w32 b/src/windows/identity/config/Makefile.w32 index d2281b6ae..3a87ba6ff 100644 --- a/src/windows/identity/config/Makefile.w32 +++ b/src/windows/identity/config/Makefile.w32 @@ -47,7 +47,7 @@ KHIMAIRA_WIN32_CONFIG=1 # Version info NETIDMGR_VERSION_MAJOR=1 NETIDMGR_VERSION_MINOR=1 -NETIDMGR_VERSION_PATCH=10 +NETIDMGR_VERSION_PATCH=11 NETIDMGR_VERSION_AUX=0 NETIDMGR_RELEASEDESC= diff --git a/src/windows/identity/include/khlist.h b/src/windows/identity/include/khlist.h index 44e0ffb68..8bf43695b 100644 --- a/src/windows/identity/include/khlist.h +++ b/src/windows/identity/include/khlist.h @@ -159,6 +159,17 @@ #define QNEXT(pe) ((pe)->prev) #define QPREV(pe) ((pe)->next) +#define QINSERT(pt, pre, pe) \ + do { \ + if ((pre) == NULL || \ + QNEXT(pre) == NULL) { QPUT(pt, pe); } \ + else { \ + (pe)->prev = (pre)->prev; \ + (pe)->next = (pre); \ + (pre)->prev->next = (pe); \ + (pre)->prev = (pe); \ + }} while(0) + /* Trees with FIFO child lists */ #define TQDCL(type) \ LDCL(type); \ @@ -167,16 +178,37 @@ #define TQINIT(pe) \ do { \ + LINIT(pe); \ QINIT(pe); \ (pe)->parent = NULL; } while(0) -#define TQADDCHILD(pt,pe) \ +#define TQPUTCHILD(pt,pe) \ do { \ QPUT((pt), (pe)); \ (pe)->parent = (pt); } while(0) +#define TQINSERT(pt, pre, pe) \ + do { \ + QINSERT(pt, pre, pe); \ + (pe)->parent = (pt); } while(0) + +#define TQGETCHILD(pt,ppe) \ + do { \ + QGET(pt, ppe); \ + if (*(ppe)) { *(ppe)->parent = NULL; } \ + } while(0) + +#define TQDELCHILD(pt, pe) \ + do { \ + QDEL(pt, pe); \ + (pe)->parent = NULL; } while(0) + #define TQFIRSTCHILD(pt) ((pt)?QTOP(pt):NULL) +#define TQNEXTCHILD(pe) QNEXT(pe) + +#define TQPREVCHILD(pe) QPREV(pe) + #define TQPARENT(pe) ((pe)?(pe)->parent:NULL) #endif diff --git a/src/windows/identity/include/khmsgtypes.h b/src/windows/identity/include/khmsgtypes.h index f1d42c217..77397f90b 100644 --- a/src/windows/identity/include/khmsgtypes.h +++ b/src/windows/identity/include/khmsgtypes.h @@ -232,13 +232,16 @@ #define KMSG_ACT_ACTIVATE 6 /*! \brief Internal */ -#define KMSG_ACT_BEGIN_CMDLINE 128 +#define KMSG_ACT_BEGIN_CMDLINE 128 /*! \brief Internal */ -#define KMSG_ACT_CONTINUE_CMDLINE 129 +#define KMSG_ACT_CONTINUE_CMDLINE 129 /*! \brief Internal */ -#define KMSG_ACT_SYNC_CFG 130 +#define KMSG_ACT_SYNC_CFG 130 + +/*! \brief Internal */ +#define KMSG_ACT_END_CMDLINE 131 /*@}*/ diff --git a/src/windows/identity/kconfig/api.c b/src/windows/identity/kconfig/api.c index d944a4ce7..60984358f 100644 --- a/src/windows/identity/kconfig/api.c +++ b/src/windows/identity/kconfig/api.c @@ -2018,26 +2018,35 @@ khc_value_exists(khm_handle conf, const wchar_t * value) { if(!khc_is_handle(conf)) return KHM_ERROR_INVALID_PARAM; - c = khc_space_from_handle(conf); + do { + c = khc_space_from_handle(conf); - if (khc_is_user_handle(conf)) - hku = khcint_space_open_key(c, KHM_PERM_READ); - if (khc_is_machine_handle(conf)) - hkm = khcint_space_open_key(c, KHM_PERM_READ | KCONF_FLAG_MACHINE); + if (khc_is_user_handle(conf)) + hku = khcint_space_open_key(c, KHM_PERM_READ); + if (khc_is_machine_handle(conf)) + hkm = khcint_space_open_key(c, KHM_PERM_READ | KCONF_FLAG_MACHINE); - if(hku && (RegQueryValueEx(hku, value, NULL, &t, NULL, NULL) == ERROR_SUCCESS)) - rv |= KCONF_FLAG_USER; - if(hkm && (RegQueryValueEx(hkm, value, NULL, &t, NULL, NULL) == ERROR_SUCCESS)) - rv |= KCONF_FLAG_MACHINE; + if(hku && (RegQueryValueEx(hku, value, NULL, &t, NULL, NULL) == ERROR_SUCCESS)) + rv |= KCONF_FLAG_USER; + if(hkm && (RegQueryValueEx(hkm, value, NULL, &t, NULL, NULL) == ERROR_SUCCESS)) + rv |= KCONF_FLAG_MACHINE; - if(c->schema && khc_is_schema_handle(conf)) { - for(i=0; inSchema; i++) { - if(!wcscmp(c->schema[i].name, value)) { - rv |= KCONF_FLAG_SCHEMA; - break; + if(c->schema && khc_is_schema_handle(conf)) { + for(i=0; inSchema; i++) { + if(!wcscmp(c->schema[i].name, value)) { + rv |= KCONF_FLAG_SCHEMA; + break; + } } } - } + + /* if the value is not found at this level and the handle is + shadowed, try the next level down. */ + if (rv == 0 && khc_is_shadowed(conf)) + conf = khc_shadow(conf); + else + break; + } while (conf); return rv; } diff --git a/src/windows/identity/kconfig/kconfig.h b/src/windows/identity/kconfig/kconfig.h index 518dc4f46..b550d1358 100644 --- a/src/windows/identity/kconfig/kconfig.h +++ b/src/windows/identity/kconfig/kconfig.h @@ -792,6 +792,12 @@ khc_get_type(khm_handle conf, const wchar_t * value_name); configuration stores that were specified when opening the configuration space corresponding to \a conf. + If the specified handle is shadowed (see khc_shadow_space()) and + the value is not found in any of the visible stores for the + topmost handle, each of the shadowed handles will be tried in turn + until the value is found. The return value will correspond to the + handle where the value is first found. + \return A combination of ::KCONF_FLAG_MACHINE, ::KCONF_FLAG_USER and ::KCONF_FLAG_SCHEMA indicating which stores contain the value. diff --git a/src/windows/identity/kcreddb/identity.c b/src/windows/identity/kcreddb/identity.c index 326d0258a..5d76384d9 100644 --- a/src/windows/identity/kcreddb/identity.c +++ b/src/windows/identity/kcreddb/identity.c @@ -231,7 +231,7 @@ kcdb_identity_create(const wchar_t *name, StringCbCopy(id->name, namesize, name); id->flags = (flags & KCDB_IDENT_FLAGMASK_RDWR); - id->flags |= KCDB_IDENT_FLAG_ACTIVE; + id->flags |= KCDB_IDENT_FLAG_ACTIVE | KCDB_IDENT_FLAG_EMPTY; LINIT(id); EnterCriticalSection(&cs_ident); diff --git a/src/windows/identity/plugins/common/dynimport.c b/src/windows/identity/plugins/common/dynimport.c index b3d764476..d891af122 100644 --- a/src/windows/identity/plugins/common/dynimport.c +++ b/src/windows/identity/plugins/common/dynimport.c @@ -144,6 +144,7 @@ DECL_FUNC_PTR(krb5_free_host_realm); DECL_FUNC_PTR(krb5_c_random_make_octets); DECL_FUNC_PTR(krb5_free_addresses); DECL_FUNC_PTR(krb5_free_default_realm); +DECL_FUNC_PTR(krb5_string_to_deltat); // Krb524 functions DECL_FUNC_PTR(krb524_init_ets); @@ -160,6 +161,7 @@ DECL_FUNC_PTR(profile_release); DECL_FUNC_PTR(profile_get_subsection_names); DECL_FUNC_PTR(profile_free_list); DECL_FUNC_PTR(profile_get_string); +DECL_FUNC_PTR(profile_get_integer); DECL_FUNC_PTR(profile_get_values); DECL_FUNC_PTR(profile_get_relation_names); DECL_FUNC_PTR(profile_clear_relation); @@ -289,6 +291,7 @@ FUNC_INFO k5_fi[] = { MAKE_FUNC_INFO(krb5_free_host_realm), MAKE_FUNC_INFO(krb5_c_random_make_octets), MAKE_FUNC_INFO(krb5_free_default_realm), + MAKE_FUNC_INFO(krb5_string_to_deltat), END_FUNC_INFO }; @@ -305,6 +308,7 @@ FUNC_INFO profile_fi[] = { MAKE_FUNC_INFO(profile_get_subsection_names), MAKE_FUNC_INFO(profile_free_list), MAKE_FUNC_INFO(profile_get_string), + MAKE_FUNC_INFO(profile_get_integer), MAKE_FUNC_INFO(profile_get_values), MAKE_FUNC_INFO(profile_get_relation_names), MAKE_FUNC_INFO(profile_clear_relation), diff --git a/src/windows/identity/plugins/common/dynimport.h b/src/windows/identity/plugins/common/dynimport.h index a9561bc86..0f2cc237d 100644 --- a/src/windows/identity/plugins/common/dynimport.h +++ b/src/windows/identity/plugins/common/dynimport.h @@ -30,7 +30,18 @@ /* Dynamic imports */ #include #include + +#if _WIN32_WINNT < 0x0501 +#define KHM_SAVE_WIN32_WINNT _WIN32_WINNT +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#endif #include +#ifdef KHM_SAVE_WIN32_WINNT +#undef _WIN32_WINNT +#define _WIN32_WINNT KHM_SAVE_WIN32_WINNT +#undef KHM_SAVE_WIN32_WINNT +#endif extern HINSTANCE hKrb4; extern HINSTANCE hKrb5; @@ -256,6 +267,7 @@ extern DECL_FUNC_PTR(krb5_get_host_realm); extern DECL_FUNC_PTR(krb5_free_host_realm); extern DECL_FUNC_PTR(krb5_c_random_make_octets); extern DECL_FUNC_PTR(krb5_free_default_realm); +extern DECL_FUNC_PTR(krb5_string_to_deltat); // Krb524 functions extern DECL_FUNC_PTR(krb524_init_ets); @@ -272,6 +284,7 @@ extern DECL_FUNC_PTR(profile_release); extern DECL_FUNC_PTR(profile_get_subsection_names); extern DECL_FUNC_PTR(profile_free_list); extern DECL_FUNC_PTR(profile_get_string); +extern DECL_FUNC_PTR(profile_get_integer); extern DECL_FUNC_PTR(profile_get_values); extern DECL_FUNC_PTR(profile_get_relation_names); extern DECL_FUNC_PTR(profile_clear_relation); diff --git a/src/windows/identity/plugins/common/krb5common.c b/src/windows/identity/plugins/common/krb5common.c index 5ba59df4e..759641ff7 100644 --- a/src/windows/identity/plugins/common/krb5common.c +++ b/src/windows/identity/plugins/common/krb5common.c @@ -164,8 +164,10 @@ khm_krb5_initialize(khm_handle ident, khm_krb5_error(rc, "krb5_cc_set_flags()", 0, ctx, cache); else if ((rc == KRB5_FCC_NOFILE || rc == KRB5_CC_NOTFOUND) && *ctx != NULL) { - if (*cache != NULL) + if (*cache != NULL) { (*pkrb5_cc_close)(*ctx, *cache); + *cache = NULL; + } } return rc; } diff --git a/src/windows/identity/plugins/krb4/krb4funcs.c b/src/windows/identity/plugins/krb4/krb4funcs.c index 306437a00..b2b5fef4e 100644 --- a/src/windows/identity/plugins/krb4/krb4funcs.c +++ b/src/windows/identity/plugins/krb4/krb4funcs.c @@ -32,7 +32,6 @@ modified and adapted for NetIDMgr */ #define SECURITY_WIN32 #include -#include #include #include diff --git a/src/windows/identity/plugins/krb4/krb4funcs.h b/src/windows/identity/plugins/krb4/krb4funcs.h index 742d13626..5ec11cc63 100644 --- a/src/windows/identity/plugins/krb4/krb4funcs.h +++ b/src/windows/identity/plugins/krb4/krb4funcs.h @@ -35,7 +35,18 @@ #include #define SECURITY_WIN32 #include -#include + +#if _WIN32_WINNT < 0x0501 +#define KHM_SAVE_WIN32_WINNT _WIN32_WINNT +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#endif +#include +#ifdef KHM_SAVE_WIN32_WINNT +#undef _WIN32_WINNT +#define _WIN32_WINNT KHM_SAVE_WIN32_WINNT +#undef KHM_SAVE_WIN32_WINNT +#endif #include diff --git a/src/windows/identity/plugins/krb4/lang/en_us/langres.rc b/src/windows/identity/plugins/krb4/lang/en_us/langres.rc index 374f6ced5..f7a849e24 100644 --- a/src/windows/identity/plugins/krb4/lang/en_us/langres.rc +++ b/src/windows/identity/plugins/krb4/lang/en_us/langres.rc @@ -95,7 +95,7 @@ EXSTYLE WS_EX_CONTROLPARENT FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL "Obtain Kerberos v4 credentials for this identity",IDC_CFG_GETTIX, - "Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,7,7,147,10 + "Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,7,7,165,10 END diff --git a/src/windows/identity/plugins/krb5/krb5configdlg.c b/src/windows/identity/plugins/krb5/krb5configdlg.c index 9c8af5dc4..2d1e3259c 100644 --- a/src/windows/identity/plugins/krb5/krb5configdlg.c +++ b/src/windows/identity/plugins/krb5/krb5configdlg.c @@ -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; } diff --git a/src/windows/identity/plugins/krb5/krb5funcs.c b/src/windows/identity/plugins/krb5/krb5funcs.c index 8cf2b86fd..bab9ba4cd 100644 --- a/src/windows/identity/plugins/krb5/krb5funcs.c +++ b/src/windows/identity/plugins/krb5/krb5funcs.c @@ -33,7 +33,6 @@ #define SECURITY_WIN32 #include -#include #include #include @@ -1004,7 +1003,6 @@ khm_krb5_renew_ident(khm_handle identity) krb5_creds my_creds; krb5_data *realm = NULL; wchar_t idname[KCDB_IDENT_MAXCCH_NAME]; - char cidname[KCDB_IDENT_MAXCCH_NAME]; khm_size cb; memset(&my_creds, 0, sizeof(krb5_creds)); @@ -1016,9 +1014,11 @@ khm_krb5_renew_ident(khm_handle identity) kcdb_identity_get_name(identity, idname, &cb); if (khm_krb5_get_identity_flags(identity) & K5IDFLAG_IMPORTED) { +#ifdef REIMPORT_MSLSA_CREDS /* we are trying to renew the identity that was imported from MSLSA: */ BOOL imported; + char cidname[KCDB_IDENT_MAXCCH_NAME]; UnicodeStrToAnsi(cidname, sizeof(cidname), idname); @@ -1029,6 +1029,11 @@ khm_krb5_renew_ident(khm_handle identity) /* if the import failed, then we try to renew the identity via the usual procedure. */ +#else + /* if we are suppressing further imports from MSLSA, we just + skip renewing this identity. */ + goto cleanup; +#endif } code = khm_krb5_initialize(identity, &ctx, &cc); @@ -2002,11 +2007,11 @@ khm_krb5_ms2mit(char * match_princ, BOOL match_realm, BOOL save_creds) wname[0] = L'\0'; - kcdb_identity_get_config(ident, 0, &idconfig); + kcdb_identity_get_config(ident, KHM_FLAG_CREATE, &idconfig); if (idconfig == NULL) goto _done_checking_config; - khc_open_space(idconfig, CSNAME_KRB5CRED, 0, &k5config); + khc_open_space(idconfig, CSNAME_KRB5CRED, KHM_FLAG_CREATE, &k5config); if (k5config == NULL) goto _done_checking_config; @@ -2612,3 +2617,487 @@ khm_krb5_get_temp_ccache(krb5_context ctx, return code; } + +/* + + The configuration information for each identity comes from a + multitude of layers organized as follows. The ordering is + decreasing in priority. When looking up a value, the value will be + looked up in each layer in turn starting at level 0. The first + instance of the value found will be the effective value. + + 0 : \Krb5Cred + + 0.1: per user + + 0.2: per machine + + 1 : \Parameters\Realms\ + + 1.1: per user + + 1.2: per machine + + 2 : \Parameters + + 2.1: per user + + 2.2: per machine + + 2.3: schema + + */ +khm_int32 +khm_krb5_get_identity_config(khm_handle ident, + khm_int32 flags, + khm_handle * ret_csp) { + + khm_int32 rv = KHM_ERROR_SUCCESS; + khm_handle csp_i = NULL; + khm_handle csp_ik5 = NULL; + khm_handle csp_realms = NULL; + khm_handle csp_realm = NULL; + khm_handle csp_plugins = NULL; + khm_handle csp_krbcfg = NULL; + khm_handle csp_rv = NULL; + wchar_t realm[KCDB_IDENT_MAXCCH_NAME]; + + realm[0] = L'\0'; + + if (ident) { + wchar_t idname[KCDB_IDENT_MAXCCH_NAME]; + wchar_t * trealm; + khm_size cb_idname = sizeof(idname); + + rv = kcdb_identity_get_name(ident, idname, &cb_idname); + if (KHM_SUCCEEDED(rv) && + (trealm = khm_get_realm_from_princ(idname)) != NULL) { + StringCbCopy(realm, sizeof(realm), trealm); + } + } + + if (ident) { + rv = kcdb_identity_get_config(ident, flags, &csp_i); + if (KHM_FAILED(rv)) + goto done; + + rv = khc_open_space(csp_i, CSNAME_KRB5CRED, flags, &csp_ik5); + if (KHM_FAILED(rv)) + goto done; + + if (realm[0] == L'\0') + goto done_shadow_realm; + + rv = khc_open_space(csp_params, CSNAME_REALMS, flags, &csp_realms); + if (KHM_FAILED(rv)) + goto done_shadow_realm; + + rv = khc_open_space(csp_realms, realm, flags, &csp_realm); + if (KHM_FAILED(rv)) + goto done_shadow_realm; + + rv = khc_shadow_space(csp_realm, csp_params); + + done_shadow_realm: + + if (csp_realm) + rv = khc_shadow_space(csp_ik5, csp_realm); + else + rv = khc_shadow_space(csp_ik5, csp_params); + + csp_rv = csp_ik5; + + } else { + + /* No valid identity specified. We default to the parameters key. */ + rv = kmm_get_plugins_config(0, &csp_plugins); + if (KHM_FAILED(rv)) + goto done; + + rv = khc_open_space(csp_plugins, CSNAME_KRB5CRED, flags, &csp_krbcfg); + if (KHM_FAILED(rv)) + goto done; + + rv = khc_open_space(csp_krbcfg, CSNAME_PARAMS, flags, &csp_rv); + } + + done: + + *ret_csp = csp_rv; + + /* leave csp_ik5. If it's non-NULL, then it's the return value */ + /* leave csp_rv. It's the return value. */ + if (csp_i) + khc_close_space(csp_i); + if (csp_realms) + khc_close_space(csp_realms); + if (csp_realm) + khc_close_space(csp_realm); + if (csp_plugins) + khc_close_space(csp_plugins); + if (csp_krbcfg) + khc_close_space(csp_krbcfg); + + return rv; +} + +khm_int32 +khm_krb5_get_identity_params(khm_handle ident, k5_params * p) { + + khm_int32 rv = KHM_ERROR_SUCCESS; + khm_handle csp_id = NULL; + khm_int32 regf = 0; + khm_int32 proff = 0; + khm_int32 e; + khm_int32 v; + CHAR confname[MAX_PATH]; + + ZeroMemory(p, sizeof(*p)); + + rv = khm_krb5_get_identity_config(ident, 0, &csp_id); + if (KHM_FAILED(rv)) + goto done_reg; + + +#define GETVAL(vname, vfield, flag) \ + do { \ + e = khc_value_exists(csp_id, vname); \ + rv = khc_read_int32(csp_id, vname, &v); \ + if (KHM_FAILED(rv)) goto done_reg; \ + p->vfield = v; \ + if ((e & ~KCONF_FLAG_SCHEMA) != 0) regf |= flag; \ + } while(FALSE) + + /* Flags */ + GETVAL(L"Renewable", renewable, K5PARAM_F_RENEW); + GETVAL(L"Forwardable", forwardable, K5PARAM_F_FORW); + GETVAL(L"Proxiable", proxiable, K5PARAM_F_PROX); + GETVAL(L"Addressless", addressless, K5PARAM_F_ADDL); + GETVAL(L"PublicIP", publicIP, K5PARAM_F_PUBIP); + + /* Lifetime */ + GETVAL(L"DefaultLifetime", lifetime, K5PARAM_F_LIFE); + GETVAL(L"MaxLifetime", lifetime_max, K5PARAM_F_LIFE_H); + GETVAL(L"MinLifetime", lifetime_min, K5PARAM_F_LIFE_L); + + /* Renewable lifetime */ + GETVAL(L"DefaultRenewLifetime", renew_life, K5PARAM_F_RLIFE); + GETVAL(L"MaxRenewLifetime", renew_life_max, K5PARAM_F_RLIFE_H); + GETVAL(L"MinRenewLifetime", renew_life_min, K5PARAM_F_RLIFE_L); + +#undef GETVAL + + done_reg: + + if (csp_id) + khc_close_space(csp_id); + + /* if all the parameters were read from the registry, then we have + no reason to read from the profile file. */ + if (regf == K5PARAM_FM_ALL) { + p->source_reg = regf; + return KHM_ERROR_SUCCESS; + } + + if (rv) + return rv; + + /* If we get here, then some of the settings we read from the + configuration actually came from the schema. In other words, + the values weren't really defined for this identity. So we now + have to read the values from the krb5 configuration file. */ + + if (!khm_krb5_get_profile_file(confname, sizeof(confname))) { + profile_t profile; + const char * filenames[2]; + long retval; + + filenames[0] = confname; + filenames[1] = NULL; + + if (!pprofile_init(filenames, &profile)) { + + /* default ticket lifetime */ + if (!(regf & K5PARAM_F_LIFE)) { + char * value = NULL; + retval = pprofile_get_string(profile, "libdefaults", "ticket_lifetime", 0, 0, &value); + if (retval == 0 && value) { + krb5_deltat d; + + retval = pkrb5_string_to_deltat(value, &d); + if (retval == KRB5_DELTAT_BADFORMAT) { + /* Historically some sites use relations of + the form 'ticket_lifetime = 24000' where + the unit is left out but is assumed to be + seconds. Then there are other sites which + use the form 'ticket_lifetime = 600' where + the unit is assumed to be minutes. While + these are technically wrong (a unit needs + to be specified), we try to accomodate for + this using the safe assumption that the + unit is seconds and tack an 's' to the end + and see if that works. */ + + size_t cch; + char tmpbuf[256]; + char * buf; + + do { + if (FAILED(StringCchLengthA(value, 1024 /* unresonably large size */, + &cch))) + break; + + cch += sizeof(char) * 2; /* NULL and new 's' */ + if (cch > ARRAYLENGTH(tmpbuf)) + buf = PMALLOC(cch * sizeof(char)); + else + buf = tmpbuf; + + StringCchCopyA(buf, cch, value); + StringCchCatA(buf, cch, "s"); + + retval = pkrb5_string_to_deltat(buf, &d); + if (retval == 0) { + p->lifetime = d; + proff |= K5PARAM_F_LIFE; + } + + if (buf != tmpbuf) + PFREE(buf); + + } while(0); + + } else if (retval == 0) { + p->lifetime = d; + proff |= K5PARAM_F_LIFE; + } + pprofile_release_string(value); + } + } + + if (!(regf & K5PARAM_F_RLIFE)) { + char * value = NULL; + retval = pprofile_get_string(profile, "libdefaults", "renew_lifetime", 0, 0, &value); + if (retval == 0 && value) { + krb5_deltat d; + + retval = pkrb5_string_to_deltat(value, &d); + if (retval == 0) { + p->renew_life = d; + proff |= K5PARAM_F_RLIFE; + } + pprofile_release_string(value); + } + } + + if (!(regf & K5PARAM_F_FORW)) { + char * value = NULL; + retval = pprofile_get_string(profile, "libdefaults", "forwardable", 0, 0, &value); + if (retval == 0 && value) { + khm_boolean b; + + if (!khm_krb5_parse_boolean(value, &b)) + p->forwardable = b; + else + p->forwardable = FALSE; + pprofile_release_string(value); + proff |= K5PARAM_F_FORW; + } + } + + if (!(regf & K5PARAM_F_RENEW)) { + char * value = NULL; + retval = pprofile_get_string(profile, "libdefaults", "renewable", 0, 0, &value); + + if (retval == 0 && value) { + khm_boolean b; + + if (!khm_krb5_parse_boolean(value, &b)) + p->renewable = b; + else + p->renewable = TRUE; + pprofile_release_string(value); + proff |= K5PARAM_F_RENEW; + } + } + + if (!(regf & K5PARAM_F_ADDL)) { + char * value = NULL; + retval = pprofile_get_string(profile, "libdefaults", "noaddresses", 0, 0, &value); + + if (retval == 0 && value) { + khm_boolean b; + + if (!khm_krb5_parse_boolean(value, &b)) + p->addressless = b; + else + p->addressless = TRUE; + pprofile_release_string(value); + proff |= K5PARAM_F_ADDL; + } + } + + if (!(regf & K5PARAM_F_PROX)) { + char * value = NULL; + retval = pprofile_get_string(profile, "libdefaults", "proxiable", 0, 0, &value); + + if (retval == 0 && value) { + khm_boolean b; + + if (!khm_krb5_parse_boolean(value, &b)) + p->proxiable = b; + else + p->proxiable = FALSE; + pprofile_release_string(value); + proff |= K5PARAM_F_PROX; + } + } + + pprofile_release(profile); + } + } + + p->source_reg = regf; + p->source_prof = proff; + + return rv; +} + +/* Note that p->source_reg and p->source_prof is used in special ways + here. All fields that are flagged in source_reg will be written to + the configuration (if they are different from what + khm_krb5_get_identity_params() reports). All fields that are + flagged in source_prof will be removed from the configuration + (thereby exposing the value defined in the profile file). */ +khm_int32 +khm_krb5_set_identity_params(khm_handle ident, const k5_params * p) { + khm_int32 rv = KHM_ERROR_SUCCESS; + khm_handle csp_id = NULL; + k5_params p_s; + khm_int32 source_reg = p->source_reg; + khm_int32 source_prof = p->source_prof; + + rv = khm_krb5_get_identity_config(ident, + KHM_PERM_WRITE | KHM_FLAG_CREATE | + KCONF_FLAG_WRITEIFMOD, + &csp_id); + if (KHM_FAILED(rv)) + goto done_reg; + + khm_krb5_get_identity_params(ident, &p_s); + + /* Remove any bits that don't make sense. Not all values can be + specified in the profile file. */ + source_prof &= K5PARAM_FM_PROF; + + /* if a flag appears in both source_prof and source_reg, remove + the flag from source_reg. */ + source_reg &= ~source_prof; + + /* we only write values that have changed, and that are flagged in + source_reg */ + + if ((source_reg & K5PARAM_F_RENEW) && + !!p_s.renewable != !!p->renewable) + khc_write_int32(csp_id, L"Renewable", !!p->renewable); + + if ((source_reg & K5PARAM_F_FORW) && + !!p_s.forwardable != !!p->forwardable) + khc_write_int32(csp_id, L"Forwardable", !!p->forwardable); + + if ((source_reg & K5PARAM_F_PROX) && + !!p_s.proxiable != !!p->proxiable) + khc_write_int32(csp_id, L"Proxiable", !!p->proxiable); + + if ((source_reg & K5PARAM_F_ADDL) && + !!p_s.addressless != !!p->addressless) + khc_write_int32(csp_id, L"Addressless", !!p->addressless); + + if ((source_reg & K5PARAM_F_PUBIP) && + p_s.publicIP != p->publicIP) + khc_write_int32(csp_id, L"PublicIP", p->publicIP); + + if ((source_reg & K5PARAM_F_LIFE) && + p_s.lifetime != p->lifetime) + khc_write_int32(csp_id, L"DefaultLifetime", p->lifetime); + + if ((source_reg & K5PARAM_F_LIFE_H) && + p_s.lifetime_max != p->lifetime_max) + khc_write_int32(csp_id, L"MaxLifetime", p->lifetime_max); + + if ((source_reg & K5PARAM_F_LIFE_L) && + p_s.lifetime_min != p->lifetime_min) + khc_write_int32(csp_id, L"MinLifetime", p->lifetime_min); + + if ((source_reg & K5PARAM_F_RLIFE) && + p_s.renew_life != p->renew_life) + khc_write_int32(csp_id, L"DefaultRenewLifetime", p->renew_life); + + if ((source_reg & K5PARAM_F_RLIFE_H) && + p_s.renew_life_max != p->renew_life_max) + khc_write_int32(csp_id, L"MaxRenewLifetime", p->renew_life_max); + + if ((source_reg & K5PARAM_F_RLIFE_L) && + p_s.renew_life_min != p->renew_life_min) + khc_write_int32(csp_id, L"MinRenewLifetime", p->renew_life_min); + + /* and now, remove the values that are present in source_prof. + Not all values are removed since not all values can be + specified in the profile file. */ + if (source_prof & K5PARAM_F_RENEW) + khc_remove_value(csp_id, L"Renewable", 0); + + if (source_prof & K5PARAM_F_FORW) + khc_remove_value(csp_id, L"Forwardable", 0); + + if (source_prof & K5PARAM_F_PROX) + khc_remove_value(csp_id, L"Proxiable", 0); + + if (source_prof & K5PARAM_F_ADDL) + khc_remove_value(csp_id, L"Addressless", 0); + + if (source_prof & K5PARAM_F_LIFE) + khc_remove_value(csp_id, L"DefaultLifetime", 0); + + if (source_prof & K5PARAM_F_RLIFE) + khc_remove_value(csp_id, L"DefaultRenewLifetime", 0); + + done_reg: + if (csp_id != NULL) + khc_close_space(csp_id); + + return rv; +} + +static const char *const conf_yes[] = { + "y", "yes", "true", "t", "1", "on", + 0, +}; + +static const char *const conf_no[] = { + "n", "no", "false", "nil", "0", "off", + 0, +}; + +int +khm_krb5_parse_boolean(const char *s, khm_boolean * b) +{ + const char *const *p; + + for(p=conf_yes; *p; p++) { + if (!_stricmp(*p,s)) { + *b = TRUE; + return 0; + } + } + + for(p=conf_no; *p; p++) { + if (!_stricmp(*p,s)) { + *b = FALSE; + return 0; + } + } + + /* Default to "no" */ + return KHM_ERROR_INVALID_PARAM; +} diff --git a/src/windows/identity/plugins/krb5/krb5funcs.h b/src/windows/identity/plugins/krb5/krb5funcs.h index ece4f7908..ce3989682 100644 --- a/src/windows/identity/plugins/krb5/krb5funcs.h +++ b/src/windows/identity/plugins/krb5/krb5funcs.h @@ -35,7 +35,18 @@ #include #define SECURITY_WIN32 #include -#include + +#if _WIN32_WINNT < 0x0501 +#define KHM_SAVE_WIN32_WINNT _WIN32_WINNT +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#endif +#include +#ifdef KHM_SAVE_WIN32_WINNT +#undef _WIN32_WINNT +#define _WIN32_WINNT KHM_SAVE_WIN32_WINNT +#undef KHM_SAVE_WIN32_WINNT +#endif #include @@ -50,7 +61,49 @@ #define KRB5_MAXCCH_CCNAME 1024 -// Function Prototypes. +#define KRB5_CONF_YES "yes" +#define KRB5_CONF_NO "no" + +typedef struct tag_k5params { + + khm_int32 source_reg; /* flags indicating which fields were + retrieved using the registry */ + khm_int32 source_prof; /* flags indicating which fields were + retrieved using krb5.ini */ + + khm_boolean renewable; + khm_boolean forwardable; + khm_boolean proxiable; + khm_boolean addressless; + + khm_ui_4 publicIP; + + krb5_deltat lifetime; + krb5_deltat lifetime_min; + krb5_deltat lifetime_max; + + krb5_deltat renew_life; + krb5_deltat renew_life_min; + krb5_deltat renew_life_max; + +} k5_params; + +#define K5PARAM_F_RENEW 0x00000001 +#define K5PARAM_F_FORW 0x00000002 +#define K5PARAM_F_PROX 0x00000004 +#define K5PARAM_F_ADDL 0x00000008 +#define K5PARAM_F_PUBIP 0x00000010 +#define K5PARAM_F_LIFE 0x00000020 +#define K5PARAM_F_RLIFE 0x00000040 +#define K5PARAM_F_LIFE_L 0x00000080 +#define K5PARAM_F_LIFE_H 0x00000100 +#define K5PARAM_F_RLIFE_L 0x00000200 +#define K5PARAM_F_RLIFE_H 0x00000400 + +#define K5PARAM_FM_ALL 0x000007ff +#define K5PARAM_FM_PROF 0x0000007f + +/* Credential and principal operations */ BOOL khm_krb5_ms2mit(char * match_princ, @@ -92,36 +145,23 @@ khm_krb5_renew_cred(khm_handle cred); int khm_krb5_renew_ident(khm_handle identity); -wchar_t * -khm_krb5_get_default_realm(void); - -long -khm_krb5_set_default_realm(wchar_t * realm); - -wchar_t * -khm_krb5_get_realm_list(void); - long khm_krb5_list_tickets(krb5_context *krbv5Context); -long -khm_krb4_list_tickets(void); - -wchar_t * -khm_get_realm_from_princ(wchar_t * princ); - long khm_krb5_copy_ccache_by_name(krb5_context in_ctx, wchar_t * wscc_dest, wchar_t * wscc_src); long -khm_krb5_canon_cc_name(wchar_t * wcc_name, - size_t cb_cc_name); +khm_krb5_get_temp_ccache(krb5_context ctx, + krb5_ccache * cc); -int -khm_krb5_cc_name_cmp(const wchar_t * cc_name_1, - const wchar_t * cc_name_2); +khm_int32 KHMAPI +khm_krb5_creds_is_equal(khm_handle vcred1, khm_handle vcred2, void * dummy); + + +/* Configuration */ BOOL khm_krb5_get_profile_file(LPSTR confname, UINT szConfname); @@ -129,8 +169,19 @@ khm_krb5_get_profile_file(LPSTR confname, UINT szConfname); BOOL khm_krb5_get_temp_profile_file(LPSTR confname, UINT szConfname); -khm_int32 KHMAPI -khm_krb5_creds_is_equal(khm_handle vcred1, khm_handle vcred2, void * dummy); +wchar_t * +khm_krb5_get_default_realm(void); + +long +khm_krb5_set_default_realm(wchar_t * realm); + +wchar_t * +khm_krb5_get_realm_list(void); + +khm_int32 +khm_krb5_get_identity_config(khm_handle ident, + khm_int32 flags, + khm_handle * ret_csp); void khm_krb5_set_identity_flags(khm_handle identity, @@ -140,7 +191,26 @@ khm_krb5_set_identity_flags(khm_handle identity, khm_int32 khm_krb5_get_identity_flags(khm_handle identity); +khm_int32 +khm_krb5_set_identity_params(khm_handle ident, const k5_params * p); + +khm_int32 +khm_krb5_get_identity_params(khm_handle ident, k5_params * p); + +/* Utility */ + +wchar_t * +khm_get_realm_from_princ(wchar_t * princ); + long -khm_krb5_get_temp_ccache(krb5_context ctx, - krb5_ccache * cc); +khm_krb5_canon_cc_name(wchar_t * wcc_name, + size_t cb_cc_name); + +int +khm_krb5_cc_name_cmp(const wchar_t * cc_name_1, + const wchar_t * cc_name_2); + +int +khm_krb5_parse_boolean(const char *s, khm_boolean * b); + #endif diff --git a/src/windows/identity/plugins/krb5/krb5newcreds.c b/src/windows/identity/plugins/krb5/krb5newcreds.c index 34cc8bd20..d52e934cc 100644 --- a/src/windows/identity/plugins/krb5/krb5newcreds.c +++ b/src/windows/identity/plugins/krb5/krb5newcreds.c @@ -635,19 +635,41 @@ k5_kinit_fiber_proc(PVOID lpParameter) _reportf(L" g_fjob.valid_principal = %d", (int) g_fjob.valid_principal); #endif + /* If we don't know if we have a valid principal, we + restrict the options that are set when we call kinit. + This way we will be able to use the response from the + KDC to verify the principal. */ + + g_fjob.retry_if_valid_principal = (g_fjob.forwardable || + g_fjob.proxiable || + g_fjob.renewable); + + retry_kinit: g_fjob.code = khm_krb5_kinit(0, g_fjob.principal, g_fjob.password, g_fjob.ccache, g_fjob.lifetime, - g_fjob.valid_principal ? g_fjob.forwardable : 0, - g_fjob.valid_principal ? g_fjob.proxiable : 0, + g_fjob.valid_principal ? g_fjob.forwardable : 0, + g_fjob.valid_principal ? g_fjob.proxiable : 0, (g_fjob.valid_principal && g_fjob.renewable ? g_fjob.renew_life : 0), g_fjob.addressless, g_fjob.publicIP, k5_kinit_prompter, &g_fjob); + + /* If the principal was found to be valid, and if we + restricted the options that were being passed to kinit, + then we need to retry the kinit call. This time we use + the real options. */ + if (g_fjob.state == FIBER_STATE_RETRY_KINIT) { +#ifdef DEBUG + assert(g_fjob.valid_principal); +#endif + g_fjob.state = FIBER_STATE_KINIT; + goto retry_kinit; + } } _switch_to_main: @@ -946,7 +968,20 @@ k5_kinit_prompter(krb5_context context, #endif /* we got prompts? Then we assume that the principal is valid */ - g_fjob.valid_principal = TRUE; + + if (!g_fjob.valid_principal) { + g_fjob.valid_principal = TRUE; + + /* if the flags that were used to call kinit were restricted + because we didn't know the validity of the principal, then + we need to go back and retry the call with the correct + flags. */ + if (g_fjob.retry_if_valid_principal) { + _reportf(L"Retrying kinit call due to restricted flags on first call."); + g_fjob.state = FIBER_STATE_RETRY_KINIT; + return KRB5_LIBOS_PWDINTR; + } + } nc = g_fjob.nc; @@ -1198,7 +1233,7 @@ k5_kinit_prompter(krb5_context context, actual acquisition of credentials. */ if(g_fjob.command != FIBER_CMD_CONTINUE && g_fjob.command != FIBER_CMD_KINIT) { - code = -2; + code = KRB5_LIBOS_PWDINTR; goto _exit; } @@ -1241,164 +1276,32 @@ k5_kinit_prompter(krb5_context context, /* entering a NULL password is equivalent to cancelling out */ if (g_fjob.null_password) - return -2; + return KRB5_LIBOS_PWDINTR; else return code; } -/* - - The configuration information for each identity comes from a - multitude of layers organized as follows. The ordering is - decreasing in priority. When looking up a value, the value will be - looked up in each layer in turn starting at level 0. The first - instance of the value found will be the effective value. - - 0 : \Krb5Cred - - 0.1: per user - - 0.2: per machine - - 1 : \Parameters\Realms\ - - 1.1: per user - - 1.2: per machine - - 2 : \Parameters - - 2.1: per user - - 2.2: per machine - - 2.3: schema - - */ -khm_int32 -k5_open_config_handle(khm_handle ident, - khm_int32 flags, - khm_handle * ret_csp) { - - khm_int32 rv = KHM_ERROR_SUCCESS; - khm_handle csp_i = NULL; - khm_handle csp_ik5 = NULL; - khm_handle csp_realms = NULL; - khm_handle csp_realm = NULL; - khm_handle csp_plugins = NULL; - khm_handle csp_krbcfg = NULL; - khm_handle csp_rv = NULL; - wchar_t realm[KCDB_IDENT_MAXCCH_NAME]; - - realm[0] = L'\0'; - - if (ident) { - wchar_t idname[KCDB_IDENT_MAXCCH_NAME]; - wchar_t * trealm; - khm_size cb_idname = sizeof(idname); - - rv = kcdb_identity_get_name(ident, idname, &cb_idname); - if (KHM_SUCCEEDED(rv) && - (trealm = khm_get_realm_from_princ(idname)) != NULL) { - StringCbCopy(realm, sizeof(realm), trealm); - } - } - - if (ident) { - rv = kcdb_identity_get_config(ident, flags, &csp_i); - if (KHM_FAILED(rv)) - goto done; - - rv = khc_open_space(csp_i, CSNAME_KRB5CRED, flags, &csp_ik5); - if (KHM_FAILED(rv)) - goto done; - - if (realm[0] == L'\0') - goto done_shadow_realm; - - rv = khc_open_space(csp_params, CSNAME_REALMS, flags, &csp_realms); - if (KHM_FAILED(rv)) - goto done_shadow_realm; - - rv = khc_open_space(csp_realms, realm, flags, &csp_realm); - if (KHM_FAILED(rv)) - goto done_shadow_realm; - - rv = khc_shadow_space(csp_realm, csp_params); - done_shadow_realm: - - if (csp_realm) - rv = khc_shadow_space(csp_ik5, csp_realm); - else - rv = khc_shadow_space(csp_ik5, csp_params); - - csp_rv = csp_ik5; - - } else { - - /* No valid identity specified. We default to the parameters key. */ - rv = kmm_get_plugins_config(0, &csp_plugins); - if (KHM_FAILED(rv)) - goto done; - - rv = khc_open_space(csp_plugins, CSNAME_KRB5CRED, flags, &csp_krbcfg); - if (KHM_FAILED(rv)) - goto done; - - rv = khc_open_space(csp_krbcfg, CSNAME_PARAMS, flags, &csp_rv); - } - - done: +void +k5_read_dlg_params(k5_dlg_data * d, khm_handle identity) +{ + k5_params p; - *ret_csp = csp_rv; + khm_krb5_get_identity_params(identity, &p); - /* leave csp_ik5. If it's non-NULL, then it's the return value */ - /* leave csp_rv. It's the return value. */ - if (csp_i) - khc_close_space(csp_i); - if (csp_realms) - khc_close_space(csp_realms); - if (csp_realm) - khc_close_space(csp_realm); - if (csp_plugins) - khc_close_space(csp_plugins); - if (csp_krbcfg) - khc_close_space(csp_krbcfg); + d->renewable = p.renewable; + d->forwardable = p.forwardable; + d->proxiable = p.proxiable; + d->addressless = p.addressless; + d->publicIP = p.publicIP; - return rv; -} + d->tc_lifetime.current = p.lifetime; + d->tc_lifetime.max = p.lifetime_max; + d->tc_lifetime.min = p.lifetime_min; -void -k5_read_dlg_params(khm_handle conf, - k5_dlg_data * d) -{ - khm_int32 i; - - khc_read_int32(conf, L"Renewable", &i); - d->renewable = i; - khc_read_int32(conf, L"Forwardable", &i); - d->forwardable = i; - khc_read_int32(conf, L"Proxiable", &i); - d->proxiable = i; - khc_read_int32(conf, L"Addressless", &i); - d->addressless = i; - khc_read_int32(conf, L"PublicIP", &i); - d->publicIP = i; - - khc_read_int32(conf, L"DefaultLifetime", &i); - d->tc_lifetime.current = i; - khc_read_int32(conf, L"MaxLifetime", &i); - d->tc_lifetime.max = i; - khc_read_int32(conf, L"MinLifetime", &i); - d->tc_lifetime.min = i; - - khc_read_int32(conf, L"DefaultRenewLifetime", &i); - d->tc_renew.current = i; - khc_read_int32(conf, L"MaxRenewLifetime", &i); - d->tc_renew.max = i; - khc_read_int32(conf, L"MinRenewLifetime", &i); - d->tc_renew.min = i; + d->tc_renew.current = p.renew_life; + d->tc_renew.max = p.renew_life_max; + d->tc_renew.min = p.renew_life_min; /* however, if this has externally supplied defaults, we have to use them too. */ @@ -1431,28 +1334,32 @@ k5_read_dlg_params(khm_handle conf, } void -k5_write_dlg_params(khm_handle conf, - k5_dlg_data * d) +k5_write_dlg_params(k5_dlg_data * d, khm_handle identity) { - khc_write_int32(conf, L"Renewable", d->renewable); - khc_write_int32(conf, L"Forwardable", d->forwardable); - khc_write_int32(conf, L"Proxiable", d->proxiable); - khc_write_int32(conf, L"Addressless", d->addressless); - khc_write_int32(conf, L"PublicIP", d->publicIP); - - khc_write_int32(conf, L"DefaultLifetime", - (khm_int32) d->tc_lifetime.current); - khc_write_int32(conf, L"MaxLifetime", - (khm_int32) d->tc_lifetime.max); - khc_write_int32(conf, L"MinLifetime", - (khm_int32) d->tc_lifetime.min); - - khc_write_int32(conf, L"DefaultRenewLifetime", - (khm_int32) d->tc_renew.current); - khc_write_int32(conf, L"MaxRenewLifetime", - (khm_int32) d->tc_renew.max); - khc_write_int32(conf, L"MinRenewLifetime", - (khm_int32) d->tc_renew.min); + + k5_params p; + + ZeroMemory(&p, sizeof(p)); + + p.source_reg = K5PARAM_FM_ALL; /* we want to write all the + settings to the registry, if + necessary. */ + + p.renewable = d->renewable; + p.forwardable = d->forwardable; + p.proxiable = d->proxiable; + p.addressless = d->addressless; + p.publicIP = d->publicIP; + + p.lifetime = (krb5_deltat) d->tc_lifetime.current; + p.lifetime_max = (krb5_deltat) d->tc_lifetime.max; + p.lifetime_min = (krb5_deltat) d->tc_lifetime.min; + + p.renew_life = (krb5_deltat) d->tc_renew.current; + p.renew_life_max = (krb5_deltat) d->tc_renew.max; + p.renew_life_min = (krb5_deltat) d->tc_renew.min; + + khm_krb5_set_identity_params(identity, &p); /* as in k5_read_dlg_params, once we write the data in, the local data is no longer dirty */ @@ -1528,6 +1435,13 @@ k5_prep_kinit_job(khui_new_creds * nc) g_fjob.identity = ident; g_fjob.prompt_set = 0; g_fjob.valid_principal = FALSE; + g_fjob.retry_if_valid_principal = FALSE; + + /* the value for + retry_if_valid_principal is not + necessarily the correct value here, + but the correct value will be + assigned k5_kinit_fiber_proc(). */ /* if we have external parameters, we should use them as well */ if (nc->ctx.cb_vparam == sizeof(NETID_DLGINFO) && @@ -1891,7 +1805,7 @@ k5_msg_cred_dialog(khm_int32 msg_type, } if (nc->subtype == KMSG_CRED_NEW_CREDS) { - k5_read_dlg_params(csp_params, d); + k5_read_dlg_params(d, NULL); } PostMessage(hwnd, KHUI_WM_NC_NOTIFY, @@ -1926,22 +1840,10 @@ k5_msg_cred_dialog(khm_int32 msg_type, if(/* !d->dirty && */ nc->n_identities > 0 && nc->subtype == KMSG_CRED_NEW_CREDS) { - khm_handle h_idcfg = NULL; - - do { - if (KHM_FAILED - (k5_open_config_handle(nc->identities[0], - 0, &h_idcfg))) - break; - - k5_read_dlg_params(h_idcfg, d); - - PostMessage(nct->hwnd_panel, KHUI_WM_NC_NOTIFY, - MAKEWPARAM(0,WMNC_DIALOG_SETUP), 0); - } while(FALSE); + k5_read_dlg_params(d, nc->identities[0]); - if(h_idcfg) - khc_close_space(h_idcfg); + PostMessage(nct->hwnd_panel, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0,WMNC_DIALOG_SETUP), 0); } khui_cw_unlock_nc(nc); @@ -2015,12 +1917,14 @@ k5_msg_cred_dialog(khm_int32 msg_type, if (d->pwd_change) return KHM_ERROR_SUCCESS; - /* At this point, we assume that the current set of - prompts is no longer valid. And since we might not be - able to come up with a set of prompts until the KDC - replies (unless we have cached prompts), we remove the - ones that are already shown. */ +#if 0 + /* Clearing the prompts at this point is a bad idea since + the prompter depends on the prompts to know if this set + of prompts is the same as the new set and if so, use + the values entered in the old prompts as responses to + the new one. */ khui_cw_clear_prompts(nc); +#endif /* if the fiber is already in a kinit, cancel it */ if(g_fjob.state == FIBER_STATE_KINIT) { @@ -2259,7 +2163,19 @@ k5_msg_cred_dialog(khm_int32 msg_type, NULL)))) { _reportf(L"No password entered, but a valid TGT exists. Continuing"); g_fjob.code = 0; - } + } else if (g_fjob.state == FIBER_STATE_NONE && + g_fjob.code == 0 && + nc->n_identities > 0 && + nc->identities[0] != NULL) { + + /* we had a password and we used it to get + tickets. We should reset the IMPORTED flag now + since the tickets are not imported. */ + + khm_krb5_set_identity_flags(nc->identities[0], + K5IDFLAG_IMPORTED, + 0); + } if(g_fjob.code != 0) { wchar_t tbuf[1024]; @@ -2296,7 +2212,6 @@ k5_msg_cred_dialog(khm_int32 msg_type, } else if (nc->result == KHUI_NC_RESULT_PROCESS && g_fjob.state == FIBER_STATE_NONE) { - khm_handle csp_idcfg = NULL; krb5_context ctx = NULL; _reportf(L"Tickets successfully acquired"); @@ -2311,16 +2226,7 @@ k5_msg_cred_dialog(khm_int32 msg_type, assert(nc->n_identities > 0); assert(nc->identities[0]); - if (KHM_SUCCEEDED - (k5_open_config_handle(nc->identities[0], - KHM_FLAG_CREATE | - KCONF_FLAG_WRITEIFMOD, - &csp_idcfg))) { - k5_write_dlg_params(csp_idcfg, d); - } - - if(csp_idcfg != NULL) - khc_close_space(csp_idcfg); + k5_write_dlg_params(d, nc->identities[0]); /* We should also quickly refresh the credentials so that the identity flags and ccache @@ -2631,16 +2537,8 @@ k5_msg_cred_dialog(khm_int32 msg_type, /* since this was just a password change, we need to load new credentials options from the configuration store. */ - - if (KHM_SUCCEEDED - (k5_open_config_handle(nc->identities[0], - KHM_FLAG_CREATE | - KCONF_FLAG_WRITEIFMOD, - &csp_idcfg))) { - k5_read_dlg_params(csp_idcfg, d); - khc_close_space(csp_idcfg); - csp_idcfg = NULL; - } + + k5_read_dlg_params(d, nc->identities[0]); } /* the password change phase is now done */ @@ -2669,14 +2567,9 @@ k5_msg_cred_dialog(khm_int32 msg_type, /* save the settings that we used for obtaining the ticket. */ - if (nc->subtype == KMSG_CRED_NEW_CREDS && - KHM_SUCCEEDED - (k5_open_config_handle(nc->identities[0], - KHM_FLAG_CREATE | - KCONF_FLAG_WRITEIFMOD, - &csp_idcfg))) { - k5_write_dlg_params(csp_idcfg, d); - khc_close_space(csp_idcfg); + if (nc->subtype == KMSG_CRED_NEW_CREDS) { + + k5_write_dlg_params(d, nc->identities[0]); /* and then update the LRU too */ k5_update_LRU(nc->identities[0]); diff --git a/src/windows/identity/plugins/krb5/krbcred.h b/src/windows/identity/plugins/krb5/krbcred.h index 44b7733e3..d552fd0c8 100644 --- a/src/windows/identity/plugins/krb5/krbcred.h +++ b/src/windows/identity/plugins/krb5/krbcred.h @@ -176,6 +176,7 @@ typedef struct _fiber_job_t { BOOL null_password; BOOL valid_principal; + BOOL retry_if_valid_principal; } fiber_job; extern fiber_job g_fjob; /* global fiber job object */ @@ -184,8 +185,9 @@ extern fiber_job g_fjob; /* global fiber job object */ #define FIBER_CMD_CANCEL 2 #define FIBER_CMD_CONTINUE 3 -#define FIBER_STATE_NONE 0 -#define FIBER_STATE_KINIT 1 +#define FIBER_STATE_NONE 0 +#define FIBER_STATE_KINIT 1 +#define FIBER_STATE_RETRY_KINIT 2 #define K5_SET_CRED_MSG WMNC_USER diff --git a/src/windows/identity/plugins/krb5/lang/en_us/langres.rc b/src/windows/identity/plugins/krb5/lang/en_us/langres.rc index d93b4415e..4d98fd314 100644 --- a/src/windows/identity/plugins/krb5/lang/en_us/langres.rc +++ b/src/windows/identity/plugins/krb5/lang/en_us/langres.rc @@ -338,7 +338,7 @@ IDI_MODIFIED ICON "..\\..\\images\\modified.ico" STRINGTABLE BEGIN IDS_UNK_ADDR_FMT "Unknown address type %d" - IDS_KRB5_CREDTEXT_0 "

Krb5: Creds for realm %s

" + IDS_KRB5_CREDTEXT_0 "

Krb5: Tickets for realm %s

" IDS_KRB5_CCNAME_SHORT_DESC "Kerberos v5 CCache" IDS_KEY_ENCTYPE_SHORT_DESC "Session EncType" IDS_TKT_ENCTYPE_SHORT_DESC "Service EncType" @@ -501,3 +501,4 @@ END ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED + diff --git a/src/windows/identity/ui/Makefile b/src/windows/identity/ui/Makefile index c3355041d..e328866a1 100644 --- a/src/windows/identity/ui/Makefile +++ b/src/windows/identity/ui/Makefile @@ -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) $** $@ diff --git a/src/windows/identity/ui/cfg_general_wnd.c b/src/windows/identity/ui/cfg_general_wnd.c index 71edb2688..fa7ac6fd4 100644 --- a/src/windows/identity/ui/cfg_general_wnd.c +++ b/src/windows/identity/ui/cfg_general_wnd.c @@ -36,6 +36,7 @@ typedef struct tag_cfg_data { BOOL auto_detect_net; BOOL log_to_file; BOOL destroy_creds; + khm_int32 notif_action; } cfg_data; typedef struct tag_dlg_data { @@ -81,6 +82,9 @@ read_params(dlg_data * dd) { khc_read_int32(csp_cw, L"DestroyCredsOnExit", &t); d->destroy_creds = !!t; + khc_read_int32(csp_cw, L"NotificationAction", &t); + d->notif_action = t; + khc_close_space(csp_cw); dd->work = *d; @@ -144,6 +148,11 @@ write_params(dlg_data * dd) { applied = TRUE; } + if (d->notif_action != s->notif_action) { + khc_write_int32(csp_cw, L"NotificationAction", d->notif_action); + applied = TRUE; + } + khc_close_space(csp_cw); khui_cfg_set_flags(dd->node, @@ -165,7 +174,8 @@ check_for_modification(dlg_data * dd) { !!d->keep_running != !!s->keep_running || !!d->auto_detect_net != !!s->auto_detect_net || !!d->log_to_file != !!s->log_to_file || - !!d->destroy_creds != !!s->destroy_creds) { + !!d->destroy_creds != !!s->destroy_creds || + d->notif_action != s->notif_action) { khui_cfg_set_flags(dd->node, KHUI_CNFLAG_MODIFIED, @@ -180,9 +190,22 @@ check_for_modification(dlg_data * dd) { } } + +static void +strip_ampersands(wchar_t * str) { + wchar_t *f, *t; + + for(f = t = str; *f; f++) + if (*f != L'&') + *t++ = *f; + + *t = L'\0'; +} + static void refresh_view(HWND hwnd, dlg_data * d) { wchar_t buf[512]; + khm_size i; CheckDlgButton(hwnd, IDC_CFG_AUTOINIT, (d->work.auto_init?BST_CHECKED:BST_UNCHECKED)); @@ -199,6 +222,44 @@ refresh_view(HWND hwnd, dlg_data * d) { CheckDlgButton(hwnd, IDC_CFG_DESTROYALL, (d->work.destroy_creds?BST_CHECKED:BST_UNCHECKED)); + /* we need populate the notification action combo box control and + set the current selection to match the default action. */ + + if (n_khm_notifier_actions != SendDlgItemMessage(hwnd, IDC_CFG_NOTACTION, + CB_GETCOUNT, 0, 0)) { + + for (i=0; i < n_khm_notifier_actions; i++) { + int idx; + + khm_get_action_caption(khm_notifier_actions[i], + buf, sizeof(buf)); + + strip_ampersands(buf); + + idx = (int) SendDlgItemMessage(hwnd, IDC_CFG_NOTACTION, + CB_INSERTSTRING, i, + (LPARAM) buf); + +#ifdef DEBUG + if (idx != (int) i) { + assert(FALSE); + } +#endif + } + } + + for (i=0; i < n_khm_notifier_actions; i++) { + if (khm_notifier_actions[i] == d->work.notif_action) + break; + } + + if (i >= n_khm_notifier_actions) { + d->work.notif_action = khm_notifier_actions[0]; + i = 0; + } + + SendDlgItemMessage(hwnd, IDC_CFG_NOTACTION, CB_SETCURSEL, i, 0); + /* in addition, we correct the label on the trace log control to reflect the actual path that is going to get used */ if (GetDlgItemText(hwnd, IDC_CFG_LOGPATH, buf, @@ -212,6 +273,8 @@ refresh_view(HWND hwnd, dlg_data * d) { static void refresh_data(HWND hwnd, dlg_data * d) { + int idx; + d->work.auto_init = (IsDlgButtonChecked(hwnd, IDC_CFG_AUTOINIT) == BST_CHECKED); d->work.auto_start = (IsDlgButtonChecked(hwnd, IDC_CFG_AUTOSTART) @@ -226,6 +289,14 @@ refresh_data(HWND hwnd, dlg_data * d) { == BST_CHECKED); d->work.destroy_creds = (IsDlgButtonChecked(hwnd, IDC_CFG_DESTROYALL) == BST_CHECKED); + + idx = (int) SendDlgItemMessage(hwnd, IDC_CFG_NOTACTION, CB_GETCURSEL, 0, 0); + if (idx < 0) + idx = 0; + else if (idx >= (int) n_khm_notifier_actions) + idx = (int) n_khm_notifier_actions - 1; + + d->work.notif_action = khm_notifier_actions[idx]; } INT_PTR CALLBACK @@ -323,6 +394,9 @@ khm_cfg_general_proc(HWND hwnd, refresh_data(hwnd, d); check_for_modification(d); } + } else if (HIWORD(wParam) == CBN_SELCHANGE) { + refresh_data(hwnd, d); + check_for_modification(d); } khm_set_dialog_result(hwnd, 0); diff --git a/src/windows/identity/ui/credfuncs.c b/src/windows/identity/ui/credfuncs.c index 7b7032212..2f1ac8033 100644 --- a/src/windows/identity/ui/credfuncs.c +++ b/src/windows/identity/ui/credfuncs.c @@ -34,6 +34,7 @@ static HANDLE in_dialog_evt = NULL; static LONG init_dialog = 0; static khm_int32 dialog_result = 0; static wchar_t dialog_identity[KCDB_IDENT_MAXCCH_NAME]; +static khui_new_creds * dialog_nc = NULL; static void dialog_sync_init(void) { @@ -65,9 +66,28 @@ khm_cred_begin_dialog(void) { EnterCriticalSection(&cs_dialog); - if (in_dialog) + if (in_dialog) { rv = FALSE; - else { + + /* if a dialog is being displayed and we got a another request + to show one, we bring the existing one to the + foreground. */ + if (dialog_nc && dialog_nc->hwnd) { + khm_int32 t = 0; + + if (KHM_SUCCEEDED(khc_read_int32(NULL, + L"CredWindow\\Windows\\NewCred\\ForceToTop", + &t)) && + t != 0) { + + khm_activate_main_window(); + + SetWindowPos(dialog_nc->hwnd, HWND_TOP, 0, 0, 0, 0, + (SWP_NOMOVE | SWP_NOSIZE)); + } + } + + } else { rv = TRUE; in_dialog = TRUE; ResetEvent(in_dialog_evt); @@ -87,6 +107,10 @@ khm_cred_end_dialog(khui_new_creds * nc) { SetEvent(in_dialog_evt); } dialog_result = nc->result; +#ifdef DEBUG + assert(dialog_nc == nc); +#endif + dialog_nc = NULL; if (nc->subtype == KMSG_CRED_NEW_CREDS && nc->n_identities > 0 && nc->identities[0]) { @@ -367,6 +391,22 @@ kmsg_cred_completion(kmq_message *m) if(evt->suggestion) khui_alert_set_suggestion(alert, evt->suggestion); + if (nc->subtype == KMSG_CRED_RENEW_CREDS && + nc->ctx.identity != NULL) { + + khm_int32 n_cmd; + + n_cmd = khm_get_identity_new_creds_action(nc->ctx.identity); + + if (n_cmd != 0) { + khui_alert_add_command(alert, n_cmd); + khui_alert_add_command(alert, KHUI_PACTION_CLOSE); + + khui_alert_set_flags(alert, KHUI_ALERT_FLAG_DISPATCH_CMD, + KHUI_ALERT_FLAG_DISPATCH_CMD); + } + } + khui_alert_show(alert); khui_alert_release(alert); @@ -763,6 +803,7 @@ void khm_cred_change_password(wchar_t * title) khui_cw_create_cred_blob(&nc); nc->subtype = KMSG_CRED_PASSWORD; + dialog_nc = nc; khui_context_get(&nc->ctx); @@ -813,6 +854,31 @@ void khm_cred_change_password(wchar_t * title) } } +void +khm_cred_obtain_new_creds_for_ident(khm_handle ident, wchar_t * title) +{ + khui_action_context ctx; + + if (ident == NULL) + khm_cred_obtain_new_creds(title); + + khui_context_get(&ctx); + + khui_context_set(KHUI_SCOPE_IDENT, + ident, + KCDB_CREDTYPE_INVALID, + NULL, + NULL, + 0, + NULL); + + khm_cred_obtain_new_creds(title); + + khui_context_set_indirect(&ctx); + + khui_context_release(&ctx); +} + void khm_cred_obtain_new_creds(wchar_t * title) { khui_new_creds * nc; @@ -824,6 +890,7 @@ void khm_cred_obtain_new_creds(wchar_t * title) khui_cw_create_cred_blob(&nc); nc->subtype = KMSG_CRED_NEW_CREDS; + dialog_nc = nc; khui_context_get(&nc->ctx); @@ -1190,6 +1257,8 @@ khm_cred_process_startup_actions(void) { line stuff */ khm_startup.processing = FALSE; khm_startup.remote = FALSE; + + kmq_post_message(KMSG_ACT, KMSG_ACT_END_CMDLINE, 0, 0); } while(FALSE); if (defident) diff --git a/src/windows/identity/ui/credfuncs.h b/src/windows/identity/ui/credfuncs.h index b3c88faa4..761cbf506 100644 --- a/src/windows/identity/ui/credfuncs.h +++ b/src/windows/identity/ui/credfuncs.h @@ -53,6 +53,9 @@ khm_cred_renew_creds(void); void khm_cred_obtain_new_creds(wchar_t * window_title); +void +khm_cred_obtain_new_creds_for_ident(khm_handle ident, wchar_t * title); + void khm_cred_set_default(void); diff --git a/src/windows/identity/ui/credwnd.c b/src/windows/identity/ui/credwnd.c index a555ebeaa..474bfa9aa 100644 --- a/src/windows/identity/ui/credwnd.c +++ b/src/windows/identity/ui/credwnd.c @@ -403,9 +403,16 @@ cw_load_view(khui_credwnd_tbl * tbl, wchar_t * view, HWND hwnd) { int i; HDC hdc; LOGFONT log_font; + khm_int32 t; + const wchar_t * viewval; tbl->hwnd = hwnd; + if (khm_main_wnd_mode == KHM_MAIN_WND_MINI) + viewval = L"DefaultViewMini"; + else + viewval = L"DefaultView"; + if(KHM_FAILED(khc_open_space(NULL, L"CredWindow", KHM_PERM_READ | KHM_PERM_WRITE, &hc_cw))) return; @@ -415,7 +422,7 @@ cw_load_view(khui_credwnd_tbl * tbl, wchar_t * view, HWND hwnd) { if(!view) { cbsize = sizeof(buf); - if(KHM_FAILED(khc_read_string(hc_cw, L"DefaultView", buf, &cbsize))) + if(KHM_FAILED(khc_read_string(hc_cw, viewval, buf, &cbsize))) goto _exit; view = buf; @@ -434,10 +441,13 @@ cw_load_view(khui_credwnd_tbl * tbl, wchar_t * view, HWND hwnd) { else if (!wcscmp(view, L"Custom_0")) khui_check_radio_action(khui_find_menu(KHUI_MENU_LAYOUT), KHUI_ACTION_LAYOUT_CUST); + else if (!wcscmp(view, L"CompactIdentity")) + khui_check_radio_action(khui_find_menu(KHUI_MENU_LAYOUT), + KHUI_ACTION_LAYOUT_MINI); - kmq_post_message(KMSG_ACT, KMSG_ACT_REFRESH, 0, 0); + kmq_post_message(KMSG_ACT, KMSG_ACT_REFRESH, 0, 0); } else { - khc_write_string(hc_cw, L"DefaultView", view); + khc_write_string(hc_cw, viewval, view); } if(KHM_FAILED(khc_open_space(hc_vs, view, KHM_PERM_READ, &hc_v))) @@ -466,6 +476,18 @@ cw_load_view(khui_credwnd_tbl * tbl, wchar_t * view, HWND hwnd) { tbl->flags &= ~(KHUI_CW_TBL_CUSTVIEW | KHUI_CW_TBL_COLSKIP); + if (KHM_SUCCEEDED(khc_read_int32(hc_v, L"ExpandedIdentity", &t)) && t) { + tbl->flags |= KHUI_CW_TBL_EXPIDENT; + } else { + tbl->flags &= ~KHUI_CW_TBL_EXPIDENT; + } + + if (KHM_SUCCEEDED(khc_read_int32(hc_v, L"NoHeader", &t)) && t) { + tbl->flags |= KHUI_CW_TBL_NOHEADER; + } else { + tbl->flags &= ~KHUI_CW_TBL_NOHEADER; + } + iter = clist; i = 0; while(iter) { @@ -594,6 +616,8 @@ _skip_col: tbl->cr_hdr_outline = RGB(0,0,0); tbl->cr_hdr_normal = RGB(0,0,0); tbl->cr_hdr_sel = RGB(255,255,255); + tbl->cr_hdr_gray = RGB(128,128,128); + tbl->cr_hdr_gray_sel = RGB(240,240,240); tbl->ilist = khui_create_ilist(KHUI_SMICON_CX, KHUI_SMICON_CY-1, 20, 8, 0); { @@ -652,6 +676,71 @@ _exit: We keep that open until the view is unloaded. */ } +khm_int32 KHMAPI +cw_credset_iter_func(khm_handle cred, void * rock) { + khui_credwnd_tbl * tbl = (khui_credwnd_tbl *) rock; + khm_handle ident = NULL; + khm_size i; + khui_credwnd_ident * cwi = NULL; + + kcdb_cred_get_identity(cred, &ident); + + if (ident == NULL) + goto _cleanup; + + for (i=0; i < tbl->n_idents; i++) { + if (kcdb_identity_is_equal(ident, tbl->idents[i].ident)) + break; + } + + if (i >= tbl->n_idents) { + khm_size cb; + + /* need to add this one */ + if (tbl->n_idents == tbl->nc_idents) { + tbl->nc_idents = UBOUNDSS(tbl->n_idents + 1, + CW_IDENT_ALLOC_INCR, + CW_IDENT_ALLOC_INCR); +#ifdef DEBUG + assert(tbl->nc_idents > tbl->n_idents); +#endif + tbl->idents = PREALLOC(tbl->idents, sizeof(tbl->idents[0]) * tbl->nc_idents); +#ifdef DEBUG + assert(tbl->idents); +#endif + ZeroMemory(&tbl->idents[tbl->n_idents], + sizeof(tbl->idents[0]) * (tbl->nc_idents - tbl->n_idents)); + } + + i = tbl->n_idents; + cwi = &tbl->idents[tbl->n_idents++]; + + cwi->ident = ident; + kcdb_identity_hold(ident); + cb = sizeof(cwi->name); + kcdb_identity_get_name(ident, cwi->name, &cb); + kcdb_identity_get_type(&cwi->credtype); + cb = sizeof(cwi->credtype_name); + kcdb_credtype_describe(cwi->credtype, cwi->credtype_name, &cb, KCDB_TS_SHORT); + cwi->credcount = 0; + } + + cwi = &tbl->idents[i]; + + /* this is the first time we are seeing this */ + if (cwi->credcount == 0) { + kcdb_identity_get_flags(cwi->ident, &cwi->ident_flags); + } + + cwi->credcount++; + + _cleanup: + if (ident) + kcdb_identity_release(ident); + + return KHM_ERROR_SUCCESS; +} + void cw_update_creds(khui_credwnd_tbl * tbl) { @@ -751,6 +840,13 @@ cw_update_creds(khui_credwnd_tbl * tbl) } } + /* refresh the per-identity information */ + for (i=0; i < (int) tbl->n_idents; i++) { + tbl->idents[i].credcount = 0; + } + + kcdb_credset_apply(tbl->credset, cw_credset_iter_func, (void *) tbl); + if (fields) PFREE(fields); } @@ -957,6 +1053,9 @@ cw_set_tbl_row_header(khui_credwnd_tbl * tbl, tbl->rows[row].flags = KHUI_CW_ROW_HEADER; if(o->flags & KHUI_CW_O_SELECTED) tbl->rows[row].flags |= KHUI_CW_ROW_SELECTED; + if ((tbl->flags & KHUI_CW_TBL_EXPIDENT) && + tbl->cols[col].attr_id == KCDB_ATTR_ID_NAME) + tbl->rows[row].flags |= KHUI_CW_ROW_EXPVIEW; } static int @@ -1230,6 +1329,12 @@ cw_update_outline(khui_credwnd_tbl * tbl) ol->flags &= ~KHUI_CW_O_SHOWFLAG; ol->flags &= ~KHUI_CW_O_STICKY; + if (grouping[j] == tbl->n_cols - 1) { + ol->flags |= KHUI_CW_O_NOOUTLINE; + } else { + ol->flags &= ~KHUI_CW_O_NOOUTLINE; + } + if(selected) { ol->flags |= KHUI_CW_O_SELECTED; } @@ -1256,9 +1361,15 @@ cw_update_outline(khui_credwnd_tbl * tbl) ol->flags |= KHUI_CW_O_SHOWFLAG; expstate |= flags; } + + /* if we aren't showing any creds under this outline + level, we should also show any flags. */ + else if (grouping[j] == tbl->n_cols - 1) { + ol->flags |= KHUI_CW_O_SHOWFLAG; + } } } - + /* we need to do this here too just in case we were already at the level we were supposed to be in */ if (ol) @@ -1266,7 +1377,7 @@ cw_update_outline(khui_credwnd_tbl * tbl) flags = cw_get_buf_exp_flags(tbl, thiscred); - if(visible) { + if(visible && grouping[n_grouping - 1] < tbl->n_cols - 1) { khm_int32 c_flags; cw_set_tbl_row_cred(tbl, n_rows, thiscred, @@ -1492,6 +1603,21 @@ cw_unload_view(khui_credwnd_tbl * tbl) } tbl->cell_height = 0; /* recalculate cell height next time */ + + if (tbl->idents) { + khm_size i; + + for (i=0; i < tbl->n_idents; i++) { + if (tbl->idents[i].ident) { + kcdb_identity_release(tbl->idents[i].ident); + } + } + + PFREE(tbl->idents); + tbl->idents = NULL; + tbl->n_idents = 0; + tbl->nc_idents = 0; + } } void @@ -1589,7 +1715,19 @@ cw_update_extents(khui_credwnd_tbl * tbl, tbl->cell_height = height + tbl->vpad * 2; } - ext_y = (int) tbl->n_rows * tbl->cell_height; + if (tbl->flags & KHUI_CW_TBL_EXPIDENT) { + ext_y = 0; + + for (i=0; i < (int) tbl->n_rows; i++) { + if (tbl->rows[i].flags & KHUI_CW_ROW_EXPVIEW) { + ext_y += tbl->cell_height * CW_EXP_ROW_MULT; + } else { + ext_y += tbl->cell_height; + } + } + } else { + ext_y = (int) tbl->n_rows * tbl->cell_height; + } tbl->ext_width = ext_x; tbl->ext_height = ext_y; @@ -1824,26 +1962,36 @@ cw_draw_header(HDC hdc, if (o->flags & KHUI_CW_O_STICKY) { /* khui_ilist_draw_id(tbl->ilist, IDB_TK_NEW_SM, hdc, r->left, r->bottom - KHUI_SMICON_CY, 0); */ - } else if((tbl->mouse_state & CW_MOUSE_WOUTLINE) && - tbl->mouse_row == row) { - if(o->flags & KHUI_CW_O_EXPAND) { - khui_ilist_draw_id(tbl->ilist, IDB_WDG_EXPAND_HI, - hdc, r->left, r->bottom - KHUI_SMICON_CY, 0); - } else { - khui_ilist_draw_id(tbl->ilist, IDB_WDG_COLLAPSE_HI, - hdc, r->left, r->bottom - KHUI_SMICON_CY, 0); - } - } else { - if(o->flags & KHUI_CW_O_EXPAND) { - khui_ilist_draw_id(tbl->ilist, IDB_WDG_EXPAND, - hdc, r->left, r->bottom - KHUI_SMICON_CY, 0); + } else if (!(o->flags & KHUI_CW_O_NOOUTLINE)) { + if((tbl->mouse_state & CW_MOUSE_WOUTLINE) && + tbl->mouse_row == row) { + if(o->flags & KHUI_CW_O_EXPAND) { + khui_ilist_draw_id(tbl->ilist, IDB_WDG_EXPAND_HI, + hdc, r->left, + r->bottom - (KHUI_SMICON_CY + + (r->bottom - (r->top + KHUI_SMICON_CY)) / 2), 0); + } else { + khui_ilist_draw_id(tbl->ilist, IDB_WDG_COLLAPSE_HI, + hdc, r->left, + r->bottom - (KHUI_SMICON_CY + + (r->bottom - (r->top + KHUI_SMICON_CY)) / 2), 0); + } } else { - khui_ilist_draw_id(tbl->ilist, IDB_WDG_COLLAPSE, - hdc, r->left, r->bottom - KHUI_SMICON_CY, 0); + if(o->flags & KHUI_CW_O_EXPAND) { + khui_ilist_draw_id(tbl->ilist, IDB_WDG_EXPAND, + hdc, r->left, + r->bottom - (KHUI_SMICON_CY + + (r->bottom - (r->top + KHUI_SMICON_CY)) / 2), 0); + } else { + khui_ilist_draw_id(tbl->ilist, IDB_WDG_COLLAPSE, + hdc, r->left, + r->bottom - (KHUI_SMICON_CY + + (r->bottom - (r->top + KHUI_SMICON_CY)) / 2), 0); + } } - } - r->left += KHUI_SMICON_CX * 3 / 2; + r->left += KHUI_SMICON_CX * 3 / 2; + } /* try to draw the icon, if there is one */ if(colattr == KCDB_ATTR_ID_NAME) { @@ -1858,7 +2006,9 @@ cw_draw_header(HDC hdc, IDB_WDG_STUCK: IDB_WDG_STICK)), hdc, - r->left, r->bottom - KHUI_SMICON_CY, + r->left, + r->bottom - (KHUI_SMICON_CY + + (r->bottom - (r->top + KHUI_SMICON_CY)) / 2), 0); r->left += KHUI_SMICON_CX * 3 / 2; @@ -1868,38 +2018,125 @@ cw_draw_header(HDC hdc, IDB_ID_DIS_SM: IDB_ID_SM), hdc, - r->left, r->bottom - KHUI_SMICON_CY, + r->left, + r->bottom - (KHUI_SMICON_CY + + (r->bottom - (r->top + KHUI_SMICON_CY)) / 2), 0); r->left += KHUI_SMICON_CX * 3 / 2 ; } - /* ok, now o->header contains the string representation of the - outline value */ - /* for now just write out the value */ - SetTextAlign(hdc, TA_BOTTOM | TA_LEFT); - if(selected) - SetTextColor(hdc, tbl->cr_hdr_sel); - else - SetTextColor(hdc, tbl->cr_hdr_normal); + if (!(cr->flags & KHUI_CW_ROW_EXPVIEW)) { + + SetTextAlign(hdc, TA_BOTTOM | TA_LEFT); + + if(selected) + SetTextColor(hdc, tbl->cr_hdr_sel); + else + SetTextColor(hdc, tbl->cr_hdr_normal); + + TextOut(hdc, r->left, r->bottom - tbl->vpad, o->header, (int) wcslen(o->header)); + + if (colattr == KCDB_ATTR_ID_NAME && + (idf & KCDB_IDENT_FLAG_DEFAULT)) { + wchar_t defstr[64]; + SIZE size; + + LoadString(khm_hInstance, IDS_CW_DEFAULT, + defstr, ARRAYLENGTH(defstr)); + + GetTextExtentPoint32(hdc, o->header, (int) wcslen(o->header), + &size); + + r->left += size.cx + KHUI_SMICON_CX * 2; + + TextOut(hdc, r->left, r->bottom - tbl->vpad, + defstr, (int) wcslen(defstr)); + } + } else { + + RECT tr; + int len; + + /* expanded view */ +#ifdef DEBUG + assert(colattr == KCDB_ATTR_ID_NAME); +#endif + + CopyRect(&tr, r); + tr.bottom -= (tr.bottom - tr.top) / 2; /* drawing two lines of text */ + + if (selected) + SetTextColor(hdc, tbl->cr_hdr_sel); + else + SetTextColor(hdc, tbl->cr_hdr_normal); + + len = (int) wcslen(o->header); + DrawText(hdc, o->header, len, &tr, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); + + if ((idf & KCDB_IDENT_FLAG_DEFAULT)) { + SIZE size; + int cx_id; + int cx_def; + wchar_t defstr[64]; + + LoadString(khm_hInstance, IDS_CW_DEFAULT, + defstr, ARRAYLENGTH(defstr)); + + GetTextExtentPoint32(hdc, o->header, (int) len, &size); + cx_id = size.cx; + + len = (int) wcslen(defstr); + GetTextExtentPoint32(hdc, defstr, (int) len, &size); + cx_def = size.cx + KHUI_SMICON_CX / 2; - TextOut(hdc, r->left, r->bottom - tbl->vpad, o->header, (int) wcslen(o->header)); + tr.left = max(tr.right - cx_def, tr.left + cx_id + KHUI_SMICON_CX * 2); + if (selected) + SetTextColor(hdc, tbl->cr_hdr_gray_sel); + else + SetTextColor(hdc, tbl->cr_hdr_gray); + DrawText(hdc, defstr, len, &tr, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); + } + + CopyRect(&tr, r); + tr.top += (tr.bottom - tr.top) / 2; - if (colattr == KCDB_ATTR_ID_NAME && - (idf & KCDB_IDENT_FLAG_DEFAULT)) { - wchar_t defstr[64]; - SIZE size; + if (0) { + khm_size i; + wchar_t buf[128]; - LoadString(khm_hInstance, IDS_CW_DEFAULT, - defstr, ARRAYLENGTH(defstr)); + for (i = 0; i < tbl->n_idents; i++) { + if (kcdb_identity_is_equal(o->data, tbl->idents[i].ident)) + break; + } - GetTextExtentPoint32(hdc, o->header, (int) wcslen(o->header), - &size); + if (i < tbl->n_idents) { + khui_credwnd_ident * cwi; + + cwi = &tbl->idents[i]; + + if (cwi->credcount == 0) + LoadString(khm_hInstance, IDS_IDEXPDISP_NOCRED, + buf, ARRAYLENGTH(buf)); + else if (cwi->credcount == 1) + LoadString(khm_hInstance, IDS_IDEXPDISP_1CRED, + buf, ARRAYLENGTH(buf)); + else { + wchar_t fmt[128]; + LoadString(khm_hInstance, IDS_IDEXPDISP_NCRED, + fmt, ARRAYLENGTH(fmt)); + StringCbPrintf(buf, sizeof(buf), fmt, (int) cwi->credcount); + } + } - r->left += size.cx + KHUI_SMICON_CX * 2; + len = (int) wcslen(buf); - TextOut(hdc, r->left, r->bottom - tbl->vpad, - defstr, (int) wcslen(defstr)); + if (selected) + SetTextColor(hdc, tbl->cr_hdr_gray_sel); + else + SetTextColor(hdc, tbl->cr_hdr_gray); + DrawText(hdc, buf, len, &tr, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); + } } } @@ -2334,6 +2571,7 @@ cw_wm_paint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) int flag_col = -1; int d_x = -1; int selected = 0; + int rowheight = 0; BOOL has_dc = FALSE; tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); @@ -2380,11 +2618,9 @@ cw_wm_paint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) /* We *NEED* all the meta columns to be on the left */ - row_s = tbl->scr_top / tbl->cell_height; - ys = row_s * tbl->cell_height; - row_e = (tbl->scr_top + (r.bottom - r.top)) / tbl->cell_height + 1; - if(row_e > (int) tbl->n_rows) - row_e = (int) tbl->n_rows; + row_s = 0; + ys = 0; + row_e = (int) tbl->n_rows; x = 0; col_s = -1; col_e = -1; @@ -2422,6 +2658,7 @@ cw_wm_paint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) y = ys; for(i=row_s; i < row_e; i++) { selected = tbl->rows[i].flags & KHUI_CW_ROW_SELECTED; + rowheight = (tbl->rows[i].flags & KHUI_CW_ROW_EXPVIEW)? tbl->cell_height * CW_EXP_ROW_MULT : tbl->cell_height; if(tbl->cursor_row == i) { if (tbl->rows[i].flags & KHUI_CW_ROW_HEADER) @@ -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++) rh.right += tbl->cols[j].width; rh.top = y; - rh.bottom = y + tbl->cell_height; + rh.bottom = y + rowheight; if(rh.right > rh.left) { cw_erase_rect(hdc, tbl, &r, &rh, (selected)?CW_ER_SEL:CW_ER_BLANK); } @@ -2456,7 +2693,7 @@ cw_wm_paint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) x = xs; rh.top = y; - rh.bottom = y + tbl->cell_height; + rh.bottom = y + rowheight; for(j=col_s; j < col_e; x += tbl->cols[j++].width) { wchar_t buf[256]; khm_size cbbuf; @@ -2539,7 +2776,7 @@ cw_wm_paint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) SelectFont(hdc, tbl->hf_normal); } - y += tbl->cell_height; + y += rowheight; } @@ -3292,7 +3529,24 @@ cw_wm_mouse(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) x += tbl->scr_left; y += tbl->scr_top - tbl->header_height; - row = y / tbl->cell_height; + if (tbl->flags & KHUI_CW_TBL_EXPIDENT) { + int ty = 0; + + for (i=0; i < tbl->n_rows; i++) { + if (tbl->rows[i].flags & KHUI_CW_ROW_EXPVIEW) + ty += tbl->cell_height * CW_EXP_ROW_MULT; + else + ty += tbl->cell_height; + + if (ty > y) + break; + } + + row = i; + } else { + row = y / tbl->cell_height; + } + col = -1; nm_state = CW_MOUSE_NONE; nm_row = nm_col = -1; @@ -3313,22 +3567,41 @@ cw_wm_mouse(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) nm_row = row; nm_col = col; if(tbl->rows[row].flags & KHUI_CW_ROW_HEADER) { + khui_credwnd_outline * o; + + o = (khui_credwnd_outline *) tbl->rows[row].data; + /* are we on a widget then? */ x -= tbl->cols[tbl->rows[row].col].x; - if(x >= 0 && x < KHUI_SMICON_CX) /* hit */ { - nm_state |= CW_MOUSE_WOUTLINE | CW_MOUSE_WIDGET; - } else if (tbl->cols[tbl->rows[row].col].attr_id == - KCDB_ATTR_ID_NAME && - col == tbl->rows[row].col && - x >= KHUI_SMICON_CX * 3 / 2 && - x < KHUI_SMICON_CX * 5 / 2){ - nm_state |= CW_MOUSE_WSTICKY | CW_MOUSE_WIDGET; - } else if (tbl->cols[tbl->rows[row].col].attr_id == - KCDB_ATTR_ID_NAME && - col == tbl->rows[row].col && - x >= KHUI_SMICON_CX * 3 && - x < KHUI_SMICON_CX * 4) { - nm_state |= CW_MOUSE_WICON | CW_MOUSE_WIDGET; + + if (!(o->flags & KHUI_CW_O_NOOUTLINE)) { + if(x >= 0 && x < KHUI_SMICON_CX) /* hit */ { + nm_state |= CW_MOUSE_WOUTLINE | CW_MOUSE_WIDGET; + } else if (tbl->cols[tbl->rows[row].col].attr_id == + KCDB_ATTR_ID_NAME && + col == tbl->rows[row].col && + x >= KHUI_SMICON_CX * 3 / 2 && + x < KHUI_SMICON_CX * 5 / 2){ + nm_state |= CW_MOUSE_WSTICKY | CW_MOUSE_WIDGET; + } else if (tbl->cols[tbl->rows[row].col].attr_id == + KCDB_ATTR_ID_NAME && + col == tbl->rows[row].col && + x >= KHUI_SMICON_CX * 3 && + x < KHUI_SMICON_CX * 4) { + nm_state |= CW_MOUSE_WICON | CW_MOUSE_WIDGET; + } + } else if (tbl->cols[tbl->rows[row].col].attr_id == KCDB_ATTR_ID_NAME) { + if (col == tbl->rows[row].col && + x >= 0 && + x < KHUI_SMICON_CX){ + + nm_state |= CW_MOUSE_WSTICKY | CW_MOUSE_WIDGET; + + } else if (col == tbl->rows[row].col && + x >= KHUI_SMICON_CX * 3 / 2 && + x < KHUI_SMICON_CX * 5 / 2) { + nm_state |= CW_MOUSE_WICON | CW_MOUSE_WIDGET; + } } } } @@ -3408,12 +3681,50 @@ cw_wm_mouse(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) InvalidateRect(tbl->hwnd, &r, TRUE); } if(tbl->mouse_state & CW_MOUSE_WSTICKY) { - r.left = KHUI_SMICON_CX * 3 / 2 + - tbl->cols[tbl->mouse_col].x - tbl->scr_left; - r.top = tbl->mouse_row * tbl->cell_height + - tbl->header_height - tbl->scr_top; - r.right = r.left + KHUI_SMICON_CX; - r.bottom = r.top + tbl->cell_height; + if (tbl->flags & KHUI_CW_TBL_EXPIDENT) { + int ty = 0; + + for (i=0; i < tbl->n_rows && i < tbl->mouse_row; i++) { + if (tbl->rows[i].flags & KHUI_CW_ROW_EXPVIEW) + ty += tbl->cell_height * CW_EXP_ROW_MULT; + else + ty += tbl->cell_height; + } + + r.top = ty; + + if (tbl->mouse_row < tbl->n_rows && + (tbl->rows[tbl->mouse_row].flags & KHUI_CW_ROW_EXPVIEW)) { + r.bottom = r.top + tbl->cell_height * CW_EXP_ROW_MULT; + } else { + r.bottom = r.top + tbl->cell_height; + } + + if (tbl->mouse_row < tbl->n_rows && + (tbl->rows[tbl->mouse_row].flags & KHUI_CW_ROW_HEADER)) { + khui_credwnd_outline * o; + + o = (khui_credwnd_outline *) tbl->rows[tbl->mouse_row].data; + + if (o->flags & KHUI_CW_O_NOOUTLINE) { + r.left = tbl->cols[tbl->mouse_col].x - tbl->scr_left; + } else { + r.left = KHUI_SMICON_CX * 3 / 2 + tbl->cols[tbl->mouse_col].x - tbl->scr_left; + } + r.right = r.left + KHUI_SMICON_CX; + } else { +#ifdef DEBUG + assert(FALSE); +#endif + } + } else { + r.left = KHUI_SMICON_CX * 3 / 2 + + tbl->cols[tbl->mouse_col].x - tbl->scr_left; + r.top = tbl->mouse_row * tbl->cell_height + + tbl->header_height - tbl->scr_top; + r.right = r.left + KHUI_SMICON_CX; + r.bottom = r.top + tbl->cell_height; + } InvalidateRect(tbl->hwnd, &r, TRUE); } @@ -3436,12 +3747,50 @@ cw_wm_mouse(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) InvalidateRect(tbl->hwnd, &r, TRUE); } if(tbl->mouse_state & CW_MOUSE_WSTICKY) { - r.left = KHUI_SMICON_CX * 3 / 2 + - tbl->cols[tbl->mouse_col].x - tbl->scr_left; - r.top = tbl->mouse_row * tbl->cell_height + - tbl->header_height - tbl->scr_top; - r.right = r.left + KHUI_SMICON_CX; - r.bottom = r.top + tbl->cell_height; + if (tbl->flags & KHUI_CW_TBL_EXPIDENT) { + int ty = 0; + + for (i=0; i < tbl->n_rows && i < tbl->mouse_row; i++) { + if (tbl->rows[i].flags & KHUI_CW_ROW_EXPVIEW) + ty += tbl->cell_height * CW_EXP_ROW_MULT; + else + ty += tbl->cell_height; + } + + r.top = ty; + + if (tbl->mouse_row < tbl->n_rows && + (tbl->rows[tbl->mouse_row].flags & KHUI_CW_ROW_EXPVIEW)) { + r.bottom = r.top + tbl->cell_height * CW_EXP_ROW_MULT; + } else { + r.bottom = r.top + tbl->cell_height; + } + + if (tbl->mouse_row < tbl->n_rows && + (tbl->rows[tbl->mouse_row].flags & KHUI_CW_ROW_HEADER)) { + khui_credwnd_outline * o; + + o = (khui_credwnd_outline *) tbl->rows[tbl->mouse_row].data; + + if (o->flags & KHUI_CW_O_NOOUTLINE) { + r.left = tbl->cols[tbl->mouse_col].x - tbl->scr_left; + } else { + r.left = KHUI_SMICON_CX * 3 / 2 + tbl->cols[tbl->mouse_col].x - tbl->scr_left; + } + r.right = r.left + KHUI_SMICON_CX; + } else { +#ifdef DEBUG + assert(FALSE); +#endif + } + } else { + r.left = KHUI_SMICON_CX * 3 / 2 + + tbl->cols[tbl->mouse_col].x - tbl->scr_left; + r.top = tbl->mouse_row * tbl->cell_height + + tbl->header_height - tbl->scr_top; + r.right = r.left + KHUI_SMICON_CX; + r.bottom = r.top + tbl->cell_height; + } InvalidateRect(tbl->hwnd, &r, TRUE); } } else if(tbl->mouse_state != nm_state) { @@ -4285,6 +4634,22 @@ cw_wm_command(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } break; + case KHUI_ACTION_LAYOUT_MINI: + { + cw_save_view(tbl, NULL); + cw_unload_view(tbl); + + cw_load_view(tbl, NULL, hwnd); + cw_insert_header_cols(tbl); + + cw_update_creds(tbl); + cw_update_outline(tbl); + cw_update_extents(tbl, FALSE); + + InvalidateRect(tbl->hwnd, NULL, TRUE); + } + break; + case KHUI_PACTION_UP: case KHUI_PACTION_UP_EXTEND: case KHUI_PACTION_UP_TOGGLE: @@ -4513,7 +4878,24 @@ cw_wm_contextmenu(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) return DefWindowProc(hwnd, uMsg, wParam, lParam); } - row = y / tbl->cell_height; + if (tbl->flags & KHUI_CW_TBL_EXPIDENT) { + int i, yt; + + yt = 0; + for (i=0; i < tbl->n_rows && yt < y; i++) { + if (tbl->rows[i].flags & KHUI_CW_ROW_EXPVIEW) + yt += tbl->cell_height * CW_EXP_ROW_MULT; + else + yt += tbl->cell_height; + if (yt > y) + break; + } + + row = i; + + } else { + row = y / tbl->cell_height; + } if(row < 0 || row >= (int) tbl->n_rows) return FALSE; diff --git a/src/windows/identity/ui/credwnd.h b/src/windows/identity/ui/credwnd.h index 50b7a114f..3d39ebc4f 100644 --- a/src/windows/identity/ui/credwnd.h +++ b/src/windows/identity/ui/credwnd.h @@ -67,6 +67,7 @@ typedef struct khui_credwnd_outline_t { #define KHUI_CW_O_SHOWFLAG 0x00000008 #define KHUI_CW_O_SELECTED 0x00000010 #define KHUI_CW_O_DATAALLOC 0x00000020 +#define KHUI_CW_O_NOOUTLINE 0x00000040 typedef struct khui_credwnd_row_t { khm_int32 flags; @@ -80,6 +81,7 @@ typedef struct khui_credwnd_row_t { #define KHUI_CW_ROW_HEADER 0x00000004 #define KHUI_CW_ROW_TIMERSET 0x00000008 #define KHUI_CW_ROW_SELECTED 0x00000010 +#define KHUI_CW_ROW_EXPVIEW 0x00000020 /* row allocation */ /* initial number of rows to be allocated */ @@ -119,6 +121,22 @@ typedef struct khui_credwnd_col_t { #define cw_is_custom_attr(i) ((i)<0) +typedef struct tag_khui_credwnd_ident { + + khm_handle ident; + khm_int32 ident_flags; + khm_int32 credtype; + wchar_t name[KCDB_IDENT_MAXCCH_NAME]; + wchar_t credtype_name[KCDB_MAXCCH_NAME]; + + khm_size credcount; + +} khui_credwnd_ident; + +#define CW_IDENT_ALLOC_INCR 4 + +#define CW_EXP_ROW_MULT 2 + typedef struct khui_credwnd_tbl_t { HWND hwnd; /* the window that this table belongs to */ @@ -171,6 +189,8 @@ typedef struct khui_credwnd_tbl_t { COLORREF cr_sel; /* selected text color */ COLORREF cr_hdr_normal; /* normal header text color */ COLORREF cr_hdr_sel; /* selected header text color */ + COLORREF cr_hdr_gray; /* gray header text color */ + COLORREF cr_hdr_gray_sel; /* selected gray header text color */ HBRUSH hb_hdr_bg; /* header background color (normal) */ HBRUSH hb_hdr_bg_exp; /* header background color (expired) */ HBRUSH hb_hdr_bg_warn; /* header background color (warn) */ @@ -198,6 +218,11 @@ typedef struct khui_credwnd_tbl_t { /* the credentials set */ khm_handle credset; + + khui_credwnd_ident * idents; + khm_size n_idents; + khm_size nc_idents; + } khui_credwnd_tbl; #define KHUI_MAXCB_HEADING 256 @@ -209,6 +234,8 @@ typedef struct khui_credwnd_tbl_t { #define KHUI_CW_TBL_ACTIVE 0x00000100 #define KHUI_CW_TBL_CUSTVIEW 0x00000200 #define KHUI_CW_TBL_COLSKIP 0x00000400 +#define KHUI_CW_TBL_EXPIDENT 0x00000800 +#define KHUI_CW_TBL_NOHEADER 0x00001000 /* mouse_state constants */ #define CW_MOUSE_NONE 0x00000000 /* nothing interesting */ diff --git a/src/windows/identity/ui/lang/en_us/khapp.rc b/src/windows/identity/ui/lang/en_us/khapp.rc index 6fadebb94..b8e2e3d2a 100644 --- a/src/windows/identity/ui/lang/en_us/khapp.rc +++ b/src/windows/identity/ui/lang/en_us/khapp.rc @@ -240,23 +240,25 @@ IDD_CFG_GENERAL DIALOGEX 0, 0, 255, 182 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - GROUPBOX "Startup / Shutdown",IDC_CFG_STARTUP_GROUP,7,7,241,50 + GROUPBOX "Startup / Shutdown",IDC_CFG_STARTUP_GROUP,7,7,241,44 CONTROL "&Obtain new credentials at startup (if none are present)",IDC_CFG_AUTOINIT, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,22,196,10 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,20,196,10 CONTROL "&Destroy all credentials on exit",IDC_CFG_DESTROYALL, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,39,111,10 - CONTROL "&Start Network Identity Manager during Windows logon",IDC_CFG_AUTOSTART, - "Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,16,48,135,10 - GROUPBOX "Other",IDC_CFG_OTHER,7,63,241,85 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,36,111,10 + GROUPBOX "Other",IDC_CFG_OTHER,7,55,241,120 CONTROL "&Run Network Identity Manager in system tray after window close",IDC_CFG_KEEPRUNNING, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,78,230,10 - CONTROL "Monitor network connectivity",IDC_CFG_NETDETECT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,96,106,10 - CONTROL "Log trace events to trace log at the following location:",IDC_CFG_LOGTOFILE, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,113,225,10 - EDITTEXT IDC_CFG_LOGPATH,16,127,173,14,ES_AUTOHSCROLL | ES_READONLY - PUSHBUTTON "Show log ...",IDC_CFG_SHOWLOG,193,127,50,14 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,68,230,10 + LTEXT "Clicking on the ¬ification icon",IDC_CFG_NOTACT_STATIC,26,87,99,8 + COMBOBOX IDC_CFG_NOTACTION,133,85,110,48,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + CONTROL "&Monitor network connectivity",IDC_CFG_NETDETECT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,104,106,10 + CONTROL "&Log trace events to trace log at the following location:",IDC_CFG_LOGTOFILE, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,121,225,10 + EDITTEXT IDC_CFG_LOGPATH,16,135,173,14,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "&Show log",IDC_CFG_SHOWLOG,193,135,50,14 CONTROL "A&utomatically import Windows logon identity",IDC_CFG_AUTOIMPORT, - "Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,16,158,165,10 + "Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,16,156,165,10 + CONTROL "&Start Network Identity Manager during Windows logon",IDC_CFG_AUTOSTART, + "Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,113,165,135,10 END IDD_CFG_IDENTITIES DIALOGEX 0, 0, 255, 182 @@ -342,18 +344,18 @@ BEGIN PUSHBUTTON "Remove identity ...",IDC_CFG_REMOVE,139,122,78,14 END -IDD_ABOUT DIALOGEX 0, 0, 268, 170 +IDD_ABOUT DIALOGEX 0, 0, 324, 238 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About Network Identity Manager" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - DEFPUSHBUTTON "OK",IDOK,211,7,50,14 - LTEXT "Productname",IDC_PRODUCT,41,7,163,13,NOT WS_GROUP - LTEXT "© 2005-2007 Massachusetts Institute of Technology\n© 2006-2007 Secure Endpoints Inc.",IDC_COPYRIGHT,41,23,220,18,NOT WS_GROUP - LTEXT "BuildInfo",IDC_BUILDINFO,41,43,220,15,NOT WS_GROUP - ICON IDI_MAIN_APP,IDC_STATIC,6,7,20,20 - CONTROL "",IDC_MODULES,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,41,72,220,91 - LTEXT "Loaded modules",IDC_STATIC,41,60,52,8 + DEFPUSHBUTTON "OK",IDOK,267,7,50,14 + LTEXT "Productname",IDC_PRODUCT,41,7,225,13,NOT WS_GROUP + LTEXT "© 2005-2007 Massachusetts Institute of Technology\n© 2006-2007 Secure Endpoints Inc.",IDC_COPYRIGHT,41,23,276,23,NOT WS_GROUP + LTEXT "BuildInfo",IDC_BUILDINFO,41,49,276,20,NOT WS_GROUP + ICON IDI_MAIN_APP,IDC_STATIC,6,7,21,20 + CONTROL "",IDC_MODULES,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,41,88,276,143 + LTEXT "Loaded modules",IDC_STATIC,41,76,52,8 END IDD_CFG_APPEAR DIALOGEX 0, 0, 255, 182 @@ -507,11 +509,11 @@ BEGIN IDD_ABOUT, DIALOG BEGIN LEFTMARGIN, 6 - RIGHTMARGIN, 261 + RIGHTMARGIN, 317 VERTGUIDE, 41 - VERTGUIDE, 204 + VERTGUIDE, 266 TOPMARGIN, 7 - BOTTOMMARGIN, 163 + BOTTOMMARGIN, 231 END IDD_CFG_APPEAR, DIALOG @@ -623,7 +625,7 @@ BEGIN IDS_PROP_COL_VALUE "Value" IDS_NC_NEW_IDENT "( New identity ... )" IDS_NC_CREDTEXT_ID_CHECKING "%s (Checking...)" - IDS_ACTION_OPEN_APP "Open Network Identity Manager" + IDS_ACTION_OPEN_APP "Show Network Identity Manager window" IDS_CTX_NEW_IDENT "Obaining new identity" IDS_CTX_NEW_CREDS "Obtaining new credentials" IDS_CTX_RENEW_CREDS "Renewing credentials" @@ -634,7 +636,7 @@ STRINGTABLE BEGIN IDS_CTX_PROC_NEW_CREDS "Obtaining new credentials for %1!s!" IDS_CTX_PROC_RENEW_CREDS "Renewing credentials for %1!s!" - IDS_ACTION_CLOSE_APP "Close Network Identity Manager window" + IDS_ACTION_CLOSE_APP "Hide Network Identity Manager window" IDS_NC_FAILED_TITLE "Failed to acquire credentials" IDS_CFG_IDENTITIES_SHORT "Identities" IDS_CFG_IDENTITIES_LONG "Global Identity settings" @@ -774,20 +776,37 @@ END STRINGTABLE BEGIN - IDS_NC_REN_FAILED_TITLE_I "Failed to renew creds for %s" + IDS_NC_REN_FAILED_TITLE_I "Failed to renew credentials for %s" IDS_CFG_IDNAME_NON "No identity selected. Please select an identity and try again." IDS_MENU_DESTROY_CRED "Destroy" IDS_MENU_RENEW_CRED "Renew" - IDS_ACTION_DESTROY_ALL "All identities" - IDS_ACTION_RENEW_ALL "All identities" - IDS_IDACTION_RENEW "Renew credentials for %s" - IDS_IDACTION_DESTROY "Destroy credentials for %s" + IDS_ACTION_DESTROY_ALL "Destroy all identities" + IDS_ACTION_RENEW_ALL "Renew all identities" + IDS_IDACTION_RENEW "Renew %s" + IDS_IDACTION_DESTROY "Destroy %s" IDS_CTX_DESTROY_ID "Destroying identity %1!s!" IDS_NCN_IDENT_INVALID "Identity %s is invalid." IDS_NCN_IDENT_CHECKING "Checking identity %s ..." IDS_NCN_IDENT_UNKNOWN "Validity of identity %s coudn't be determined." 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." IDS_REMOTE_FAIL_TITLE "Failed to communicate with Network Identity Manager" + IDS_IDACTION_NEW "Initialize %s" + IDS_IDACTIONT_NEW "New credentials for %s" +END + +STRINGTABLE +BEGIN + IDS_IDACTIONT_RENEW "Renew credentials for %s" + IDS_IDACTIONT_DESTROY "Destroy credentials for %s" + IDS_ALERTTYPE_PLUGIN "Failed to load plug-in" + IDS_ALERTTYPE_EXPIRE "Credential expiration warning" + IDS_ALERTTYPE_RENEWFAIL "Failed to renew credentials" + IDS_ALERTTYPE_ACQUIREFAIL "Failed to acquire credentials" + IDS_ALERTTYPE_CHPW "Failed to change password" + IDS_ACTION_LAYOUT_MINI "&Advanced" + IDS_IDEXPDISP_NOCRED "(This identity has no credentials)" + IDS_IDEXPDISP_1CRED "(This identity has 1 credential)" + IDS_IDEXPDISP_NCRED "(This identity has %d credentials)" END #endif // English (U.S.) resources diff --git a/src/windows/identity/ui/main.c b/src/windows/identity/ui/main.c index 37ebe125f..82d0fb75d 100644 --- a/src/windows/identity/ui/main.c +++ b/src/windows/identity/ui/main.c @@ -202,9 +202,7 @@ void khm_add_dialog(HWND dlg) { if(n_khui_dialogs < MAX_UI_DIALOGS - 1) { khui_dialogs[n_khui_dialogs].hwnd = dlg; khui_dialogs[n_khui_dialogs].hwnd_next = NULL; - /* we set .active=FALSE for now. We don't need this to have a - meaningful value until we enter a modal loop */ - khui_dialogs[n_khui_dialogs].active = FALSE; + khui_dialogs[n_khui_dialogs].active = TRUE; n_khui_dialogs++; } else { #if DEBUG diff --git a/src/windows/identity/ui/mainmenu.c b/src/windows/identity/ui/mainmenu.c index e4267035e..70913f472 100644 --- a/src/windows/identity/ui/mainmenu.c +++ b/src/windows/identity/ui/mainmenu.c @@ -76,6 +76,50 @@ int khui_get_icon_index(int id) { return i; } +void khm_get_action_caption(khm_int32 action, wchar_t * buf, khm_size cb_buf) { + khui_action * act; + + StringCbCopy(buf, cb_buf, L""); + + khui_action_lock(); + act = khui_find_action(action); + + if (act == NULL) + goto done; + + if (act->caption) { + StringCbCopy(buf, cb_buf, act->caption); + } else if (act->is_caption) { + LoadString(khm_hInstance, act->is_caption, + buf, (int)(cb_buf / sizeof(wchar_t))); + } + + done: + khui_action_unlock(); +} + +void khm_get_action_tooltip(khm_int32 action, wchar_t * buf, khm_size cb_buf) { + khui_action * act; + + StringCbCopy(buf, cb_buf, L""); + + khui_action_lock(); + act = khui_find_action(action); + + if (act == NULL) + goto done; + + if (act->tooltip) { + StringCbCopy(buf, cb_buf, act->tooltip); + } else if (act->is_tooltip) { + LoadString(khm_hInstance, act->is_tooltip, + buf, (int) (cb_buf / sizeof(wchar_t))); + } + + done: + khui_action_unlock(); +} + void add_action_to_menu(HMENU hm, khui_action * act, int idx, int flags) { MENUITEMINFO mii; @@ -93,13 +137,7 @@ void add_action_to_menu(HMENU hm, khui_action * act, } else { khui_menu_def * def; - if (act->caption) { - StringCbCopy(buf, sizeof(buf), act->caption); - } else { - LoadString(khm_hInstance, - act->is_caption, - buf, ARRAYLENGTH(buf)); - } + khm_get_action_caption(act->cmd, buf, sizeof(buf)); if(khui_get_cmd_accel_string(act->cmd, accel, ARRAYLENGTH(accel))) { @@ -141,8 +179,10 @@ void add_action_to_menu(HMENU hm, khui_action * act, } } - if(flags & KHUI_ACTIONREF_DEFAULT) + if(flags & KHUI_ACTIONREF_DEFAULT) { + mii.fMask |= MIIM_STATE; mii.fState |= MFS_DEFAULT; + } } InsertMenuItem(hm,idx,TRUE,&mii); @@ -208,33 +248,27 @@ static void refresh_menu_item(HMENU hm, khui_action * act, static void refresh_menu(HMENU hm, khui_menu_def * def) { khui_action_ref * act; - int i; + khm_size i, n; - act = def->items; - i = 0; - while ((def->n_items == -1 && act->action != KHUI_MENU_END) || - (def->n_items >= 0 && i < (int) def->n_items)) { - refresh_menu_item(hm, khui_find_action(act->action), i, act->flags); - act++; i++; + for(i = 0, n = khui_menu_get_size(def); i < n; i++) { + act = khui_menu_get_action(def, i); + refresh_menu_item(hm, khui_find_action(act->action), (int) i, act->flags); } } static HMENU mm_create_menu_from_def(khui_menu_def * def, BOOL main) { HMENU hm; khui_action_ref * act; - int i; + khm_size i, n; if (main) hm = CreateMenu(); else hm = CreatePopupMenu(); - act = def->items; - i = 0; - while((!(def->state & KHUI_MENUSTATE_ALLOCD) && act->action != KHUI_MENU_END) || - ((def->state & KHUI_MENUSTATE_ALLOCD) && i < (int) def->n_items)) { - add_action_to_menu(hm,khui_find_action(act->action),i,act->flags); - act++; i++; + for (i = 0, n = khui_menu_get_size(def); i < n; i++) { + act = khui_menu_get_action(def, i); + add_action_to_menu(hm, khui_find_action(act->action), (int) i, act->flags); } return hm; @@ -415,12 +449,8 @@ LRESULT khm_menu_handle_select(WPARAM wParam, LPARAM lParam) { if(act == NULL || (act->is_tooltip == 0 && act->tooltip == NULL)) khm_statusbar_set_part(KHUI_SBPART_INFO, NULL, NULL); else { - if (act->tooltip) - StringCbCopy(buf, sizeof(buf), act->tooltip); - else - LoadString(khm_hInstance, - act->is_tooltip, - buf, ARRAYLENGTH(buf)); + khm_get_action_tooltip(act->cmd, buf, sizeof(buf)); + khm_statusbar_set_part(KHUI_SBPART_INFO, NULL, buf); } } @@ -562,6 +592,7 @@ struct identity_action_map { khm_handle identity; khm_int32 renew_cmd; khm_int32 destroy_cmd; + khm_int32 new_cmd; int refreshcycle; }; @@ -579,7 +610,9 @@ create_identity_cmd_map(khm_handle ident) { struct identity_action_map * actmap; wchar_t idname[KCDB_IDENT_MAXCCH_NAME]; wchar_t fmt[128]; + wchar_t caption[KHUI_MAXCCH_SHORT_DESC]; wchar_t tooltip[KHUI_MAXCCH_SHORT_DESC]; + wchar_t actionname[KHUI_MAXCCH_NAME]; khm_size cb; if (n_id_action_map + 1 > nc_id_action_map) { @@ -607,26 +640,55 @@ create_identity_cmd_map(khm_handle ident) { actmap->identity = ident; kcdb_identity_hold(ident); - fmt[0] = L'\0'; - LoadString(khm_hInstance, IDS_IDACTION_RENEW, - fmt, ARRAYLENGTH(fmt)); - StringCbPrintf(tooltip, sizeof(tooltip), fmt, idname); +#define GETFORMAT(I) do { fmt[0] = L'\0'; LoadString(khm_hInstance, I, fmt, ARRAYLENGTH(fmt)); } while(0) +#define EXPFORMAT(d,s) do { StringCbPrintf(d, sizeof(d), fmt, s); } while(0) + /* renew */ + + GETFORMAT(IDS_IDACTIONT_RENEW); + EXPFORMAT(tooltip, idname); + + GETFORMAT(IDS_IDACTION_RENEW); + EXPFORMAT(caption, idname); + + StringCbPrintf(actionname, sizeof(actionname), L"R:%s", idname); actmap->renew_cmd = - khui_action_create(NULL, idname, tooltip, NULL, + khui_action_create(actionname, caption, tooltip, NULL, KHUI_ACTIONTYPE_TRIGGER, NULL); - fmt[0] = L'\0'; - LoadString(khm_hInstance, IDS_IDACTION_DESTROY, - fmt, ARRAYLENGTH(fmt)); - StringCbPrintf(tooltip, sizeof(tooltip), fmt, idname); + /* destroy */ + + GETFORMAT(IDS_IDACTIONT_DESTROY); + EXPFORMAT(tooltip, idname); + + GETFORMAT(IDS_IDACTION_DESTROY); + EXPFORMAT(caption, idname); + + StringCbPrintf(actionname, sizeof(actionname), L"D:%s", idname); actmap->destroy_cmd = - khui_action_create(NULL, idname, tooltip, NULL, + khui_action_create(actionname, caption, tooltip, NULL, + KHUI_ACTIONTYPE_TRIGGER, NULL); + + /* new */ + + GETFORMAT(IDS_IDACTIONT_NEW); + EXPFORMAT(tooltip, idname); + + GETFORMAT(IDS_IDACTION_NEW); + EXPFORMAT(caption, idname); + + StringCbPrintf(actionname, sizeof(actionname), L"N:%s", idname); + + actmap->new_cmd = + khui_action_create(actionname, caption, tooltip, NULL, KHUI_ACTIONTYPE_TRIGGER, NULL); actmap->refreshcycle = idcmd_refreshcycle; +#undef GETFORMAT +#undef EXPFORMAT + return actmap; } @@ -669,8 +731,8 @@ get_identity_cmd_map(khm_handle ident) { } } -static khm_int32 -get_identity_renew_command(khm_handle ident) { +khm_int32 +khm_get_identity_renew_action(khm_handle ident) { struct identity_action_map * map; map = get_identity_cmd_map(ident); @@ -681,8 +743,8 @@ get_identity_renew_command(khm_handle ident) { return 0; } -static khm_int32 -get_identity_destroy_command(khm_handle ident) { +khm_int32 +khm_get_identity_destroy_action(khm_handle ident) { struct identity_action_map * map; map = get_identity_cmd_map(ident); @@ -693,6 +755,18 @@ get_identity_destroy_command(khm_handle ident) { return 0; } +khm_int32 +khm_get_identity_new_creds_action(khm_handle ident) { + struct identity_action_map * map; + + map = get_identity_cmd_map(ident); + + if (map) + return map->new_cmd; + else + return 0; +} + void khm_refresh_identity_menus(void) { khui_menu_def * renew_def = NULL; @@ -781,11 +855,11 @@ khm_refresh_identity_menus(void) { } khui_menu_insert_action(renew_def, 1000, - get_identity_renew_command(identity), + khm_get_identity_renew_action(identity), 0); khui_menu_insert_action(dest_def, 1000, - get_identity_destroy_command(identity), + khm_get_identity_destroy_action(identity), 0); } @@ -834,6 +908,12 @@ khm_check_identity_menu_action(khm_int32 act_id) { khm_cred_destroy_identity(id_action_map[i].identity); return TRUE; } + + if (id_action_map[i].new_cmd == act_id) { + khm_cred_obtain_new_creds_for_ident(id_action_map[i].identity, + NULL); + return TRUE; + } } } diff --git a/src/windows/identity/ui/mainmenu.h b/src/windows/identity/ui/mainmenu.h index a0f64a0a6..e8da77f39 100644 --- a/src/windows/identity/ui/mainmenu.h +++ b/src/windows/identity/ui/mainmenu.h @@ -49,6 +49,12 @@ LRESULT khm_menu_draw_item(WPARAM wParam, LPARAM lparam); void khm_menu_refresh_items(void); khm_boolean khm_check_identity_menu_action(khm_int32 act_id); void khm_refresh_identity_menus(void); +void khm_get_action_tooltip(khm_int32 action, wchar_t * buf, khm_size cb_buf); +void khm_get_action_caption(khm_int32 action, wchar_t * buf, khm_size cb_buf); + +khm_int32 khm_get_identity_destroy_action(khm_handle ident); +khm_int32 khm_get_identity_renew_action(khm_handle ident); +khm_int32 khm_get_identity_new_creds_action(khm_handle ident); static HMENU mm_create_menu_from_def(khui_menu_def * def, BOOL main); static void mm_show_panel_def(khui_menu_def * def, LONG x, LONG y); diff --git a/src/windows/identity/ui/mainwnd.c b/src/windows/identity/ui/mainwnd.c index 05e615786..494ef4bb6 100644 --- a/src/windows/identity/ui/mainwnd.c +++ b/src/windows/identity/ui/mainwnd.c @@ -36,6 +36,8 @@ HWND khm_hwnd_main; HWND khm_hwnd_rebar; HWND khm_hwnd_main_cred; +int khm_main_wnd_mode = KHM_MAIN_WND_NORMAL; + #define MW_RESIZE_TIMER 1 #define MW_RESIZE_TIMEOUT 2000 #define MW_REFRESH_TIMER 2 @@ -193,7 +195,14 @@ khm_ui_cb(LPARAM lParam) { #endif /* make the call */ - pcbdata->rv = (*pcbdata->cb)(khm_hwnd_main, pcbdata->rock); + if (!IsBadCodePtr(pcbdata->cb)) + pcbdata->rv = (*pcbdata->cb)(khm_hwnd_main, pcbdata->rock); + else { +#ifdef DEBUG + assert(FALSE); +#endif + pcbdata->rv = KHM_ERROR_INVALID_PARAM; + } } LRESULT CALLBACK @@ -368,6 +377,25 @@ khm_main_wnd_proc(HWND hwnd, khm_ui_cb(lParam); return 0; + /* layout control */ + case KHUI_ACTION_LAYOUT_MINI: + if (khm_main_wnd_mode == KHM_MAIN_WND_MINI) { + khm_set_main_window_mode(KHM_MAIN_WND_NORMAL); + } else { + khm_set_main_window_mode(KHM_MAIN_WND_MINI); + } + return SendMessage(khm_hwnd_main_cred, uMsg, + wParam, lParam); + + case KHUI_ACTION_LAYOUT_ID: + case KHUI_ACTION_LAYOUT_TYPE: + case KHUI_ACTION_LAYOUT_LOC: + case KHUI_ACTION_LAYOUT_CUST: + case KHUI_ACTION_LAYOUT_RELOAD: + khm_set_main_window_mode(KHM_MAIN_WND_NORMAL); + return SendMessage(khm_hwnd_main_cred, uMsg, + wParam, lParam); + /* menu commands */ case KHUI_PACTION_MENU: if(HIWORD(lParam) == 1) @@ -423,11 +451,6 @@ khm_main_wnd_proc(HWND hwnd, case KHUI_PACTION_DELETE: case KHUI_PACTION_SELALL: - case KHUI_ACTION_LAYOUT_ID: - case KHUI_ACTION_LAYOUT_TYPE: - case KHUI_ACTION_LAYOUT_LOC: - case KHUI_ACTION_LAYOUT_CUST: - case KHUI_ACTION_LAYOUT_RELOAD: /* otherwise fallthrough and bounce to the creds window */ return SendMessage(khm_hwnd_main_cred, uMsg, wParam, lParam); @@ -525,11 +548,27 @@ khm_main_wnd_proc(HWND hwnd, } break; + case WM_MOVING: + { + RECT * r; + + r = (RECT *) lParam; + khm_adjust_window_dimensions_for_display(r, + KHM_DOCK_AUTO | KHM_DOCKF_XBORDER); + } + return TRUE; + case WM_TIMER: if (wParam == MW_RESIZE_TIMER) { RECT r; khm_handle csp_cw; khm_handle csp_mw; + const wchar_t * wconfig; + + if (khm_main_wnd_mode == KHM_MAIN_WND_MINI) + wconfig = L"Windows\\MainMini"; + else + wconfig = L"Windows\\Main"; KillTimer(hwnd, wParam); @@ -540,9 +579,11 @@ khm_main_wnd_proc(HWND hwnd, KHM_PERM_WRITE, &csp_cw))) { if (KHM_SUCCEEDED(khc_open_space(csp_cw, - L"Windows\\Main", + wconfig, KHM_PERM_WRITE, &csp_mw))) { + khm_int32 t; + khc_write_int32(csp_mw, L"XPos", r.left); khc_write_int32(csp_mw, L"YPos", r.top); khc_write_int32(csp_mw, L"Width", @@ -550,6 +591,11 @@ khm_main_wnd_proc(HWND hwnd, khc_write_int32(csp_mw, L"Height", r.bottom - r.top); + if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"Dock", &t)) && + t != KHM_DOCK_NONE) { + khc_write_int32(csp_mw, L"Dock", KHM_DOCK_AUTO); + } + khc_close_space(csp_mw); } khc_close_space(csp_cw); @@ -584,6 +630,9 @@ khm_main_wnd_proc(HWND hwnd, } else if (m->type == KMSG_ACT && m->subtype == KMSG_ACT_CONTINUE_CMDLINE) { khm_cred_process_startup_actions(); + } else if (m->type == KMSG_ACT && + m->subtype == KMSG_ACT_END_CMDLINE) { + /* nothing yet */ } else if (m->type == KMSG_ACT && m->subtype == KMSG_ACT_SYNC_CFG) { khm_refresh_config(); @@ -618,6 +667,7 @@ khm_main_wnd_proc(HWND hwnd, m->subtype == KMSG_KMM_I_DONE) { kmq_post_message(KMSG_ACT, KMSG_ACT_BEGIN_CMDLINE, 0, 0); } + return kmq_wm_end(m, rv); } return 0; @@ -860,7 +910,7 @@ khm_create_main_window_controls(HWND hwnd_main) { } void -khm_adjust_window_dimensions_for_display(RECT * pr) { +khm_adjust_window_dimensions_for_display(RECT * pr, int dock) { HMONITOR hmon; RECT rm; @@ -908,14 +958,57 @@ khm_adjust_window_dimensions_for_display(RECT * pr) { if (height > (rm.bottom - rm.top)) height = rm.bottom - rm.top; - if (x < rm.left) + switch (dock & KHM_DOCKF_DOCKHINT) { + case KHM_DOCK_TOPLEFT: x = rm.left; - if (x + width > rm.right) + y = rm.top; + break; + + case KHM_DOCK_TOPRIGHT: x = rm.right - width; - if (y < rm.top) y = rm.top; - if (y + height > rm.bottom) + break; + + case KHM_DOCK_BOTTOMRIGHT: + x = rm.right - width; y = rm.bottom - height; + break; + + case KHM_DOCK_BOTTOMLEFT: + x = rm.left; + y = rm.bottom - height; + break; + + case KHM_DOCK_AUTO: + { + int cxt, cyt; + + cxt = GetSystemMetrics(SM_CXDRAG); + cyt = GetSystemMetrics(SM_CYDRAG); + + if (x > rm.left && (x - rm.left) < cxt) + x = rm.left; + else if ((x + width) < rm.right && (rm.right - (x + width)) < cxt) + x = rm.right - width; + + if (y > rm.top && (y - rm.top) < cyt) + y = rm.top; + else if ((y + height) < rm.bottom && (rm.bottom - (y + height)) < cyt) + y = rm.bottom - height; + } + break; + } + + if (!(dock & KHM_DOCKF_XBORDER)) { + if (x < rm.left) + x = rm.left; + if (x + width > rm.right) + x = rm.right - width; + if (y < rm.top) + y = rm.top; + if (y + height > rm.bottom) + y = rm.bottom - height; + } done_with_monitor: pr->left = x; @@ -925,13 +1018,109 @@ khm_adjust_window_dimensions_for_display(RECT * pr) { } +void +khm_get_main_window_rect(RECT * pr) { + khm_handle csp_mw = NULL; + int x,y,width,height,dock; + RECT r; + const wchar_t * wconfig; + + x = CW_USEDEFAULT; + y = CW_USEDEFAULT; + width = CW_USEDEFAULT; + height = CW_USEDEFAULT; + dock = KHM_DOCK_NONE; + + if (khm_main_wnd_mode == KHM_MAIN_WND_MINI) + wconfig = L"CredWindow\\Windows\\MainMini"; + else + wconfig = L"CredWindow\\Windows\\Main"; + + if (KHM_SUCCEEDED(khc_open_space(NULL, + wconfig, + KHM_PERM_READ, + &csp_mw))) { + khm_int32 t; + + if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"XPos", &t))) + x = t; + if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"YPos", &t))) + y = t; + if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"Width", &t))) + width = t; + if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"Height", &t))) + height = t; + if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"Dock", &t))) + dock = t; + + khc_close_space(csp_mw); + } + + /* If there were no default values, we default to using 1/4 of the + work area centered on the primary monitor. If there were any + docking hints, then the next call to + khm_adjust_window_dimensions_for_display() will reposition the + window. */ + if (width == CW_USEDEFAULT || x == CW_USEDEFAULT) { + RECT wr; + + SetRectEmpty(&wr); + SystemParametersInfo(SPI_GETWORKAREA, 0, &wr, 0); + + if (width == CW_USEDEFAULT) { + width = (wr.right - wr.left) / 2; + height = (wr.bottom - wr.top) / 2; + } + + if (x == CW_USEDEFAULT) { + x = (wr.left + wr.right) / 2 - width / 2; + y = (wr.top + wr.bottom) / 2 - height / 2; + } + } + + /* The saved dimensions might not actually be visible if the user + has changed the resolution of the display or if it's a multiple + monitor system where the monitor on which the Network Identity + Manager window was on previously is no longer connected. We + have to check for that and adjust the dimensions if needed. */ + SetRect(&r, x, y, x + width, y + height); + khm_adjust_window_dimensions_for_display(&r, dock); + + *pr = r; +} + +void +khm_set_main_window_mode(int mode) { + + RECT r; + + if (mode == khm_main_wnd_mode) + return; + + khui_check_action(KHUI_ACTION_LAYOUT_MINI, + ((mode == KHM_MAIN_WND_MINI)? FALSE : TRUE)); + khui_enable_action(KHUI_MENU_LAYOUT, + ((mode == KHM_MAIN_WND_MINI)? FALSE : TRUE)); + khui_enable_action(KHUI_MENU_COLUMNS, + ((mode == KHM_MAIN_WND_MINI)? FALSE : TRUE)); + + khm_main_wnd_mode = mode; + if (khm_hwnd_main) { + khm_get_main_window_rect(&r); + + SetWindowPos(khm_hwnd_main, + NULL, + r.left, r.top, + r.right - r.left, r.bottom - r.top, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_NOZORDER); + } +} void khm_create_main_window(void) { wchar_t buf[1024]; khm_handle csp_cw = NULL; - khm_handle csp_mw = NULL; - int x,y,width,height; RECT r; LoadString(khm_hInstance, IDS_MAIN_WINDOW_TITLE, @@ -951,41 +1140,19 @@ khm_create_main_window(void) { if (!khm_hwnd_null) return; - x = CW_USEDEFAULT; - y = CW_USEDEFAULT; - width = CW_USEDEFAULT; - height = CW_USEDEFAULT; - if (KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow", KHM_PERM_READ, &csp_cw))) { - if (KHM_SUCCEEDED(khc_open_space(csp_cw, - L"Windows\\Main", - KHM_PERM_READ, - &csp_mw))) { - khm_int32 t; - - if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"XPos", &t))) - x = t; - if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"YPos", &t))) - y = t; - if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"Width", &t))) - width = t; - if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"Height", &t))) - height = t; - - khc_close_space(csp_mw); + khm_int32 t; + + if (KHM_SUCCEEDED(khc_read_int32(csp_cw, L"DefaultWindowMode", &t))) { + khm_set_main_window_mode(t); } + khc_close_space(csp_cw); } - /* The saved dimensions might not actually be visible if the user - has changed the resolution of the display or if it's a multiple - monitor system where the monitor on which the Network Identity - Manager window was on previously is no longer connected. We - have to check for that and adjust the dimensions if needed. */ - SetRect(&r, x, y, x + width, y + height); - khm_adjust_window_dimensions_for_display(&r); + khm_get_main_window_rect(&r); khm_hwnd_main = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, diff --git a/src/windows/identity/ui/mainwnd.h b/src/windows/identity/ui/mainwnd.h index 221bf4246..263ffeba4 100644 --- a/src/windows/identity/ui/mainwnd.h +++ b/src/windows/identity/ui/mainwnd.h @@ -34,16 +34,23 @@ extern ATOM khm_main_window_class; extern HWND khm_hwnd_main; extern HWND khm_hwnd_rebar; +#define KHM_MAIN_WND_NORMAL 0 +#define KHM_MAIN_WND_MINI 1 + +extern int khm_main_wnd_mode; + void khm_register_main_wnd_class(void); void khm_unregister_main_wnd_class(void); void khm_create_main_window_controls(HWND); void khm_create_main_window(void); void khm_activate_main_window(void); void khm_show_main_window(void); +void khm_set_main_window_mode(int mode); void khm_close_main_window(void); void khm_hide_main_window(void); BOOL khm_is_main_window_visible(void); BOOL khm_is_main_window_active(void); +void khm_adjust_window_dimensions_for_display(RECT * pr, int dock); LRESULT khm_rebar_notify(LPNMHDR lpnm); void @@ -62,4 +69,13 @@ khm_main_wnd_proc(HWND hwnd, #define COMMANDLINE_MAP_FMT L"Local\\NetIDMgr_Cmdline_%lu" #define QUERY_APP_VER_MAP_FMT L"Local\\NetIDMgr_QueryVer_%lu" +/* dock values for window positioning */ +#define KHM_DOCK_NONE 0 +#define KHM_DOCK_TOPLEFT 1 +#define KHM_DOCK_TOPRIGHT 2 +#define KHM_DOCK_BOTTOMRIGHT 3 +#define KHM_DOCK_BOTTOMLEFT 4 +#define KHM_DOCK_AUTO 256 +#define KHM_DOCKF_DOCKHINT 0x0000ffff +#define KHM_DOCKF_XBORDER 0x00010000 #endif diff --git a/src/windows/identity/ui/newcredwnd.c b/src/windows/identity/ui/newcredwnd.c index 8265aff75..72c58a0a1 100644 --- a/src/windows/identity/ui/newcredwnd.c +++ b/src/windows/identity/ui/newcredwnd.c @@ -1326,12 +1326,6 @@ nc_layout_new_cred_window(khui_nc_wnd_data * ncd) { #define CW_PARAM DWLP_USER -static void -nc_add_control_row(khui_nc_wnd_data * d, - HWND label, - HWND input, - khui_control_size size); - static LRESULT nc_handle_wm_create(HWND hwnd, UINT uMsg, @@ -1522,7 +1516,7 @@ nc_handle_wm_create(HWND hwnd, MapDialogRect(ncd->dlg_main, &r); - /* center the new creds window over the main NetIDMgr window */ + /* position the new credentials dialog */ width = r.right - r.left; height = r.bottom - r.top; @@ -1540,7 +1534,18 @@ nc_handle_wm_create(HWND hwnd, height += (wr.bottom - wr.top) - (cr.bottom - cr.top); } - GetWindowRect(lpc->hwndParent, &r); + /* if the parent window is visible, we center the new credentials + dialog over the parent. Otherwise, we center it on the primary + display. */ + + if (IsWindowVisible(lpc->hwndParent)) { + GetWindowRect(lpc->hwndParent, &r); + } else { + if(!SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID) &r, 0)) { + /* failover to the window coordinates */ + GetWindowRect(lpc->hwndParent, &r); + } + } x = (r.right + r.left)/2 - width / 2; y = (r.top + r.bottom)/2 - height / 2; @@ -2042,9 +2047,12 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd, L"CredWindow\\Windows\\NewCred\\ForceToTop", &t)) && - t != 0 && - - !khm_is_dialog_active()) { + t != 0) { + /* it used to be that the above condition also called + !khm_is_dialog_active() to find out whether there + was a dialog active. If there was, we wouldn't try + to bring the new cred window to the foreground. But + that was not the behavior we want. */ /* if the main window is not visible, then the SetWindowPos() call is sufficient to bring the new creds window to the @@ -2378,7 +2386,9 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd, if (d->nc->n_prompts > 0 && d->nc->prompts[0]->hwnd_edit) { - SetFocus(d->nc->prompts[0]->hwnd_edit); + PostMessage(d->dlg_main, WM_NEXTDLGCTL, + (WPARAM) d->nc->prompts[0]->hwnd_edit, + MAKELPARAM(TRUE, 0)); } diff --git a/src/windows/identity/ui/notifier.c b/src/windows/identity/ui/notifier.c index 2d020bde9..530c22b56 100644 --- a/src/windows/identity/ui/notifier.c +++ b/src/windows/identity/ui/notifier.c @@ -35,6 +35,17 @@ #define KHUI_NOTIFIER_WINDOW L"KhuiNotifierMsgWindow" + +/* The commands that are available as default actions when the user + clicks the notification icon. */ + +khm_int32 khm_notifier_actions[] = { + KHUI_ACTION_OPEN_APP, + KHUI_ACTION_NEW_CRED +}; + +khm_size n_khm_notifier_actions = ARRAYLENGTH(khm_notifier_actions); + /* notifier message for notification icon */ #define KHUI_WM_NOTIFIER WM_COMMAND @@ -66,14 +77,23 @@ alert_show_list(alert_list * alist); static khm_int32 alert_enqueue(khui_alert * a); +static khm_boolean +alert_is_equal(khui_alert * a1, khui_alert * a2); + static void check_for_queued_alerts(void); +static void +show_queued_alerts(void); + static khm_int32 alert_consolidate(alert_list * alist, khui_alert * alert, khm_boolean add_from_queue); +static khm_int32 +get_default_notifier_action(void); + /* Globals */ /* window class registration atom for message only notifier window @@ -345,43 +365,7 @@ notifier_wnd_proc(HWND hwnd, break; case KMSG_ALERT_SHOW_QUEUED: - if (!ALERT_DISPLAYED()) { - - /* show next consolidated batch */ - alert_list alist; - int n; - - alert_list_init(&alist); - n = alert_consolidate(&alist, NULL, TRUE); - - if (n) { - if (n == 1) { - khui_alert_lock(alist.alerts[0]); - - if (alist.alerts[0]->title) { - alert_list_set_title(&alist, alist.alerts[0]->title); - } else { - wchar_t title[KHUI_MAXCCH_TITLE]; - LoadString(khm_hInstance, IDS_ALERT_DEFAULT, - title, ARRAYLENGTH(title)); - alert_list_set_title(&alist, title); - } - - khui_alert_unlock(alist.alerts[0]); - } else { - wchar_t title[KHUI_MAXCCH_TITLE]; - LoadString(khm_hInstance, IDS_ALERT_DEFAULT, - title, ARRAYLENGTH(title)); - alert_list_set_title(&alist, title); - } - - alert_show_list(&alist); - } - - alert_list_destroy(&alist); - - check_for_queued_alerts(); - } + show_queued_alerts(); break; case KMSG_ALERT_SHOW_MODAL: @@ -427,57 +411,99 @@ notifier_wnd_proc(HWND hwnd, { POINT pt; int menu_id; + khui_menu_def * mdef; + khui_action_ref * act; + khm_size i, n; + khm_int32 def_cmd; - GetCursorPos(&pt); + /* before we show the context menu, we need to make + sure that the default action for the notification + icon is present in the menu and that it is marked + as the default. */ - if (khm_is_main_window_visible()) + def_cmd = get_default_notifier_action(); + + if (khm_is_main_window_visible()) { menu_id = KHUI_MENU_ICO_CTX_NORMAL; - else + + if (def_cmd == KHUI_ACTION_OPEN_APP) + def_cmd = KHUI_ACTION_CLOSE_APP; + } else { menu_id = KHUI_MENU_ICO_CTX_MIN; + } + + mdef = khui_find_menu(menu_id); + +#ifdef DEBUG + assert(mdef); +#endif + n = khui_menu_get_size(mdef); + for (i=0; i < n; i++) { + act = khui_menu_get_action(mdef, i); + if (!(act->flags & KHUI_ACTIONREF_PACTION) && + (act->action == def_cmd)) + break; + } + + if (i < n) { + if (!(act->flags & KHUI_ACTIONREF_DEFAULT)) { + khui_menu_remove_action(mdef, i); + khui_menu_insert_action(mdef, i, def_cmd, KHUI_ACTIONREF_DEFAULT); + } else { + /* we are all set */ + } + } else { + /* the default action was not found on the context + menu */ +#ifdef DEBUG + assert(FALSE); +#endif + khui_menu_insert_action(mdef, 0, def_cmd, KHUI_ACTIONREF_DEFAULT); + } SetForegroundWindow(khm_hwnd_main); + GetCursorPos(&pt); khm_menu_show_panel(menu_id, pt.x, pt.y); PostMessage(khm_hwnd_main, WM_NULL, 0, 0); } break; - case WM_LBUTTONDOWN: - /* we actually wait for the WM_LBUTTONUP before doing - anything */ - break; - - case WM_LBUTTONUP: - /* fall through */ case NIN_SELECT: /* fall through */ case NIN_KEYSELECT: - khm_show_main_window(); + /* If there were any alerts waiting to be shown, we show + them. Otherwise we perform the default action. */ + khm_notify_icon_activate(); break; case NIN_BALLOONUSERCLICK: if (balloon_alert) { + khui_alert * a; + khm_notify_icon_change(KHERR_NONE); - khui_alert_lock(balloon_alert); + a = balloon_alert; + balloon_alert = NULL; - if ((balloon_alert->flags & KHUI_ALERT_FLAG_DEFACTION) && - (balloon_alert->flags & KHUI_ALERT_FLAG_REQUEST_BALLOON) && - balloon_alert->n_alert_commands > 0) { + khui_alert_lock(a); + + if ((a->flags & KHUI_ALERT_FLAG_DEFACTION) && + !(a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW) && + a->n_alert_commands > 0) { PostMessage(khm_hwnd_main, WM_COMMAND, - MAKEWPARAM(balloon_alert->alert_commands[0], + MAKEWPARAM(a->alert_commands[0], 0), 0); - } else if (balloon_alert->flags & + } else if (a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW) { khm_show_main_window(); - alert_show_normal(balloon_alert); + alert_show_normal(a); } - khui_alert_unlock(balloon_alert); - khui_alert_release(balloon_alert); - balloon_alert = NULL; + khui_alert_unlock(a); + khui_alert_release(a); } else { #ifdef DEBUG assert(FALSE); @@ -575,6 +601,12 @@ typedef struct tag_alerter_alert_data { HWND hwnd_buttons[KHUI_MAX_ALERT_COMMANDS]; /* handles for the command buttons */ + HWND hwnd_marker; + /* handle to the marker window used as + a tab-stop target when there are + not buttons associated with the + alert. */ + LDCL(struct tag_alerter_alert_data); } alerter_alert_data; @@ -596,9 +628,11 @@ typedef struct tag_alerter_wnd_data { in all the alerts being shown in this dialog. */ + int c_alert; /* current selected alert. */ + /* various metrics */ /* calculated during WM_CREATE */ - SIZE s_button; + SIZE s_button; /* minimum dimensions for command button */ SIZE s_margin; RECT r_text; /* only .left and .right are used. rest are 0 */ RECT r_title; /* only .left, .right and .bottom are used. .top=0 */ @@ -633,20 +667,14 @@ typedef struct tag_alerter_wnd_data { #define NTF_TITLE_WIDTH (NTF_WIDTH - NTF_MARGIN*2) #define NTF_TITLE_HEIGHT 10 -#define NTF_ICON_WIDTH 20 -#define NTF_ICON_HEIGHT 20 - -#define NTF_TEXT_X (NTF_MARGIN + NTF_ICON_WIDTH + NTF_MARGIN) -#define NTF_TEXT_WIDTH ((NTF_WIDTH - NTF_MARGIN) - NTF_TEXT_X) #define NTF_TEXT_PAD 2 -#define NTF_BUTTON_WIDTH ((NTF_TEXT_WIDTH - (KHUI_MAX_ALERT_COMMANDS - 1)*NTF_MARGIN) / KHUI_MAX_ALERT_COMMANDS) -#define NTF_BUTTON_HEIGHT 15 +#define NTF_BUTTON_HEIGHT 14 #define NTF_TIMEOUT 20000 #define ALERT_WINDOW_EX_SYLES (WS_EX_DLGMODALFRAME | WS_EX_CONTEXTHELP) -#define ALERT_WINDOW_STYLES (WS_DLGFRAME | WS_POPUPWINDOW | WS_CLIPCHILDREN) +#define ALERT_WINDOW_STYLES (WS_DLGFRAME | WS_POPUPWINDOW | WS_CLIPCHILDREN | DS_NOIDLEMSG) /* Control ids */ #define IDC_NTF_ALERTBIN 998 @@ -661,26 +689,51 @@ typedef struct tag_alerter_wnd_data { alert has no commands. */ #define ALERT_HAS_CMDS(a) ((a)->n_alert_commands > 1 || ((a)->n_alert_commands == 1 && (a)->alert_commands[0] != KHUI_PACTION_CLOSE)) +#define SCROLL_LINE_SIZE(d) ((d)->cy_max_wnd / 12) + static void add_alert_to_wnd_data(alerter_wnd_data * d, khui_alert * a) { - alerter_alert_data * adata; + alerter_alert_data * aiter; + khm_boolean exists = 0; - adata = PMALLOC(sizeof(*adata)); - ZeroMemory(adata, sizeof(*adata)); + khui_alert_lock(a); - adata->alert = a; - khui_alert_hold(a); + /* check if the alert is already there */ + aiter = QTOP(d); + while(aiter && !exists) { + if (aiter->alert) { + khui_alert_lock(aiter->alert); - khui_alert_lock(a); + if (alert_is_equal(aiter->alert, a)) { + exists = TRUE; + } + + khui_alert_unlock(aiter->alert); + } + + aiter = QNEXT(aiter); + } a->flags |= KHUI_ALERT_FLAG_DISPLAY_WINDOW; - a->displayed = TRUE; + if (!exists) + a->displayed = TRUE; khui_alert_unlock(a); - QPUT(d, adata); + if (!exists) { + alerter_alert_data * adata; + + adata = PMALLOC(sizeof(*adata)); + ZeroMemory(adata, sizeof(*adata)); + + adata->alert = a; + khui_alert_hold(a); + + QPUT(d, adata); + d->n_alerts ++; + } } static alerter_wnd_data * @@ -714,31 +767,33 @@ create_alerter_wnd_data(HWND hwnd, alert_list * l) { d->cx_wnd = DLG2SCNX(NTF_WIDTH); d->cy_max_wnd = DLG2SCNY(NTF_MAXHEIGHT); - d->s_button.cx = DLG2SCNX(NTF_BUTTON_WIDTH); - d->s_button.cy = DLG2SCNY(NTF_BUTTON_HEIGHT); - d->s_margin.cx = DLG2SCNX(NTF_MARGIN); d->s_margin.cy = DLG2SCNY(NTF_MARGIN); - d->r_text.left = DLG2SCNX(NTF_TEXT_X); - d->r_text.right = DLG2SCNX(NTF_TEXT_WIDTH + NTF_TEXT_X); - d->r_text.top = 0; - d->r_text.bottom = 0; - d->r_title.left = DLG2SCNX(NTF_TITLE_X); d->r_title.right = DLG2SCNX(NTF_TITLE_X + NTF_TITLE_WIDTH); d->r_title.top = 0; d->r_title.bottom = DLG2SCNY(NTF_TITLE_HEIGHT); - d->s_icon.cx = DLG2SCNX(NTF_ICON_WIDTH); - d->s_icon.cy = DLG2SCNY(NTF_ICON_HEIGHT); - d->s_pad.cx = DLG2SCNX(NTF_TEXT_PAD); d->s_pad.cy = DLG2SCNY(NTF_TEXT_PAD); + d->s_icon.cx = GetSystemMetrics(SM_CXICON); + d->s_icon.cy = GetSystemMetrics(SM_CYICON); + + d->r_text.left = d->s_margin.cx * 2 + d->s_icon.cx; + d->r_text.right = d->cx_wnd - d->s_margin.cx; + d->r_text.top = 0; + d->r_text.bottom = 0; + + d->s_button.cx = ((d->r_text.right - d->r_text.left) - (KHUI_MAX_ALERT_COMMANDS - 1) * d->s_margin.cx) / KHUI_MAX_ALERT_COMMANDS; + d->s_button.cy = DLG2SCNY(NTF_BUTTON_HEIGHT); + #undef DLG2SCNX #undef DLG2SCNY + d->c_alert = -1; + return d; } @@ -762,6 +817,9 @@ layout_alert(HDC hdc, alerter_wnd_data * d, y += d->s_margin.cy; + /* If there is a title and it differs from the title of the + alerter window, then we have to show the alert title + separately. */ if (adata->alert->title && wcscmp(adata->alert->title, d->caption)) { @@ -845,10 +903,19 @@ layout_alert(HDC hdc, alerter_wnd_data * d, if (ALERT_HAS_CMDS(adata->alert)) { khm_int32 i; - int x; + int x, width; + wchar_t caption[KHUI_MAXCCH_SHORT_DESC]; + size_t len; + SIZE s; + int skip_close; adata->has_commands = TRUE; + if (d->n_alerts > 1) + skip_close = TRUE; + else + skip_close = FALSE; + x = d->r_text.left; #ifdef DEBUG @@ -856,9 +923,38 @@ layout_alert(HDC hdc, alerter_wnd_data * d, #endif for (i=0; i < adata->alert->n_alert_commands; i++) { - SetRect(&adata->r_buttons[i], x, y, x + d->s_button.cx, y + d->s_button.cy); - x += d->s_button.cx + d->s_margin.cx; + if (adata->alert->alert_commands[i] == KHUI_PACTION_CLOSE && skip_close) { + SetRectEmpty(&adata->r_buttons[i]); + continue; + } + + caption[0] = L'\0'; + len = 0; + khm_get_action_caption(adata->alert->alert_commands[i], + caption, sizeof(caption)); + StringCchLength(caption, ARRAYLENGTH(caption), &len); + + if (!GetTextExtentPoint32(hdc, caption, (int) len, &s)) { + width = d->s_button.cx; + } else { + width = s.cx + d->s_margin.cx * 2; + } + + if (width < d->s_button.cx) + width = d->s_button.cx; + else if (width > (d->r_text.right - d->r_text.left)) + width = d->r_text.right - d->r_text.left; + + if (x + width > d->r_text.right) { + /* new line */ + x = d->r_text.left; + y += d->s_button.cy + d->s_pad.cy; + } + + SetRect(&adata->r_buttons[i], x, y, x + width, y + d->s_button.cy); + + x += width + d->s_margin.cx; } y += d->s_button.cy + d->s_margin.cy; @@ -871,6 +967,100 @@ layout_alert(HDC hdc, alerter_wnd_data * d, } +static void +pick_title_for_alerter_window(alerter_wnd_data * d) { + alerter_alert_data * adata; + wchar_t caption[KHUI_MAXCCH_TITLE]; + khm_boolean common_caption = TRUE; + khui_alert_type ctype = KHUI_ALERTTYPE_NONE; + khm_boolean common_type = TRUE; + + /* - If all the alerts have the same title, then we use the common + title. + + - If all the alerts are of the same type, then we pick a title + that is suitable for the type. + + - All else fails, we use a default caption for the window. + */ + + caption[0] = L'\0'; + adata = QTOP(d); + while (adata && (common_caption || common_type)) { + + if (adata->alert) { + khui_alert_lock(adata->alert); + + if (common_caption) { + if (caption[0] == L'\0') { + if (adata->alert->title) + StringCbCopy(caption, sizeof(caption), adata->alert->title); + } else if (adata->alert->title && + wcscmp(caption, adata->alert->title)) { + common_caption = FALSE; + } + } + + if (common_type) { + if (ctype == KHUI_ALERTTYPE_NONE) + ctype = adata->alert->alert_type; + else if (ctype != adata->alert->alert_type) + common_type = FALSE; + } + + khui_alert_unlock(adata->alert); + } + + adata = QNEXT(adata); + } + + /* just in case someone changes d->caption to a pointer from an + array */ +#ifdef DEBUG + assert(sizeof(d->caption) > sizeof(wchar_t *)); +#endif + + if (common_caption && caption[0] != L'\0') { + StringCbCopy(d->caption, sizeof(d->caption), caption); + } else if (common_type && ctype != KHUI_ALERTTYPE_NONE) { + switch(ctype) { + case KHUI_ALERTTYPE_PLUGIN: + LoadString(khm_hInstance, IDS_ALERTTYPE_PLUGIN, + d->caption, ARRAYLENGTH(d->caption)); + break; + + case KHUI_ALERTTYPE_EXPIRE: + LoadString(khm_hInstance, IDS_ALERTTYPE_EXPIRE, + d->caption, ARRAYLENGTH(d->caption)); + break; + + case KHUI_ALERTTYPE_RENEWFAIL: + LoadString(khm_hInstance, IDS_ALERTTYPE_RENEWFAIL, + d->caption, ARRAYLENGTH(d->caption)); + break; + + case KHUI_ALERTTYPE_ACQUIREFAIL: + LoadString(khm_hInstance, IDS_ALERTTYPE_ACQUIREFAIL, + d->caption, ARRAYLENGTH(d->caption)); + break; + + case KHUI_ALERTTYPE_CHPW: + LoadString(khm_hInstance, IDS_ALERTTYPE_CHPW, + d->caption, ARRAYLENGTH(d->caption)); + break; + + default: + LoadString(khm_hInstance, IDS_ALERT_DEFAULT, + d->caption, ARRAYLENGTH(d->caption)); + } + } else { + LoadString(khm_hInstance, IDS_ALERT_DEFAULT, + d->caption, ARRAYLENGTH(d->caption)); + } + + SetWindowText(d->hwnd, d->caption); +} + static void estimate_alerter_wnd_sizes(alerter_wnd_data * d) { HDC hdc; @@ -879,6 +1069,8 @@ estimate_alerter_wnd_sizes(alerter_wnd_data * d) { alerter_alert_data * adata; + pick_title_for_alerter_window(d); + hdc = GetDC(d->hwnd); #ifdef DEBUG assert(hdc); @@ -928,11 +1120,17 @@ layout_command_buttons(alerter_wnd_data * d) { goto done; for (i=0; i < adata->n_cmd_buttons; i++) { - if (IsRectEmpty(&adata->r_buttons[i]) || - adata->hwnd_buttons[i] == NULL) { -#ifdef DEBUG - assert(FALSE); -#endif + if (IsRectEmpty(&adata->r_buttons[i])) { + /* the button is no longer needed */ + if (adata->hwnd_buttons[i] != NULL) { + DestroyWindow(adata->hwnd_buttons[i]); + adata->hwnd_buttons[i] = NULL; + } + + continue; + } + + if (adata->hwnd_buttons[i] == NULL) { continue; } @@ -962,6 +1160,7 @@ setup_alerter_window_controls(alerter_wnd_data * d) { RECT r_client; RECT r_parent; HWND hw_parent; + HWND hw_focus = NULL; BOOL close_button = FALSE; BOOL scrollbar = FALSE; BOOL redraw_scollbar = FALSE; @@ -984,7 +1183,7 @@ setup_alerter_window_controls(alerter_wnd_data * d) { r_alerts.bottom = d->cy_max_wnd; CopyRect(&r_client, &r_alerts); - r_client.bottom += d->s_margin.cy * 2 + d->s_button.cy; + r_client.bottom += d->s_margin.cy + d->s_button.cy + d->s_pad.cy; close_button = TRUE; if (d->scroll_top > d->s_alerts.cy - d->cy_max_wnd) @@ -1015,7 +1214,7 @@ setup_alerter_window_controls(alerter_wnd_data * d) { if (d->hw_bin == NULL) { d->hw_bin = CreateWindowEx(WS_EX_CONTROLPARENT, MAKEINTATOM(atom_alert_bin), - L"", + L"Alert Container", WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE | ((scrollbar)? WS_VSCROLL : 0), @@ -1068,9 +1267,13 @@ setup_alerter_window_controls(alerter_wnd_data * d) { if (adata->has_commands) { int i; wchar_t caption[KHUI_MAXCCH_SHORT_DESC]; - khui_action * action; RECT r; + if (adata->hwnd_marker) { + DestroyWindow(adata->hwnd_marker); + adata->hwnd_marker = NULL; + } + khui_alert_lock(adata->alert); adata->n_cmd_buttons = adata->alert->n_alert_commands; @@ -1079,6 +1282,16 @@ setup_alerter_window_controls(alerter_wnd_data * d) { n_buttons ++; + if (IsRectEmpty(&adata->r_buttons[i])) { + /* this button is not necessary */ + if (adata->hwnd_buttons[i]) { + DestroyWindow(adata->hwnd_buttons[i]); + adata->hwnd_buttons[i] = NULL; + } + + continue; + } + if (adata->hwnd_buttons[i] != NULL) { /* already there */ CopyRect(&r, &adata->r_buttons[i]); @@ -1093,29 +1306,14 @@ setup_alerter_window_controls(alerter_wnd_data * d) { last_window = adata->hwnd_buttons[i]; - continue; - } - - action = khui_find_action(adata->alert->alert_commands[i]); + if (hw_focus == NULL) + hw_focus = adata->hwnd_buttons[i]; - if (action == NULL) { -#ifdef DEBUG - assert(FALSE); -#endif continue; } - if (action->caption) - StringCbCopy(caption, sizeof(caption), action->caption); - else if (action->is_caption) - LoadString(khm_hInstance, action->is_caption, caption, - ARRAYLENGTH(caption)); - else { -#ifdef DEBUG - assert(FALSE); -#endif - caption[0] = L'\0'; - } + khm_get_action_caption(adata->alert->alert_commands[i], + caption, sizeof(caption)); CopyRect(&r, &adata->r_buttons[i]); OffsetRect(&r, 0, y); @@ -1124,7 +1322,7 @@ setup_alerter_window_controls(alerter_wnd_data * d) { CreateWindowEx(0, L"BUTTON", caption, - WS_CHILD | WS_TABSTOP, + WS_CHILD | WS_TABSTOP | BS_NOTIFY, r.left, r.top, r.right - r.left, r.bottom - r.top, @@ -1135,18 +1333,71 @@ setup_alerter_window_controls(alerter_wnd_data * d) { #ifdef DEBUG assert(adata->hwnd_buttons[i]); #endif + + if (d->hfont) { + SendMessage(adata->hwnd_buttons[i], WM_SETFONT, + (WPARAM) d->hfont, FALSE); + } + SetWindowPos(adata->hwnd_buttons[i], last_window, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + last_window = adata->hwnd_buttons[i]; + + if (hw_focus == NULL) + hw_focus = adata->hwnd_buttons[i]; } khui_alert_unlock(adata->alert); + } else { + int i; + + /* Destroy any buttons that belong to the alert. We + might have some left over, if there were command + belonging to the alert that were ignored.*/ + + for (i=0; i < adata->n_cmd_buttons; i++) { + if (adata->hwnd_buttons[i]) { + DestroyWindow(adata->hwnd_buttons[i]); + adata->hwnd_buttons[i] = NULL; + } + } + + adata->n_cmd_buttons = 0; + + if (adata->hwnd_marker == NULL) { + adata->hwnd_marker = + CreateWindowEx(0, + L"BUTTON", + L"Marker", + WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_NOTIFY, + -10, 0, + 5, 5, + d->hw_bin, + (HMENU) (INT_PTR) IDC_FROM_IDX(idx, 0), + khm_hInstance, + NULL); +#ifdef DEBUG + assert(adata->hwnd_marker); +#endif + } + + SetWindowPos(adata->hwnd_marker, last_window, + 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_NOMOVE | SWP_NOSIZE); + + last_window = adata->hwnd_marker; + + if (hw_focus == NULL) + hw_focus = adata->hwnd_marker; } y += adata->r_alert.bottom; adata = QNEXT(adata); + idx++; } d->n_cmd_buttons = n_buttons; @@ -1154,29 +1405,14 @@ setup_alerter_window_controls(alerter_wnd_data * d) { if (close_button) { if (d->hw_close == NULL) { - khui_action * close_action; wchar_t caption[256]; - close_action = khui_find_action(KHUI_PACTION_CLOSE); -#ifdef DEBUG - assert(close_action); -#endif - if (close_action->caption) - StringCbCopy(caption, sizeof(caption), close_action->caption); - else if (close_action->is_caption) - LoadString(khm_hInstance, close_action->is_caption, caption, - ARRAYLENGTH(caption)); - else { -#ifdef DEBUG - assert(FALSE); -#endif - caption[0] = L'\0'; - } + khm_get_action_caption(KHUI_PACTION_CLOSE, caption, sizeof(caption)); d->hw_close = CreateWindowEx(0, L"BUTTON", caption, - WS_CHILD | BS_DEFPUSHBUTTON, + WS_CHILD | BS_DEFPUSHBUTTON | WS_TABSTOP | BS_NOTIFY, 0,0,100,100, d->hwnd, (HMENU) IDC_NTF_CLOSE, @@ -1204,6 +1440,15 @@ setup_alerter_window_controls(alerter_wnd_data * d) { SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_SHOWWINDOW); } + + if (hw_focus == NULL) + hw_focus = d->hw_close; + + } else { + if (d->hw_close != NULL) { + DestroyWindow(d->hw_close); + d->hw_close = NULL; + } } CopyRect(&r_window, &r_client); @@ -1230,6 +1475,242 @@ setup_alerter_window_controls(alerter_wnd_data * d) { SWP_SHOWWINDOW | SWP_NOOWNERZORDER); } + + if (hw_focus != NULL) + PostMessage(d->hwnd, WM_NEXTDLGCTL, (WPARAM) hw_focus, MAKELPARAM(TRUE, 0)); +} + +static void +scroll_to_position(alerter_wnd_data * d, int new_pos, khm_boolean redraw_scrollbar) { + int delta; + SCROLLINFO si; + HWND hwnd = d->hw_bin; + + if (new_pos < 0) + new_pos = 0; + else if (new_pos > d->s_alerts.cy - d->cy_max_wnd) + new_pos = d->s_alerts.cy - d->cy_max_wnd; + + if (new_pos == d->scroll_top) + return; + + delta = d->scroll_top - new_pos; + + d->scroll_top -= delta; + + ScrollWindowEx(hwnd, 0, delta, + NULL, NULL, NULL, NULL, + SW_INVALIDATE | SW_ERASE); + + layout_command_buttons(d); + + ZeroMemory(&si, sizeof(si)); + + si.fMask = SIF_POS; + si.nPos = d->scroll_top; + + SetScrollInfo(hwnd, SB_VERT, &si, redraw_scrollbar); +} + +static void +select_alert(alerter_wnd_data * d, int alert) { + + int y; + RECT old_sel, new_sel; + alerter_alert_data * adata; + int idx; + + if (d->n_alerts == 1 || + alert < 0 || + alert > d->n_alerts || + d->c_alert == alert) + return; + + SetRectEmpty(&old_sel); + SetRectEmpty(&new_sel); + idx = 0; y = -d->scroll_top; + adata = QTOP(d); + while(adata && (idx <= d->c_alert || idx <= alert)) { + + if (idx == d->c_alert) { + CopyRect(&old_sel, &adata->r_alert); + OffsetRect(&old_sel, 0, y); + } + + if (idx == alert) { + CopyRect(&new_sel, &adata->r_alert); + OffsetRect(&new_sel, 0, y); + } + + y += adata->r_alert.bottom; + idx ++; + adata = QNEXT(adata); + } + + d->c_alert = alert; + if (!IsRectEmpty(&old_sel)) + InvalidateRect(d->hw_bin, &old_sel, TRUE); + if (!IsRectEmpty(&new_sel)) + InvalidateRect(d->hw_bin, &new_sel, TRUE); +} + +static void +ensure_command_is_visible(alerter_wnd_data * d, int id) { + int alert_idx; + int y = 0; + alerter_alert_data * adata; + int new_pos = 0; + + alert_idx = ALERT_FROM_IDC(id); + +#ifdef DEBUG + assert(alert_idx >= 0 && alert_idx < d->n_alerts); +#endif + if (alert_idx >= d->n_alerts || alert_idx < 0) + return; + + adata = QTOP(d); + while(adata && alert_idx > 0) { + y += adata->r_alert.bottom; + alert_idx--; + adata = QNEXT(adata); + } + +#ifdef DEBUG + assert(alert_idx == 0); + assert(adata); + assert(adata->alert); +#endif + if (adata == NULL || alert_idx != 0) + return; + + new_pos = d->scroll_top; + if (y < d->scroll_top) { + new_pos = y; + } else if (y + adata->r_alert.bottom > d->scroll_top + d->cy_max_wnd) { + new_pos = y + adata->r_alert.bottom - d->cy_max_wnd; + } + + if (new_pos != d->scroll_top) + scroll_to_position(d, new_pos, TRUE); + + select_alert(d, ALERT_FROM_IDC(id)); +} + +static void +handle_mouse_select(alerter_wnd_data * d, int mouse_x, int mouse_y) { + int y; + alerter_alert_data * adata; + + y = -d->scroll_top; + adata = QTOP(d); + while(adata) { + if (y <= mouse_y && (y + adata->r_alert.bottom) > mouse_y) { + HWND hw = NULL; + + if (adata->n_cmd_buttons > 0) + hw = adata->hwnd_buttons[0]; + else + hw = adata->hwnd_marker; + + if (hw && !IsWindowEnabled(hw)) + hw = GetNextDlgTabItem(d->hwnd, hw, FALSE); + + if (hw) + PostMessage(d->hwnd, WM_NEXTDLGCTL, (WPARAM) hw, MAKELPARAM(TRUE, 0)); + + return; + } + + y += adata->r_alert.bottom; + adata = QNEXT(adata); + } +} + +static void +process_command_button(alerter_wnd_data * d, int id) { + int alert_idx; + int cmd_idx; + khm_int32 flags = 0; + khm_int32 cmd = 0; + alerter_alert_data * adata; + int i; + + alert_idx = ALERT_FROM_IDC(id); + cmd_idx = BUTTON_FROM_IDC(id); + +#ifdef DEBUG + assert(alert_idx >= 0 && alert_idx < d->n_alerts); +#endif + if (alert_idx >= d->n_alerts || alert_idx < 0) + return; + + adata = QTOP(d); + while(adata && alert_idx > 0) { + alert_idx--; + adata = QNEXT(adata); + } + +#ifdef DEBUG + assert(alert_idx == 0); + assert(adata); + assert(adata->alert); +#endif + if (adata == NULL || alert_idx != 0) + return; + + khui_alert_lock(adata->alert); +#ifdef DEBUG + assert(cmd_idx >= 0 && cmd_idx < adata->alert->n_alert_commands); +#endif + + if (cmd_idx >= 0 && cmd_idx < adata->alert->n_alert_commands) { + cmd = adata->alert->alert_commands[cmd_idx]; + } + + flags = adata->alert->flags; + + adata->alert->response = cmd; + + khui_alert_unlock(adata->alert); + + /* if we were supposed to dispatch the command, do so */ + if (cmd != 0 && + cmd != KHUI_PACTION_CLOSE && + (flags & KHUI_ALERT_FLAG_DISPATCH_CMD)) { + PostMessage(khm_hwnd_main, WM_COMMAND, + MAKEWPARAM(cmd, 0), 0); + } + + /* if this was the only alert in the alert group and its close + button was clicked, we close the alert window. Otherwise, the + alert window creates its own close button that closes the + window. */ + if (d->n_alerts == 1) { + PostMessage(d->hwnd, WM_CLOSE, 0, 0); + } + + /* While we are at it, we should disable the buttons for this + alert since we have already dispatched the command for it. */ + if (cmd != 0) { + HWND hw_focus = GetFocus(); + khm_boolean focus_trapped = FALSE; + + for (i=0; i < adata->n_cmd_buttons; i++) { + if (adata->hwnd_buttons[i]) { + if (hw_focus == adata->hwnd_buttons[i]) + focus_trapped = TRUE; + + EnableWindow(adata->hwnd_buttons[i], FALSE); + } + } + + if (focus_trapped) { + hw_focus = GetNextDlgTabItem(d->hwnd, hw_focus, FALSE); + if (hw_focus) + PostMessage(d->hwnd, WM_NEXTDLGCTL, (WPARAM) hw_focus, MAKELPARAM(TRUE,0)); + } + } } static void @@ -1245,8 +1726,6 @@ destroy_alerter_wnd_data(alerter_wnd_data * d) { khui_alert_lock(adata->alert); - adata->alert->flags &= ~KHUI_ALERT_FLAG_DISPLAY_WINDOW; - adata->alert->displayed = FALSE; khui_alert_unlock(adata->alert); @@ -1320,6 +1799,32 @@ alert_can_consolidate(khui_alert * ref, return FALSE; } +/* both a1 and a2 must be locked */ +static khm_boolean +alert_is_equal(khui_alert * a1, khui_alert * a2) { + khm_int32 i; + + if ((a1->severity != a2->severity) || + (a1->n_alert_commands != a2->n_alert_commands) || + (a1->title && (!a2->title || wcscmp(a1->title, a2->title))) || + (!a1->title && a2->title) || + (a1->message && (!a2->message || wcscmp(a1->message, a2->message))) || + (!a1->message && a2->message) || + (a1->suggestion && (!a2->suggestion || wcscmp(a1->suggestion, a2->suggestion))) || + (!a1->suggestion && a2->suggestion)) { + + return FALSE; + + } + + for (i=0; i < a1->n_alert_commands; i++) { + if (a1->alert_commands[i] != a2->alert_commands[i]) + return FALSE; + } + + return TRUE; +} + /* the return value is the number of alerts added to alist */ static khm_int32 alert_consolidate(alert_list * alist, @@ -1356,10 +1861,10 @@ alert_consolidate(alert_list * alist, if (n_added == 0 && add_from_queue) { khui_alert * q; - int s, i; + int i; - s = alert_queue_get_size(); - for (i=0; i < s && n_added == 0; i++) { + queue_size = alert_queue_get_size(); + for (i=0; i < queue_size && n_added == 0; i++) { q = alert_queue_get_alert_by_pos(i); if (q) { khui_alert_lock(q); @@ -1414,7 +1919,12 @@ alert_consolidate(alert_list * alist, alert_queue_delete_alert(a); alert_list_add_alert(alist, a); n_added ++; - queue_size = alert_queue_get_size(); + + queue_size--; + i--; +#ifdef DEBUG + assert(alert_queue_get_size() == queue_size); +#endif } khui_alert_unlock(a); khui_alert_release(a); @@ -1657,6 +2167,49 @@ alert_show(khui_alert * a) { return KHM_ERROR_SUCCESS; } +static void +show_queued_alerts(void) { + + if (!ALERT_DISPLAYED()) { + + /* show next consolidated batch */ + alert_list alist; + int n; + + alert_list_init(&alist); + n = alert_consolidate(&alist, NULL, TRUE); + + if (n) { + if (n == 1) { + khui_alert_lock(alist.alerts[0]); + + if (alist.alerts[0]->title) { + alert_list_set_title(&alist, alist.alerts[0]->title); + } else { + wchar_t title[KHUI_MAXCCH_TITLE]; + LoadString(khm_hInstance, IDS_ALERT_DEFAULT, + title, ARRAYLENGTH(title)); + alert_list_set_title(&alist, title); + } + + khui_alert_unlock(alist.alerts[0]); + } else { + wchar_t title[KHUI_MAXCCH_TITLE]; + LoadString(khm_hInstance, IDS_ALERT_DEFAULT, + title, ARRAYLENGTH(title)); + alert_list_set_title(&alist, title); + } + + alert_show_list(&alist); + } + + alert_list_destroy(&alist); + + check_for_queued_alerts(); + } +} + + static void check_for_queued_alerts(void) { if (!is_alert_queue_empty()) { @@ -1684,6 +2237,12 @@ check_for_queued_alerts(void) { khm_statusbar_set_part(KHUI_SBPART_NOTICE, hi, a->title); + } else { + khm_statusbar_set_part(KHUI_SBPART_NOTICE, + NULL, NULL); +#ifdef DEBUG + DebugBreak(); +#endif } khui_alert_unlock(a); @@ -1763,29 +2322,6 @@ alerter_wnd_proc(HWND hwnd, } break; -#if 0 - case WM_PAINT: - { - RECT r_update; - PAINTSTRUCT ps; - HDC hdc; - alerter_wnd_data * d; - - if(!GetUpdateRect(hwnd, &r_update, TRUE)) - return FALSE; - - d = (alerter_wnd_data *)(LONG_PTR) - GetWindowLongPtr(hwnd, NTF_PARAM); - - hdc = BeginPaint(hwnd, &ps); - - EndPaint(hwnd, &ps); - - return FALSE; - } - break; /* not reached */ -#endif - case WM_COMMAND: { alerter_wnd_data * d; @@ -1801,20 +2337,25 @@ alerter_wnd_proc(HWND hwnd, DestroyWindow(hwnd); -#ifdef FORLATER - if (LOWORD(wParam) == KHUI_PACTION_NEXT) { - kmq_post_message(KMSG_ALERT, KMSG_ALERT_SHOW_QUEUED, 0, 0); - } -#endif return 0; } } } break; + + case WM_CLOSE: + { + khm_leave_modal(); + + DestroyWindow(hwnd); + + return 0; + } } + /* Since this is a custom built dialog, we use DefDlgProc instead + of DefWindowProc. */ return DefDlgProc(hwnd, uMsg, wParam, lParam); - //return DefWindowProc(hwnd, uMsg, wParam, lParam); } static LRESULT CALLBACK @@ -1841,6 +2382,11 @@ alert_bin_wnd_proc(HWND hwnd, } return 0; + case WM_ERASEBKGND: + /* we erase the background when we are drawing the alerts + anyway. */ + return 0; + case WM_PRINTCLIENT: in_printclient = TRUE; /* fallthrough */ @@ -1854,6 +2400,7 @@ alert_bin_wnd_proc(HWND hwnd, alerter_wnd_data * d; alerter_alert_data * adata; size_t len; + int idx; d = (alerter_wnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, GWLP_USERDATA); #ifdef DEBUG @@ -1871,6 +2418,7 @@ alert_bin_wnd_proc(HWND hwnd, assert(d->hfont); #endif +#ifdef ALERT_STATIC_BACKGROUND if (in_printclient || ps.fErase) { HBRUSH hb_background; @@ -1879,29 +2427,78 @@ alert_bin_wnd_proc(HWND hwnd, GetClientRect(hwnd, &r); FillRect(hdc, &r, hb_background); } +#endif SetBkMode(hdc, TRANSPARENT); hf_old = SelectFont(hdc, d->hfont); y = -d->scroll_top; - + idx = 0; /* go through the alerts and display them */ adata = QTOP(d); while(adata) { khui_alert * a; +#ifndef ALERT_STATIC_BACKGROUND +#define MIX_C(v1, v2, p) (((int)v1) * p + (((int) v2) * (256 - p))) +#define ALPHA 50 + if (in_printclient || ps.fErase) { + TRIVERTEX v[2]; + GRADIENT_RECT gr; + COLORREF clr; + COLORREF clr2; + + CopyRect(&r, &adata->r_alert); + OffsetRect(&r, 0, y); + + v[0].x = r.left; + v[0].y = r.top; + v[0].Alpha = 0; + + v[1].x = r.right; + v[1].y = r.bottom; + v[1].Alpha = 0; + + if (idx == d->c_alert) { + clr = GetSysColor(COLOR_HOTLIGHT); + + clr2 = GetSysColor(COLOR_BTNHIGHLIGHT); + v[0].Red = MIX_C(GetRValue(clr), GetRValue(clr2), ALPHA); + v[0].Green = MIX_C(GetGValue(clr), GetGValue(clr2), ALPHA); + v[0].Blue = MIX_C(GetBValue(clr), GetBValue(clr2), ALPHA); + + clr2 = GetSysColor(COLOR_BTNFACE); + v[1].Red = MIX_C(GetRValue(clr), GetRValue(clr2), ALPHA); + v[1].Green = MIX_C(GetGValue(clr), GetGValue(clr2), ALPHA); + v[1].Blue = MIX_C(GetBValue(clr), GetBValue(clr2), ALPHA); + } else { + clr = GetSysColor(COLOR_BTNHIGHLIGHT); + v[0].Red = ((int)GetRValue(clr)) << 8; + v[0].Green = ((int)GetGValue(clr)) << 8; + v[0].Blue = ((int)GetBValue(clr)) << 8; + + clr = GetSysColor(COLOR_BTNFACE); + v[1].Red = ((int)GetRValue(clr)) << 8; + v[1].Green = ((int)GetGValue(clr)) << 8; + v[1].Blue = ((int)GetBValue(clr)) << 8; + } + + gr.UpperLeft = 0; + gr.LowerRight = 1; + GradientFill(hdc, v, 2, &gr, 1, GRADIENT_FILL_RECT_V); + } +#undef ALPHA +#undef MIX_C +#endif + a = adata->alert; #ifdef DEBUG assert(a != NULL); #endif khui_alert_lock(a); - /* if the alert has a title and it's different from - the original caption for the alert dialog, we have - to display the title. */ - if (a->title && - wcscmp(a->title, d->caption)) { + if (!IsRectEmpty(&adata->r_title)) { CopyRect(&r, &adata->r_title); OffsetRect(&r, 0, y); @@ -1980,6 +2577,7 @@ alert_bin_wnd_proc(HWND hwnd, khui_alert_unlock(a); y += adata->r_alert.bottom; + idx++; adata = QNEXT(adata); } @@ -1994,16 +2592,108 @@ alert_bin_wnd_proc(HWND hwnd, case WM_VSCROLL: { + alerter_wnd_data * d; + int new_pos = 0; + SCROLLINFO si; + + d = (alerter_wnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, GWLP_USERDATA); +#ifdef DEBUG + assert(d); +#endif + if (d == NULL) + break; /* we can't handle the message */ + + ZeroMemory(&si, sizeof(si)); + + switch(LOWORD(wParam)) { + case SB_BOTTOM: + new_pos = d->s_alerts.cy - d->cy_max_wnd; + break; + + case SB_LINEDOWN: + new_pos = d->scroll_top + SCROLL_LINE_SIZE(d); + break; + + case SB_LINEUP: + new_pos = d->scroll_top - SCROLL_LINE_SIZE(d); + break; + + case SB_PAGEDOWN: + new_pos = d->scroll_top + d->cy_max_wnd; + break; + + case SB_PAGEUP: + new_pos = d->scroll_top - d->cy_max_wnd; + break; + + case SB_THUMBPOSITION: + case SB_THUMBTRACK: + si.fMask = SIF_TRACKPOS; + GetScrollInfo(hwnd, SB_VERT, &si); + new_pos = si.nTrackPos; + break; + + case SB_TOP: + new_pos = 0; + break; + + case SB_ENDSCROLL: + si.fMask = SIF_POS; + si.nPos = d->scroll_top; + SetScrollInfo(hwnd, SB_VERT, &si, TRUE); + return 0; + + default: + return 0; + } + + scroll_to_position(d, new_pos, FALSE); } return 0; case WM_COMMAND: { + alerter_wnd_data * d; + + d = (alerter_wnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, GWLP_USERDATA); +#ifdef DEBUG + assert(d); +#endif + if (d == NULL) + break; + + if (HIWORD(wParam) == BN_CLICKED) { + process_command_button(d, LOWORD(wParam)); + return 0; + } else if (HIWORD(wParam) == BN_SETFOCUS) { + ensure_command_is_visible(d, LOWORD(wParam)); + return 0; + } + } + break; + + case WM_LBUTTONUP: + { + alerter_wnd_data * d; + int x,y; + + d = (alerter_wnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, GWLP_USERDATA); +#ifdef DEBUG + assert(d); +#endif + if (d == NULL) + break; + + x = GET_X_LPARAM(lParam); + y = GET_Y_LPARAM(lParam); + + handle_mouse_select(d, x, y); } break; case WM_DESTROY: { + /* nothing needs to be done here */ } return 0; } @@ -2090,11 +2780,11 @@ void khm_notify_icon_add(void) { Shell_NotifyIcon(NIM_ADD, &ni); + DestroyIcon(ni.hIcon); + ni.cbSize = sizeof(ni); ni.uVersion = NOTIFYICON_VERSION; Shell_NotifyIcon(NIM_SETVERSION, &ni); - - DestroyIcon(ni.hIcon); } void @@ -2222,6 +2912,76 @@ void khm_notify_icon_remove(void) { Shell_NotifyIcon(NIM_DELETE, &ni); } +static khm_int32 +get_default_notifier_action(void) { + khm_int32 def_cmd = KHUI_ACTION_OPEN_APP; + khm_handle csp_cw = NULL; + khm_size i; + + if (KHM_FAILED(khc_open_space(NULL, L"CredWindow", KHM_PERM_READ, + &csp_cw))) + def_cmd; + + khc_read_int32(csp_cw, L"NotificationAction", &def_cmd); + + khc_close_space(csp_cw); + + for (i=0; i < n_khm_notifier_actions; i++) { + if (khm_notifier_actions[i] == def_cmd) + break; + } + + if (i < n_khm_notifier_actions) + return def_cmd; + else + return KHUI_ACTION_OPEN_APP; +} + +void khm_notify_icon_activate(void) { + /* if there are any notifications waiting to be shown and there + are no alerts already being shown, we show them. Otherwise we + execute the default action. */ + + khm_notify_icon_change(KHERR_NONE); + + if (!is_alert_queue_empty() && !ALERT_DISPLAYED()) { + + khm_show_main_window(); + show_queued_alerts(); + + } else if (balloon_alert != NULL && khui_alert_windows == NULL) { + + khui_alert * a; + + a = balloon_alert; + balloon_alert = NULL; + + khui_alert_lock(a); + if (balloon_alert->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW) { + alert_show_normal(a); + } + khui_alert_unlock(a); + khui_alert_release(a); + + } else { + khm_int32 cmd = 0; + + cmd = get_default_notifier_action(); + + if (cmd == KHUI_ACTION_OPEN_APP) { + if (khm_is_main_window_visible()) { + khm_hide_main_window(); + } else { + khm_show_main_window(); + } + } else { + khui_action_trigger(cmd, NULL); + } + + check_for_queued_alerts(); + } +} + /********************************************************************* Initialization **********************************************************************/ @@ -2294,3 +3054,29 @@ void khm_exit_notifier(void) notifier_ready = FALSE; } + +/***** testing *****/ + +void +create_test_alerts(void) { + + khui_alert * a; + int i; + + for (i=0; i < 50; i++) { + wchar_t buf[128]; + + StringCbPrintf(buf, sizeof(buf), L"Foo bar baz. This is alert number %d", i); + khui_alert_create_simple(L"Title", buf, KHERR_INFO, &a); + khui_alert_set_type(a, KHUI_ALERTTYPE_PLUGIN); + + khui_alert_add_command(a, KHUI_ACTION_NEW_CRED); + khui_alert_add_command(a, KHUI_ACTION_CLOSE_APP); + khui_alert_add_command(a, KHUI_ACTION_PROPERTIES); + khui_alert_add_command(a, KHUI_ACTION_OPEN_APP); + khui_alert_add_command(a, KHUI_ACTION_VIEW_REFRESH); + + khui_alert_show(a); + khui_alert_release(a); + } +} diff --git a/src/windows/identity/ui/notifier.h b/src/windows/identity/ui/notifier.h index 140d02c44..a42e63f1f 100644 --- a/src/windows/identity/ui/notifier.h +++ b/src/windows/identity/ui/notifier.h @@ -36,6 +36,9 @@ enum khm_notif_expstate { KHM_NOTIF_EXP }; +extern khm_int32 khm_notifier_actions[]; +extern khm_size n_khm_notifier_actions; + void khm_init_notifier(void); @@ -54,4 +57,7 @@ khm_notify_icon_balloon(khm_int32 severity, void khm_notify_icon_expstate(enum khm_notif_expstate expseverity); +void +khm_notify_icon_activate(void); + #endif diff --git a/src/windows/identity/ui/resource.h b/src/windows/identity/ui/resource.h index 69d35b316..d7c9f27a6 100644 --- a/src/windows/identity/ui/resource.h +++ b/src/windows/identity/ui/resource.h @@ -305,6 +305,20 @@ #define IDS_NCN_IDENT_UNKNOWN 299 #define IDS_REMOTE_FAIL 300 #define IDS_REMOTE_FAIL_TITLE 301 +#define IDS_NOTIFICATION_ACTIONS 302 +#define IDS_IDACTION_NEW 302 +#define IDS_IDACTIONT_NEW 303 +#define IDS_IDACTIONT_RENEW 304 +#define IDS_IDACTIONT_DESTROY 305 +#define IDS_ALERTTYPE_PLUGIN 306 +#define IDS_ALERTTYPE_EXPIRE 307 +#define IDS_ALERTTYPE_RENEWFAIL 308 +#define IDS_ALERTTYPE_ACQUIREFAIL 309 +#define IDS_ALERTTYPE_CHPW 310 +#define IDS_ACTION_LAYOUT_MINI 311 +#define IDS_IDEXPDISP_NOCRED 312 +#define IDS_IDEXPDISP_1CRED 313 +#define IDS_IDEXPDISP_NCRED 314 #define IDC_NC_USERNAME 1007 #define IDC_NC_PASSWORD 1008 #define IDC_NC_CREDTEXT_LABEL 1009 @@ -409,6 +423,8 @@ #define IDC_SM_LBL 1137 #define IDC_MED_LBL 1138 #define IDC_LG_LBL 1139 +#define IDC_CFG_NOTACTION 1141 +#define IDC_CFG_NOTACT_STATIC 1142 #define IDA_ACTIVATE_MENU 40003 #define IDA_UP 40004 #define IDA_DOWN 40005 @@ -423,7 +439,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 212 #define _APS_NEXT_COMMAND_VALUE 40010 -#define _APS_NEXT_CONTROL_VALUE 1140 +#define _APS_NEXT_CONTROL_VALUE 1143 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/src/windows/identity/ui/timer.c b/src/windows/identity/ui/timer.c index 3adf9e9a4..77fa6c161 100644 --- a/src/windows/identity/ui/timer.c +++ b/src/windows/identity/ui/timer.c @@ -258,6 +258,18 @@ tmr_fire_timer(void) { khui_alert_create_simple(wtitle, wmsg, KHERR_WARNING, &alert); khui_alert_set_flags(alert, KHUI_ALERT_FLAG_REQUEST_BALLOON, KHUI_ALERT_FLAG_REQUEST_BALLOON); + + if (eff_ident != NULL) { + khm_int32 cmd; + + cmd = khm_get_identity_new_creds_action(eff_ident); + + if (cmd) { + khui_alert_add_command(alert, cmd); + khui_alert_add_command(alert, KHUI_PACTION_CLOSE); + } + } + khui_alert_show(alert); khui_alert_release(alert); } diff --git a/src/windows/identity/ui/uiconfig.csv b/src/windows/identity/ui/uiconfig.csv index 1cde80055..7056fce6e 100644 --- a/src/windows/identity/ui/uiconfig.csv +++ b/src/windows/identity/ui/uiconfig.csv @@ -6,6 +6,7 @@ CredWindow,KC_SPACE,0,Options for the credentials window AutoDetectNet,KC_INT32,1,Automatically detect network connectivity changes KeepRunning,KC_INT32,1,Keep running after closing Khimaira DefaultView,KC_STRING,ByIdentity, + DefaultViewMini,KC_STRING,CompactIdentity, ViewList,KC_STRING,"ByIdentity,ByLocation,ByType", PaddingHorizontal,KC_INT32,4, PaddingVertical,KC_INT32,2, @@ -26,15 +27,23 @@ CredWindow,KC_SPACE,0,Options for the credentials window MinThreshold,KC_INT32,10,Min value for a threshold (0) LogToFile,KC_INT32,0,Boolean. If true logs trace events to a nidmdbg.log in the temp folder DestroyCredsOnExit,KC_INT32,0,Boolean. If true; all credentials will be destroyed when NetIDMgr exits. + NotificationAction,KC_INT32,50025,Action to perform when the user clicks on the notification icon. + DefaultWindowMode,KC_INT32,1,(0-normal; 1-mini) Windows,KC_SPACE,0,Window parameters _Schema,KC_SPACE,0,Schema Width,KC_INT32,0, Height,KC_INT32,0, XPos,KC_INT32,0, YPos,KC_INT32,0, + Dock,KC_INT32,0,Dock on window corner (0-none; 1-top left; 2-top right; 3-bottom right; 4-bottom left) _Schema,KC_ENDSPACE,0, Main,KC_SPACE,0,Main window Main,KC_ENDSPACE,0, + MainMini,KC_SPACE,0,Main window (mini mode) + Width,KC_INT32,470, + Height,KC_INT32,500, + Dock,KC_INT32,3, + MainMini,KC_ENDSPACE,0, NewCred,KC_SPACE,0,New credentials window ForceToTop,KC_INT32,1,Force new creds window to the top AnimateSizeChanges,KC_INT32,1,Animate the new creds window when the size needs changing. @@ -169,6 +178,19 @@ CredWindow,KC_SPACE,0,Options for the credentials window TimeLeft,KC_ENDSPACE,0 Columns,KC_ENDSPACE,0 ByLocation,KC_ENDSPACE,0 + CompactIdentity,KC_SPACE,0,Default Compact View by Identity + Description,KC_STRING,Compact view of identities + ColumnList,KC_STRING,"IdentityName", + ExpandedIdentity,KC_INT32,1,Use expanded display of identity headers + NoHeader,KC_INT32,1,Suppress the column header + Columns,KC_SPACE,0, + IdentityName,KC_SPACE,0, + Width,KC_INT32,415, + SortIndex,KC_INT32,0, + Flags,KC_INT32,11 + IdentityName,KC_ENDSPACE,0, + Columns,KC_ENDSPACE,0, + CompactIdentity,KC_ENDSPACE,0 Views,KC_ENDSPACE,0 Notices,KC_SPACE,0,Notices and alerts MinimizeWarning,KC_INT32,1,Show the minimize warning? diff --git a/src/windows/identity/uilib/accel.csv b/src/windows/identity/uilib/accel.csv index f95e89bc8..2984eba61 100644 --- a/src/windows/identity/uilib/accel.csv +++ b/src/windows/identity/uilib/accel.csv @@ -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 KHUI_ACTION_IMPORT,FCONTROL|FVIRTKEY,\'I\',KHUI_ACCEL_SCOPE_GLOBAL KHUI_ACTION_DESTROY_CRED,FCONTROL|FVIRTKEY,\'D\',KHUI_ACCEL_SCOPE_GLOBAL +KHUI_ACTION_LAYOUT_MINI,FVIRTKEY,VK_F7,KHUI_ACCEL_SCOPE_GLOBAL KHUI_PACTION_SELALL,FCONTROL|FVIRTKEY,\'A\',KHUI_ACCEL_SCOPE_GLOBAL diff --git a/src/windows/identity/uilib/action.c b/src/windows/identity/uilib/action.c index 7c42889de..8ec98f8cd 100644 --- a/src/windows/identity/uilib/action.c +++ b/src/windows/identity/uilib/action.c @@ -80,6 +80,7 @@ khui_action_ref khui_menu_toolbars[] = { }; khui_action_ref khui_menu_view[] = { + MENU_ACTION(KHUI_ACTION_LAYOUT_MINI), MENU_SUBMENU(KHUI_MENU_COLUMNS), MENU_SUBMENU(KHUI_MENU_LAYOUT), #if 0 @@ -288,7 +289,8 @@ khui_action_create(const wchar_t * name, int i; size_t s; - if (!caption || + if ((name && FAILED(StringCchLength(name, KHUI_MAXCCH_NAME, &s))) || + !caption || FAILED(StringCchLength(caption, KHUI_MAXCCH_SHORT_DESC, &s)) || (tooltip && FAILED(StringCchLength(tooltip, KHUI_MAXCCH_SHORT_DESC, &s))) || (type != KHUI_ACTIONTYPE_TRIGGER && type != KHUI_ACTIONTYPE_TOGGLE)) { @@ -591,6 +593,7 @@ menu_const_to_allocd(khui_menu_def * d) KHMEXP void KHMAPI khui_menu_insert_action(khui_menu_def * d, khm_size idx, khm_int32 action, khm_int32 flags) { + khm_size i; EnterCriticalSection(&cs_actions); @@ -616,12 +619,22 @@ khui_menu_insert_action(khui_menu_def * d, khm_size idx, khm_int32 action, khm_i d->n_items++; + /* only one action is allowed to have the KHUI_ACTIONREF_DEFAULT + flag */ + if (flags & KHUI_ACTIONREF_DEFAULT) { + for (i=0; i < d->n_items; i++) { + if (i != idx && (d->items[i].flags & KHUI_ACTIONREF_DEFAULT)) + d->items[i].flags &= ~KHUI_ACTIONREF_DEFAULT; + } + } + LeaveCriticalSection(&cs_actions); } KHMEXP void KHMAPI khui_menu_insert_paction(khui_menu_def * d, khm_size idx, khui_action * paction, int flags) { + khm_size i; if (paction == NULL) return; @@ -647,6 +660,15 @@ khui_menu_insert_paction(khui_menu_def * d, khm_size idx, khui_action * paction, d->n_items++; + /* only one action is allowed to have the KHUI_ACTIONREF_DEFAULT + flag */ + if (flags & KHUI_ACTIONREF_DEFAULT) { + for (i=0; i < d->n_items; i++) { + if (i != idx && (d->items[i].flags & KHUI_ACTIONREF_DEFAULT)) + d->items[i].flags &= ~KHUI_ACTIONREF_DEFAULT; + } + } + LeaveCriticalSection(&cs_actions); } @@ -1065,6 +1087,14 @@ khui_get_cmd_accel_string(khm_int32 cmd, ap = L"F10"; break; + case VK_F11: + ap = L"F11"; + break; + + case VK_F12: + ap = L"F12"; + break; + case VK_DELETE: ap = L"Del"; break; diff --git a/src/windows/identity/uilib/actions.csv b/src/windows/identity/uilib/actions.csv index 88ac44052..36a43dfdd 100644 --- a/src/windows/identity/uilib/actions.csv +++ b/src/windows/identity/uilib/actions.csv @@ -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 KHUI_ACTION_LAYOUT_LOC,KHUI_ACTIONTYPE_TRIGGER | KHUI_ACTIONTYPE_TOGGLE,,0,0,0,0,0,IDS_ACTION_LAYOUT_LOC,0,0,0 KHUI_ACTION_LAYOUT_CUST,KHUI_ACTIONTYPE_TRIGGER | KHUI_ACTIONTYPE_TOGGLE,,0,0,0,0,0,IDS_ACTION_LAYOUT_CUST,0,0,0 +KHUI_ACTION_LAYOUT_MINI,KHUI_ACTIONTYPE_TRIGGER | KHUI_ACTIONTYPE_TOGGLE,,0,0,0,0,0,IDS_ACTION_LAYOUT_MINI,0,0,0 KHUI_ACTION_TB_STANDARD,KHUI_ACTIONTYPE_TRIGGER | KHUI_ACTIONTYPE_TOGGLE,,0,0,0,0,0,IDS_ACTION_TB_STANDARD,0,0,KHUI_ACTIONSTATE_CHECKED|KHUI_ACTIONSTATE_DISABLED KHUI_ACTION_DEBUG_WINDOW,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_ACTION_DEBUG_WINDOW,0,IDH_ACTION_DEBUG_WINDOW,KHUI_ACTIONSTATE_DISABLED KHUI_ACTION_VIEW_REFRESH,KHUI_ACTIONTYPE_TRIGGER,,IDB_VW_REFRESH,0,0,IDB_VW_REFRESH_SM,0,IDS_ACTION_VIEW_REFRESH,IDS_ACTIONT_VIEW_REFRESH,IDH_ACTION_VIEW_REFRESH,0 diff --git a/src/windows/identity/uilib/khaction.h b/src/windows/identity/uilib/khaction.h index b36ea098c..92fa7b0fa 100644 --- a/src/windows/identity/uilib/khaction.h +++ b/src/windows/identity/uilib/khaction.h @@ -165,6 +165,11 @@ typedef struct tag_khui_action_ref { context menus. In general, it is good practice to place the default item at the top of a menu, although the UI library does not enforce this. This is purely meant as a rendering hint. + + Only one action is allowed to have this flag set. When an action + is added to a menu using khui_menu_insert_action() or + khui_menu_insert_paction() and this flag is set, all other menu + items will be stripped of this flag. */ #define KHUI_ACTIONREF_DEFAULT 0x20 @@ -386,11 +391,10 @@ khui_menu_delete(khui_menu_def * d); that are valid for this function are: ::KHUI_ACTIONREF_SEP, ::KHUI_ACTIONREF_SUBMENU, ::KHUI_ACTIONREF_DEFAULT. ::KHUI_ACTIONREF_SEP will be automatically added if the - command is ::KHUI_MENU_SEP. - - \note The ::khui_menu_def structure is not thread safe. Multiple - threads modifying the same ::khui_menu_def structure may cause - thread safety issues. + command is ::KHUI_MENU_SEP. If ::KHUI_ACTIONREF_DEFAULT is + specified, then all other items in the menu will be stripped + of that flag leaving this action as the only one with that + flag set. */ KHMEXP void KHMAPI khui_menu_insert_action(khui_menu_def * d, khm_size idx, khm_int32 cmd, khm_int32 flags); @@ -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 function, ::KHUI_ACTIONREF_PACTION will automatically be aded when adding the action. ::KHUI_ACTIONREF_SEP will be - automatically added if the command is ::KHUI_MENU_SEP. - - \note The ::khui_menu_def structure is not thread safe. Multiple - threads modifying the same ::khui_menu_def structure may cause - thread safety issues. + automatically added if the command is ::KHUI_MENU_SEP. If + ::KHUI_ACTIONREF_DEFAULT is specified, then all other items in + the menu will be stripped of that flag leaving this action as + the only one with that flag set. */ KHMEXP void KHMAPI khui_menu_insert_paction(khui_menu_def * d, khm_size idx, khui_action * act, khm_int32 flags); @@ -463,9 +466,6 @@ khui_menu_get_size(khui_menu_def * d); If the specified index is out of bounds, then the function returns NULL. - \note The ::khui_menu_def structure is not thread safe. Multiple - threads modifying the same ::khui_menu_def structure may cause - thread safety issues. */ KHMEXP khui_action_ref * khui_menu_get_action(khui_menu_def * d, khm_size idx); diff --git a/src/windows/identity/uilib/khactiondef.h b/src/windows/identity/uilib/khactiondef.h index 31866ba27..87e2355f1 100644 --- a/src/windows/identity/uilib/khactiondef.h +++ b/src/windows/identity/uilib/khactiondef.h @@ -69,6 +69,7 @@ #define KHUI_ACTION_RENEW_ALL (KHUI_ACTION_BASE + 33) #define KHUI_ACTION_DESTROY_ALL (KHUI_ACTION_BASE + 34) #define KHUI_ACTION_UICB (KHUI_ACTION_BASE + 35) +#define KHUI_ACTION_LAYOUT_MINI (KHUI_ACTION_BASE + 36) /*@}*/ /*! \name Pseudo actions diff --git a/src/windows/identity/uilib/khalerts.h b/src/windows/identity/uilib/khalerts.h index 2bbc7d0da..7f0e624b4 100644 --- a/src/windows/identity/uilib/khalerts.h +++ b/src/windows/identity/uilib/khalerts.h @@ -85,11 +85,19 @@ enum khui_alert_flags { are no actions or if ::KHUI_ALERT_FLAG_REQUEST_WINDOW is specified.*/ + KHUI_ALERT_FLAG_DISPATCH_CMD =0x00000020, + /*!< If the message has commands, when the user clicks on one of + the command buttons, the corresponding command will be + immediately dispatched as if khui_action_trigger() is called + with a NULL UI context. Otherwise, the selected command will be + stored in the alert and can be retrieved via a call to + khui_alert_get_response(). */ + KHUI_ALERT_FLAG_VALID_TARGET =0x00010000, - /*!< There is a valid target for the alert */ + /*!< Internal. There is a valid target for the alert */ KHUI_ALERT_FLAG_VALID_ERROR =0x00020000, - /*!< There is a valid error context associated with the alert */ + /*!< Internal. There is a valid error context associated with the alert */ KHUI_ALERT_FLAG_DISPLAY_WINDOW =0x01000000, /*!< The alert has been displayed in a window */ @@ -104,9 +112,9 @@ enum khui_alert_flags { /*!< The alert should be displayed in a balloon */ KHUI_ALERT_FLAG_MODAL =0x10000000, - /*!< Modal alert. Do not set direclty. */ + /*!< Internal. Modal alert. Do not set direclty. */ - KHUI_ALERT_FLAGMASK_RDWR =0x0C000010, + KHUI_ALERT_FLAGMASK_RDWR =0x0C000030, /*!< Bit mask of flags that can be set by khui_alert_set_flags() */ }; @@ -266,8 +274,8 @@ khui_alert_get_response(khui_alert * alert); An exception is when ::KHUI_ALERT_FLAG_DEFACTION is specified in flags. In this case instead of a placeholder balloon prompt, one will be shown with the actual title and message (truncated if - necessary). Clicking on the balloon will have the same effect as - choosing the first command in the action. + necessary). Clicking on the balloon will cause the first command + in the command list to be performed. The placeholder balloon prompt will have a title derived from the first 63 characters of the \a title field in the alert and a @@ -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 63 characters (64 counting the terminating NULL). This limit is enforced on Windows. Also, try to make the title descriptive. + + User interaction with the alert will be as follows: + + - If the alert contains no commands, then the alert will be + displayed to the user as described above. A 'close' button will + be added to the alert if the alert is being displayed in a + window. + + - If the alert contains commands, has the + ::KHUI_ALERT_FLAG_DEFACTION flag set and is displayed in a + balloon and the user clicks on it, the first command in the + command list will be executed. + + - If the alert contains commands and does not have the + ::KHUI_ALERT_FLAG_DEFACTION and has the + ::KHUI_ALERT_FLAG_DISPATCH_CMD flag set, then when the user + selects one of the command buttons, the corresponding command + will immediately be dispatched. (see + ::KHUI_ALERT_FLAG_DISPATCH_CMD). + + - If the alert contains command and have neither + ::KHUI_ALERT_FLAG_DEFACTION nor ::KHUI_ALERT_FLAG_DISPATCH_CMD, + then when the user selects one of the command buttons, the + selected command will be stored along with the alert. It can be + retrieved via a call to khui_alert_get_response(). + */ KHMEXP khm_int32 KHMAPI khui_alert_show(khui_alert * alert);