Windows: netidmgr_plugin move roken.h to afscred.h
[openafs.git] / src / WINNT / netidmgr_plugin / afsicon.c
1
2 #define NOSTRSAFE
3 #include "afscred.h"
4 #include "AFS_component_version_number.h"
5 #include <tchar.h>
6 #include <shellapi.h>
7 #include <shlwapi.h>
8 #include <htmlhelp.h>
9 #include <strsafe.h>
10 #include <assert.h>
11
12 static ATOM message_window_class = 0;
13 static HWND notifier_window = NULL;
14 static volatile BOOL notification_icon_added = FALSE;
15
16 #define TOKEN_ICON_ID 1
17 #define TOKEN_MESSAGE_ID WM_USER
18
19 static khm_int32
20 get_default_notifier_action(void)
21 {
22     khm_int32 cmd = KHUI_ACTION_OPEN_APP;
23
24     khc_read_int32(NULL, L"CredWindow\\NotificationAction", &cmd);
25
26     return cmd;
27 }
28
29 static BOOL
30 get_release_notes(wchar_t rpath[MAX_PATH])
31 {
32     BOOL rv  = FALSE;
33     HKEY hk_client = NULL;
34     wchar_t cpath[MAX_PATH];
35     DWORD cb_data;
36
37     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
38                      L"Software\\TransarcCorporation\\AFS Client\\CurrentVersion",
39                      0, KEY_READ, &hk_client) != ERROR_SUCCESS)
40         return FALSE;
41
42     cb_data = sizeof(cpath);
43     if (RegQueryValueEx(hk_client, L"PathName", NULL, NULL, (LPBYTE) cpath, &cb_data) != ERROR_SUCCESS)
44         goto done;
45
46     cpath[min(cb_data, MAX_PATH - 1)] = L'\0';
47
48     if (!PathRemoveFileSpec(cpath))
49         goto done;
50
51     if (!PathAppend(cpath, L"Documentation"))
52         goto done;
53
54     if (!PathAppend(cpath, L"ReleaseNotes.chm"))
55         goto done;
56
57     if (!PathCanonicalize(rpath, cpath))
58         goto done;
59
60     if (!PathFileExists(rpath))
61         goto done;
62
63     rv = TRUE;
64
65  done:
66     if (hk_client)
67         RegCloseKey(hk_client);
68
69     return rv;
70 }
71
72 static void
73 handle_select(void)
74 {
75     NOTIFYICONDATA idata;
76     khm_int32 cmd;
77
78     cmd = get_default_notifier_action();
79
80     khui_action_trigger(cmd, NULL);
81
82     ZeroMemory(&idata, sizeof(idata));
83
84     Shell_NotifyIcon(NIM_SETFOCUS, &idata);
85 }
86
87 static void
88 prepare_context_menu(HMENU hmenu)
89 {
90     khm_int32 cmd;
91     wchar_t caption[128];
92     wchar_t relnotes[MAX_PATH];
93
94     cmd = get_default_notifier_action();
95
96     if (cmd == KHUI_ACTION_NEW_CRED)
97         LoadString(hResModule, IDS_ACT_NEW, caption, ARRAYLENGTH(caption));
98     else
99         LoadString(hResModule, IDS_ACT_OPEN, caption, ARRAYLENGTH(caption));
100
101     ModifyMenu(hmenu, ID_DEFAULT, MF_STRING|MF_BYCOMMAND, ID_DEFAULT, caption);
102     SetMenuDefaultItem(hmenu, ID_DEFAULT, FALSE);
103
104     if (!get_release_notes(relnotes))
105         RemoveMenu(hmenu, ID_RELEASENOTES, MF_BYCOMMAND);
106 }
107
108 static void
109 handle_context_menu(void)
110 {
111     POINT pt;
112     HMENU hMenu;
113     HMENU hMenuBar;
114
115     GetCursorPos(&pt);
116
117     hMenuBar = LoadMenu(hResModule, MAKEINTRESOURCE(IDR_CTXMENU));
118     hMenu = GetSubMenu(hMenuBar, 0);
119
120     if (hMenu) {
121         prepare_context_menu(hMenu);
122         TrackPopupMenu(hMenu, TPM_NONOTIFY, pt.x, pt.y, 0, notifier_window, NULL);
123     }
124
125     {
126         NOTIFYICONDATA idata;
127         ZeroMemory(&idata, sizeof(idata));
128         Shell_NotifyIcon(NIM_SETFOCUS, &idata);
129     }
130 }
131
132 static LRESULT CALLBACK
133 notifier_wnd_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
134 {
135     if (uMsg == TOKEN_MESSAGE_ID) {
136         switch (lParam) {
137         case NIN_SELECT:
138         case NIN_KEYSELECT:
139
140             handle_select();
141             return TRUE;
142
143         case WM_CONTEXTMENU:
144             handle_context_menu();
145             return TRUE;
146
147         default:
148             return FALSE;
149         }
150     }
151     else if (uMsg == WM_COMMAND) {
152         switch (LOWORD(wParam)) {
153         case ID_DEFAULT:
154             {
155                 khm_int32 cmd;
156
157                 cmd = get_default_notifier_action();
158
159                 khui_action_trigger(cmd, NULL);
160             }
161             return TRUE;
162
163         case ID_SHOWHELP:
164             {
165                 afs_html_help(notifier_window, L"::/html/welcome.htm", HH_DISPLAY_TOPIC, 0);
166             }
167             return TRUE;
168
169         case ID_RELEASENOTES:
170             {
171                 wchar_t relnotes[MAX_PATH];
172
173                 if (get_release_notes(relnotes))
174                     HtmlHelp(GetDesktopWindow(), relnotes, HH_DISPLAY_TOC, 0);
175             }
176             return TRUE;
177         }
178     }
179     return DefWindowProc(hwnd, uMsg, wParam, lParam);
180 }
181
182 static void
183 initialize_if_necessary(void)
184 {
185     if (message_window_class == 0) {
186         WNDCLASSEX c = {
187             sizeof(WNDCLASSEX), /* cbSize */
188             0,                  /* style */
189             notifier_wnd_proc,  /* lpfnWndProc */
190             0,                  /* cbClsExtra */
191             0,                  /* cbWndExtra */
192             NULL,               /* hinstance */
193             NULL,               /* hIcon */
194             NULL,               /* hCursor */
195             NULL,               /* hbrBackground */
196             NULL,               /* lpszMenuName */
197             L"OpenAFSTokenStateIconNotifier", /* lpszClassName */
198             NULL,                             /* hIconSm */
199         };
200
201         c.hInstance = hInstance;
202         message_window_class = RegisterClassEx(&c);
203     }
204
205     if (notifier_window == NULL && message_window_class != 0) {
206         notifier_window = CreateWindow(MAKEINTATOM(message_window_class),
207                                        L"OpenAFSTokenStateIconNotifierWindow",
208                                        0, 0, 0, 0, 0,
209                                        HWND_MESSAGE,
210                                        NULL,
211                                        hInstance,
212                                        NULL);
213     }
214
215     assert(notifier_window != NULL);
216
217     if (!notification_icon_added && notifier_window != NULL) {
218         NOTIFYICONDATA idata;
219
220         ZeroMemory(&idata, sizeof(idata));
221
222         idata.cbSize = sizeof(idata);
223         idata.hWnd = notifier_window;
224         idata.uID = TOKEN_ICON_ID;
225         idata.uFlags = NIF_ICON | NIF_MESSAGE;
226         idata.uCallbackMessage = TOKEN_MESSAGE_ID;
227         idata.hIcon = (HICON) LoadImage(hResModule, MAKEINTRESOURCE(IDI_CRED_NONE),
228                                         IMAGE_ICON, 0, 0,
229                                         LR_DEFAULTSIZE | LR_DEFAULTCOLOR | LR_SHARED);
230         notification_icon_added = Shell_NotifyIcon(NIM_ADD, &idata);
231
232         idata.cbSize = sizeof(idata);
233         idata.uVersion = NOTIFYICON_VERSION;
234
235         Shell_NotifyIcon(NIM_SETVERSION, &idata);
236
237         assert(notification_icon_added);
238     }
239 }
240
241 void
242 afs_remove_icon(void)
243 {
244     NOTIFYICONDATA idata;
245
246     ZeroMemory(&idata, sizeof(idata));
247
248     idata.cbSize = sizeof(idata);
249     idata.hWnd = notifier_window;
250     idata.uID = TOKEN_ICON_ID;
251     Shell_NotifyIcon(NIM_DELETE, &idata);
252     notification_icon_added = FALSE;
253 }
254
255 static void
256 set_tooltip_and_icon(UINT tooltip_text, const wchar_t * postfix, UINT icon_id)
257 {
258     NOTIFYICONDATA idata;
259     wchar_t buf[ARRAYLENGTH(idata.szTip)];
260
261     ZeroMemory(&idata, sizeof(idata));
262
263     idata.cbSize = sizeof(idata);
264     idata.hWnd = notifier_window;
265     idata.uID = TOKEN_ICON_ID;
266     idata.uFlags = NIF_ICON | NIF_TIP;
267     idata.hIcon = (HICON) LoadImage(hResModule, MAKEINTRESOURCE(icon_id),
268                                     IMAGE_ICON, 0, 0,
269                                     LR_DEFAULTCOLOR | LR_DEFAULTSIZE | LR_SHARED);
270     if (tooltip_text != 0) {
271         LoadString(hResModule, tooltip_text, buf, ARRAYLENGTH(buf));
272     }
273     StringCbPrintf(idata.szTip, sizeof(idata.szTip),
274                    L"%s%s%s%s",
275                    (tooltip_text != 0)? buf : L"",
276                    (postfix != NULL)? postfix : L"",
277                    (tooltip_text != 0 || postfix != NULL)? L"\n": L"",
278                    _T(AFS_VERINFO_BUILD));
279
280     Shell_NotifyIcon(NIM_MODIFY, &idata);
281 }
282
283 struct state_data {
284     enum notification_icon_state state;
285     khm_handle credset;
286 };
287
288 #define COLLECT_STR_LEN 256
289
290 static khm_int32 KHMAPI
291 collect_cell_names(khm_handle cred, void * rock)
292 {
293     wchar_t *str = (wchar_t *) rock;
294     wchar_t cell[KCDB_MAXCCH_NAME] = L"";
295     FILETIME ft_now;
296     FILETIME ft_expire;
297     khm_size cb;
298
299     cb = sizeof(ft_expire);
300     if (KHM_FAILED(kcdb_cred_get_attr(cred, KCDB_ATTR_EXPIRE, NULL, &ft_expire, &cb)))
301         return KHM_ERROR_SUCCESS;
302
303     GetSystemTimeAsFileTime(&ft_now);
304     if (CompareFileTime(&ft_now, &ft_expire) >= 0)
305         return KHM_ERROR_SUCCESS;
306
307     cb = sizeof(cell);
308
309     if (KHM_SUCCEEDED(kcdb_cred_get_attr(cred, afs_attr_cell, NULL, cell, &cb)) &&
310         cell[0]) {
311         StringCchCat(str, COLLECT_STR_LEN, cell);
312         StringCchCat(str, COLLECT_STR_LEN, L"\n");
313     }
314
315     return KHM_ERROR_SUCCESS;
316 }
317
318 static khm_int32 KHMAPI
319 set_state_from_ui_thread(HWND hwnd_main, void * stuff)
320 {
321     struct state_data * d = (struct state_data *) stuff;
322
323     initialize_if_necessary();
324
325     switch (d->state) {
326     case AFSICON_REPORT_TOKENS:
327         {
328             wchar_t cells[COLLECT_STR_LEN] = L"";
329
330             kcdb_credset_apply(d->credset, collect_cell_names, cells);
331
332             if (cells[0] == L'\0') {
333                 set_tooltip_and_icon(IDS_CRED_TT_NONE, NULL, IDI_CRED_NONE);
334                 break;
335             }
336
337             set_tooltip_and_icon(0, cells, IDI_CRED_OK);
338         }
339         break;
340
341     case AFSICON_SERVICE_STOPPED:
342         set_tooltip_and_icon(IDS_CRED_TT_NOS, NULL, IDI_CRED_SVCSTOP);
343         break;
344
345     case AFSICON_SERVICE_ERROR:
346         set_tooltip_and_icon(IDS_CRED_TT_SERR, NULL, IDI_CRED_BROKEN);
347         break;
348
349     default:
350         assert(FALSE);
351     }
352
353     (void) hwnd_main;           /* unreferenced */
354
355     return KHM_ERROR_SUCCESS;
356 }
357
358 void
359 afs_icon_set_state(enum notification_icon_state state,
360                    khm_handle credset_with_tokens)
361 {
362     struct state_data d;
363
364 #if KH_VERSION_API < 7
365     if (pkhui_request_UI_callback == NULL)
366         return;
367 #endif
368
369     d.state = state;
370     d.credset = credset_with_tokens;
371
372     if (notification_icon_added) {
373         set_state_from_ui_thread(NULL, &d);
374     } else {
375         khui_request_UI_callback(set_state_from_ui_thread, &d);
376     }
377 }