1*7f2fe78bSCy Schubert /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2*7f2fe78bSCy Schubert /* leashdll/lshutil.cpp - text manipulation for principal entry */
3*7f2fe78bSCy Schubert /*
4*7f2fe78bSCy Schubert * Copyright (C) 2012 by the Massachusetts Institute of Technology.
5*7f2fe78bSCy Schubert * All rights reserved.
6*7f2fe78bSCy Schubert *
7*7f2fe78bSCy Schubert * Redistribution and use in source and binary forms, with or without
8*7f2fe78bSCy Schubert * modification, are permitted provided that the following conditions
9*7f2fe78bSCy Schubert * are met:
10*7f2fe78bSCy Schubert *
11*7f2fe78bSCy Schubert * * Redistributions of source code must retain the above copyright
12*7f2fe78bSCy Schubert * notice, this list of conditions and the following disclaimer.
13*7f2fe78bSCy Schubert *
14*7f2fe78bSCy Schubert * * Redistributions in binary form must reproduce the above copyright
15*7f2fe78bSCy Schubert * notice, this list of conditions and the following disclaimer in
16*7f2fe78bSCy Schubert * the documentation and/or other materials provided with the
17*7f2fe78bSCy Schubert * distribution.
18*7f2fe78bSCy Schubert *
19*7f2fe78bSCy Schubert * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20*7f2fe78bSCy Schubert * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21*7f2fe78bSCy Schubert * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22*7f2fe78bSCy Schubert * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23*7f2fe78bSCy Schubert * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24*7f2fe78bSCy Schubert * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25*7f2fe78bSCy Schubert * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26*7f2fe78bSCy Schubert * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27*7f2fe78bSCy Schubert * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28*7f2fe78bSCy Schubert * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29*7f2fe78bSCy Schubert * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30*7f2fe78bSCy Schubert * OF THE POSSIBILITY OF SUCH DAMAGE.
31*7f2fe78bSCy Schubert */
32*7f2fe78bSCy Schubert
33*7f2fe78bSCy Schubert /*
34*7f2fe78bSCy Schubert *
35*7f2fe78bSCy Schubert * Leash Principal Edit Control
36*7f2fe78bSCy Schubert *
37*7f2fe78bSCy Schubert * Edit control customized to enter a principal.
38*7f2fe78bSCy Schubert * -Autocomplete functionality using history of previously successful
39*7f2fe78bSCy Schubert * authentications
40*7f2fe78bSCy Schubert * -Option to automatically convert realm to uppercase as user types
41*7f2fe78bSCy Schubert * -Suggest default realm when no matches available from history
42*7f2fe78bSCy Schubert */
43*7f2fe78bSCy Schubert
44*7f2fe78bSCy Schubert #include <windows.h>
45*7f2fe78bSCy Schubert #include <wtypes.h> // LPOLESTR
46*7f2fe78bSCy Schubert #include <Shldisp.h> // IAutoComplete
47*7f2fe78bSCy Schubert #include <ShlGuid.h> // CLSID_AutoComplete
48*7f2fe78bSCy Schubert #include <shobjidl.h> // IAutoCompleteDropDown
49*7f2fe78bSCy Schubert #include <objbase.h> // CoCreateInstance
50*7f2fe78bSCy Schubert #include <tchar.h>
51*7f2fe78bSCy Schubert #include <map>
52*7f2fe78bSCy Schubert #include <vector>
53*7f2fe78bSCy Schubert
54*7f2fe78bSCy Schubert #include "leashwin.h"
55*7f2fe78bSCy Schubert #include "leashdll.h"
56*7f2fe78bSCy Schubert
57*7f2fe78bSCy Schubert #pragma comment(lib, "ole32.lib") // CoCreateInstance
58*7f2fe78bSCy Schubert
59*7f2fe78bSCy Schubert //
60*7f2fe78bSCy Schubert // DynEnumString:
61*7f2fe78bSCy Schubert // IEnumString implementation that can be dynamically updated after creation.
62*7f2fe78bSCy Schubert //
63*7f2fe78bSCy Schubert class DynEnumString : public IEnumString
64*7f2fe78bSCy Schubert {
65*7f2fe78bSCy Schubert public:
66*7f2fe78bSCy Schubert // IUnknown
AddRef()67*7f2fe78bSCy Schubert STDMETHODIMP_(ULONG) AddRef()
68*7f2fe78bSCy Schubert {
69*7f2fe78bSCy Schubert return ++m_refcount;
70*7f2fe78bSCy Schubert }
71*7f2fe78bSCy Schubert
Release()72*7f2fe78bSCy Schubert STDMETHODIMP_(ULONG) Release()
73*7f2fe78bSCy Schubert {
74*7f2fe78bSCy Schubert ULONG refcount = --m_refcount;
75*7f2fe78bSCy Schubert if (refcount == 0)
76*7f2fe78bSCy Schubert delete this;
77*7f2fe78bSCy Schubert return refcount;
78*7f2fe78bSCy Schubert }
79*7f2fe78bSCy Schubert
QueryInterface(REFIID riid,void ** ppvObject)80*7f2fe78bSCy Schubert STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject)
81*7f2fe78bSCy Schubert {
82*7f2fe78bSCy Schubert IUnknown *punk = NULL;
83*7f2fe78bSCy Schubert if (riid == IID_IUnknown)
84*7f2fe78bSCy Schubert punk = static_cast<IUnknown*>(this);
85*7f2fe78bSCy Schubert else if (riid == IID_IEnumString)
86*7f2fe78bSCy Schubert punk = static_cast<IEnumString*>(this);
87*7f2fe78bSCy Schubert *ppvObject = punk;
88*7f2fe78bSCy Schubert if (punk == NULL)
89*7f2fe78bSCy Schubert return E_NOINTERFACE;
90*7f2fe78bSCy Schubert punk->AddRef();
91*7f2fe78bSCy Schubert return S_OK;
92*7f2fe78bSCy Schubert }
93*7f2fe78bSCy Schubert
94*7f2fe78bSCy Schubert // IEnumString
95*7f2fe78bSCy Schubert public:
Clone(IEnumString ** ppclone)96*7f2fe78bSCy Schubert STDMETHODIMP Clone(IEnumString **ppclone)
97*7f2fe78bSCy Schubert {
98*7f2fe78bSCy Schubert LPTSTR *data = m_aStrings.data();
99*7f2fe78bSCy Schubert ULONG count = m_aStrings.size();
100*7f2fe78bSCy Schubert *ppclone = new DynEnumString(count, data);
101*7f2fe78bSCy Schubert return S_OK;
102*7f2fe78bSCy Schubert }
103*7f2fe78bSCy Schubert
Next(ULONG count,LPOLESTR * elements,ULONG * pFetched)104*7f2fe78bSCy Schubert STDMETHODIMP Next(ULONG count, LPOLESTR *elements, ULONG *pFetched)
105*7f2fe78bSCy Schubert {
106*7f2fe78bSCy Schubert ULONG fetched = 0;
107*7f2fe78bSCy Schubert while (fetched < count) {
108*7f2fe78bSCy Schubert if (m_iter == m_aStrings.end())
109*7f2fe78bSCy Schubert break;
110*7f2fe78bSCy Schubert LPTSTR src = *m_iter++;
111*7f2fe78bSCy Schubert // @TODO: add _UNICODE version
112*7f2fe78bSCy Schubert DWORD nLengthW = ::MultiByteToWideChar(CP_ACP,
113*7f2fe78bSCy Schubert 0, &src[0], -1, NULL, 0);
114*7f2fe78bSCy Schubert LPOLESTR copy =
115*7f2fe78bSCy Schubert (LPOLESTR )::CoTaskMemAlloc(sizeof(OLECHAR) * nLengthW);
116*7f2fe78bSCy Schubert if (copy != NULL) {
117*7f2fe78bSCy Schubert if (::MultiByteToWideChar(CP_ACP,
118*7f2fe78bSCy Schubert 0, &src[0], -1, copy, nLengthW)) {
119*7f2fe78bSCy Schubert elements[fetched++] = copy;
120*7f2fe78bSCy Schubert } else {
121*7f2fe78bSCy Schubert // failure...
122*7f2fe78bSCy Schubert // TODO: debug spew
123*7f2fe78bSCy Schubert ::CoTaskMemFree(copy);
124*7f2fe78bSCy Schubert copy = NULL;
125*7f2fe78bSCy Schubert }
126*7f2fe78bSCy Schubert }
127*7f2fe78bSCy Schubert }
128*7f2fe78bSCy Schubert *pFetched = fetched;
129*7f2fe78bSCy Schubert
130*7f2fe78bSCy Schubert return fetched == count ? S_OK : S_FALSE;
131*7f2fe78bSCy Schubert }
132*7f2fe78bSCy Schubert
Reset()133*7f2fe78bSCy Schubert STDMETHODIMP Reset()
134*7f2fe78bSCy Schubert {
135*7f2fe78bSCy Schubert m_iter = m_aStrings.begin();
136*7f2fe78bSCy Schubert return S_OK;
137*7f2fe78bSCy Schubert }
138*7f2fe78bSCy Schubert
Skip(ULONG count)139*7f2fe78bSCy Schubert STDMETHODIMP Skip(ULONG count)
140*7f2fe78bSCy Schubert {
141*7f2fe78bSCy Schubert for (ULONG i=0; i<count; i++) {
142*7f2fe78bSCy Schubert if (m_iter == m_aStrings.end()) {
143*7f2fe78bSCy Schubert m_iter = m_aStrings.begin();
144*7f2fe78bSCy Schubert break;
145*7f2fe78bSCy Schubert }
146*7f2fe78bSCy Schubert m_iter++;
147*7f2fe78bSCy Schubert }
148*7f2fe78bSCy Schubert return S_OK;
149*7f2fe78bSCy Schubert }
150*7f2fe78bSCy Schubert
151*7f2fe78bSCy Schubert // Custom interface
DynEnumString(ULONG count,LPTSTR * strings)152*7f2fe78bSCy Schubert DynEnumString(ULONG count, LPTSTR *strings)
153*7f2fe78bSCy Schubert {
154*7f2fe78bSCy Schubert m_aStrings.reserve(count + 1);
155*7f2fe78bSCy Schubert for (ULONG i = 0; i < count; i++) {
156*7f2fe78bSCy Schubert AddString(strings[i]);
157*7f2fe78bSCy Schubert }
158*7f2fe78bSCy Schubert m_iter = m_aStrings.begin();
159*7f2fe78bSCy Schubert m_refcount = 1;
160*7f2fe78bSCy Schubert }
161*7f2fe78bSCy Schubert
~DynEnumString()162*7f2fe78bSCy Schubert virtual ~DynEnumString()
163*7f2fe78bSCy Schubert {
164*7f2fe78bSCy Schubert RemoveAll();
165*7f2fe78bSCy Schubert }
166*7f2fe78bSCy Schubert
RemoveAll()167*7f2fe78bSCy Schubert void RemoveAll()
168*7f2fe78bSCy Schubert {
169*7f2fe78bSCy Schubert for (m_iter = m_aStrings.begin();
170*7f2fe78bSCy Schubert m_iter != m_aStrings.end();
171*7f2fe78bSCy Schubert m_iter++)
172*7f2fe78bSCy Schubert delete[] (*m_iter);
173*7f2fe78bSCy Schubert m_aStrings.erase(m_aStrings.begin(), m_aStrings.end());
174*7f2fe78bSCy Schubert }
175*7f2fe78bSCy Schubert
AddString(LPTSTR str)176*7f2fe78bSCy Schubert void AddString(LPTSTR str)
177*7f2fe78bSCy Schubert {
178*7f2fe78bSCy Schubert LPTSTR copy = NULL;
179*7f2fe78bSCy Schubert if (str) {
180*7f2fe78bSCy Schubert copy = _tcsdup(str);
181*7f2fe78bSCy Schubert if (copy)
182*7f2fe78bSCy Schubert m_aStrings.push_back(copy);
183*7f2fe78bSCy Schubert }
184*7f2fe78bSCy Schubert }
185*7f2fe78bSCy Schubert
186*7f2fe78bSCy Schubert
RemoveString(LPTSTR str)187*7f2fe78bSCy Schubert void RemoveString(LPTSTR str)
188*7f2fe78bSCy Schubert {
189*7f2fe78bSCy Schubert std::vector<LPTSTR>::const_iterator i;
190*7f2fe78bSCy Schubert for (i = m_aStrings.begin(); i != m_aStrings.end(); i++) {
191*7f2fe78bSCy Schubert if (_tcscmp(*i, str) == 0) {
192*7f2fe78bSCy Schubert delete[] (*i);
193*7f2fe78bSCy Schubert m_aStrings.erase(i);
194*7f2fe78bSCy Schubert break;
195*7f2fe78bSCy Schubert }
196*7f2fe78bSCy Schubert }
197*7f2fe78bSCy Schubert }
198*7f2fe78bSCy Schubert
199*7f2fe78bSCy Schubert private:
200*7f2fe78bSCy Schubert ULONG m_refcount;
201*7f2fe78bSCy Schubert std::vector<LPTSTR>::iterator m_iter;
202*7f2fe78bSCy Schubert std::vector<LPTSTR> m_aStrings;
203*7f2fe78bSCy Schubert };
204*7f2fe78bSCy Schubert
205*7f2fe78bSCy Schubert // Registry key to store history of successfully authenticated principals
206*7f2fe78bSCy Schubert #define LEASH_REGISTRY_PRINCIPALS_KEY_NAME "Software\\MIT\\Leash\\Principals"
207*7f2fe78bSCy Schubert
208*7f2fe78bSCy Schubert // Free principal list obtained by getPrincipalList()
freePrincipalList(LPTSTR * princs,int count)209*7f2fe78bSCy Schubert static void freePrincipalList(LPTSTR *princs, int count)
210*7f2fe78bSCy Schubert {
211*7f2fe78bSCy Schubert int i;
212*7f2fe78bSCy Schubert if (count) {
213*7f2fe78bSCy Schubert for (i = 0; i < count; i++)
214*7f2fe78bSCy Schubert if (princs[i])
215*7f2fe78bSCy Schubert free(princs[i]);
216*7f2fe78bSCy Schubert delete[] princs;
217*7f2fe78bSCy Schubert }
218*7f2fe78bSCy Schubert }
219*7f2fe78bSCy Schubert
220*7f2fe78bSCy Schubert // Retrieve history of successfully authenticated principals from registry
getPrincipalList(LPTSTR ** outPrincs,int * outPrincCount)221*7f2fe78bSCy Schubert static void getPrincipalList(LPTSTR **outPrincs, int *outPrincCount)
222*7f2fe78bSCy Schubert {
223*7f2fe78bSCy Schubert DWORD count = 0;
224*7f2fe78bSCy Schubert DWORD valCount = 0;
225*7f2fe78bSCy Schubert DWORD maxLen = 0;
226*7f2fe78bSCy Schubert LPTSTR tempValName = NULL;
227*7f2fe78bSCy Schubert LPTSTR *princs = NULL;
228*7f2fe78bSCy Schubert *outPrincs = NULL;
229*7f2fe78bSCy Schubert HKEY hKey = NULL;
230*7f2fe78bSCy Schubert unsigned long rc = RegCreateKeyEx(HKEY_CURRENT_USER,
231*7f2fe78bSCy Schubert LEASH_REGISTRY_PRINCIPALS_KEY_NAME, 0, 0,
232*7f2fe78bSCy Schubert 0, KEY_READ, 0, &hKey, 0);
233*7f2fe78bSCy Schubert if (rc == S_OK) {
234*7f2fe78bSCy Schubert // get string count
235*7f2fe78bSCy Schubert rc = RegQueryInfoKey(
236*7f2fe78bSCy Schubert hKey,
237*7f2fe78bSCy Schubert NULL, // __out_opt LPTSTR lpClass,
238*7f2fe78bSCy Schubert NULL, // __inout_opt LPDWORD lpcClass,
239*7f2fe78bSCy Schubert NULL, // __reserved LPDWORD lpReserved,
240*7f2fe78bSCy Schubert NULL, // __out_opt LPDWORD lpcSubKeys,
241*7f2fe78bSCy Schubert NULL, // __out_opt LPDWORD lpcMaxSubKeyLen,
242*7f2fe78bSCy Schubert NULL, // __out_opt LPDWORD lpcMaxClassLen,
243*7f2fe78bSCy Schubert &valCount, //__out_opt LPDWORD lpcValues,
244*7f2fe78bSCy Schubert &maxLen, // __out_opt LPDWORD lpcMaxValueNameLen,
245*7f2fe78bSCy Schubert NULL, // __out_opt LPDWORD lpcMaxValueLen,
246*7f2fe78bSCy Schubert NULL, // __out_opt LPDWORD lpcbSecurityDescriptor,
247*7f2fe78bSCy Schubert NULL // __out_opt PFILETIME lpftLastWriteTime
248*7f2fe78bSCy Schubert );
249*7f2fe78bSCy Schubert }
250*7f2fe78bSCy Schubert if (valCount == 0)
251*7f2fe78bSCy Schubert goto cleanup;
252*7f2fe78bSCy Schubert
253*7f2fe78bSCy Schubert princs = new LPTSTR[valCount];
254*7f2fe78bSCy Schubert if (princs == NULL)
255*7f2fe78bSCy Schubert goto cleanup;
256*7f2fe78bSCy Schubert
257*7f2fe78bSCy Schubert tempValName = new TCHAR[maxLen+1];
258*7f2fe78bSCy Schubert if (tempValName == NULL)
259*7f2fe78bSCy Schubert goto cleanup;
260*7f2fe78bSCy Schubert
261*7f2fe78bSCy Schubert // enumerate values...
262*7f2fe78bSCy Schubert for (DWORD iReg = 0; iReg < valCount; iReg++) {
263*7f2fe78bSCy Schubert LPTSTR princ = NULL;
264*7f2fe78bSCy Schubert DWORD size = maxLen+1;
265*7f2fe78bSCy Schubert rc = RegEnumValue(hKey, iReg, tempValName, &size,
266*7f2fe78bSCy Schubert NULL, NULL, NULL, NULL);
267*7f2fe78bSCy Schubert if (rc == ERROR_SUCCESS)
268*7f2fe78bSCy Schubert princ = _tcsdup(tempValName);
269*7f2fe78bSCy Schubert if (princ != NULL)
270*7f2fe78bSCy Schubert princs[count++] = princ;
271*7f2fe78bSCy Schubert }
272*7f2fe78bSCy Schubert
273*7f2fe78bSCy Schubert *outPrincCount = count;
274*7f2fe78bSCy Schubert count = 0;
275*7f2fe78bSCy Schubert *outPrincs = princs;
276*7f2fe78bSCy Schubert princs = NULL;
277*7f2fe78bSCy Schubert
278*7f2fe78bSCy Schubert cleanup:
279*7f2fe78bSCy Schubert if (tempValName)
280*7f2fe78bSCy Schubert delete[] tempValName;
281*7f2fe78bSCy Schubert if (princs)
282*7f2fe78bSCy Schubert freePrincipalList(princs, count);
283*7f2fe78bSCy Schubert if (hKey)
284*7f2fe78bSCy Schubert RegCloseKey(hKey);
285*7f2fe78bSCy Schubert return;
286*7f2fe78bSCy Schubert }
287*7f2fe78bSCy Schubert
288*7f2fe78bSCy Schubert
289*7f2fe78bSCy Schubert // HookWindow
290*7f2fe78bSCy Schubert // Utility class to process messages relating to the specified hwnd
291*7f2fe78bSCy Schubert class HookWindow
292*7f2fe78bSCy Schubert {
293*7f2fe78bSCy Schubert public:
294*7f2fe78bSCy Schubert typedef std::pair<HWND, HookWindow*> map_elem;
295*7f2fe78bSCy Schubert typedef std::map<HWND, HookWindow*> map;
296*7f2fe78bSCy Schubert
HookWindow(HWND in_hwnd)297*7f2fe78bSCy Schubert HookWindow(HWND in_hwnd) : m_hwnd(in_hwnd)
298*7f2fe78bSCy Schubert {
299*7f2fe78bSCy Schubert // add 'this' to static hash
300*7f2fe78bSCy Schubert m_ctrl_id = GetDlgCtrlID(in_hwnd);
301*7f2fe78bSCy Schubert m_parent = ::GetParent(m_hwnd);
302*7f2fe78bSCy Schubert sm_map.insert(map_elem(m_parent, this));
303*7f2fe78bSCy Schubert // grab current window proc and replace with our wndproc
304*7f2fe78bSCy Schubert m_parent_wndproc = SetWindowLongPtr(m_parent,
305*7f2fe78bSCy Schubert GWLP_WNDPROC,
306*7f2fe78bSCy Schubert (ULONG_PTR)(&sWindowProc));
307*7f2fe78bSCy Schubert }
308*7f2fe78bSCy Schubert
~HookWindow()309*7f2fe78bSCy Schubert virtual ~HookWindow()
310*7f2fe78bSCy Schubert {
311*7f2fe78bSCy Schubert // unhook hwnd and restore old wndproc
312*7f2fe78bSCy Schubert SetWindowLongPtr(m_parent, GWLP_WNDPROC, m_parent_wndproc);
313*7f2fe78bSCy Schubert sm_map.erase(m_parent);
314*7f2fe78bSCy Schubert }
315*7f2fe78bSCy Schubert
316*7f2fe78bSCy Schubert // Process a message
317*7f2fe78bSCy Schubert // return 'false' to forward message to parent wndproc
318*7f2fe78bSCy Schubert virtual bool WindowProc(UINT msg, WPARAM wParam, LPARAM lParam,
319*7f2fe78bSCy Schubert LRESULT *lr) = 0;
320*7f2fe78bSCy Schubert
321*7f2fe78bSCy Schubert protected:
322*7f2fe78bSCy Schubert static LRESULT sWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
323*7f2fe78bSCy Schubert LPARAM lParam);
324*7f2fe78bSCy Schubert
325*7f2fe78bSCy Schubert HWND m_hwnd;
326*7f2fe78bSCy Schubert HWND m_parent;
327*7f2fe78bSCy Schubert ULONG_PTR m_parent_wndproc;
328*7f2fe78bSCy Schubert int m_ctrl_id;
329*7f2fe78bSCy Schubert
330*7f2fe78bSCy Schubert static map sm_map;
331*7f2fe78bSCy Schubert };
332*7f2fe78bSCy Schubert
333*7f2fe78bSCy Schubert HookWindow::map HookWindow::sm_map;
334*7f2fe78bSCy Schubert
sWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)335*7f2fe78bSCy Schubert LRESULT HookWindow::sWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
336*7f2fe78bSCy Schubert LPARAM lParam)
337*7f2fe78bSCy Schubert {
338*7f2fe78bSCy Schubert LRESULT result;
339*7f2fe78bSCy Schubert // hash hwnd to get object and call actual window proc,
340*7f2fe78bSCy Schubert // then parent window proc as necessary
341*7f2fe78bSCy Schubert HookWindow::map::const_iterator iter = sm_map.find(hwnd);
342*7f2fe78bSCy Schubert if (iter != sm_map.end()) {
343*7f2fe78bSCy Schubert if (!iter->second->WindowProc(uMsg, wParam, lParam, &result))
344*7f2fe78bSCy Schubert result = CallWindowProc((WNDPROC )iter->second->m_parent_wndproc,
345*7f2fe78bSCy Schubert hwnd, uMsg, wParam, lParam);
346*7f2fe78bSCy Schubert } else {
347*7f2fe78bSCy Schubert result = ::DefWindowProc(hwnd, uMsg, wParam, lParam);
348*7f2fe78bSCy Schubert }
349*7f2fe78bSCy Schubert return result;
350*7f2fe78bSCy Schubert }
351*7f2fe78bSCy Schubert
352*7f2fe78bSCy Schubert //
353*7f2fe78bSCy Schubert class PrincipalEditControl : public HookWindow
354*7f2fe78bSCy Schubert {
355*7f2fe78bSCy Schubert public:
PrincipalEditControl(HWND hwnd,bool bUpperCaseRealm)356*7f2fe78bSCy Schubert PrincipalEditControl(HWND hwnd, bool bUpperCaseRealm) : HookWindow(hwnd)
357*7f2fe78bSCy Schubert ,m_ignore_change(0)
358*7f2fe78bSCy Schubert ,m_bUpperCaseRealm(bUpperCaseRealm)
359*7f2fe78bSCy Schubert ,m_defaultRealm(NULL)
360*7f2fe78bSCy Schubert ,m_ctx(0)
361*7f2fe78bSCy Schubert ,m_enumString(NULL)
362*7f2fe78bSCy Schubert ,m_acdd(NULL)
363*7f2fe78bSCy Schubert ,m_princStr(NULL)
364*7f2fe78bSCy Schubert {
365*7f2fe78bSCy Schubert pkrb5_init_context(&m_ctx);
366*7f2fe78bSCy Schubert GetDefaultRealm();
367*7f2fe78bSCy Schubert InitAutocomplete();
368*7f2fe78bSCy Schubert }
369*7f2fe78bSCy Schubert
~PrincipalEditControl()370*7f2fe78bSCy Schubert ~PrincipalEditControl()
371*7f2fe78bSCy Schubert {
372*7f2fe78bSCy Schubert DestroyAutocomplete();
373*7f2fe78bSCy Schubert if (m_princStr)
374*7f2fe78bSCy Schubert delete[] m_princStr;
375*7f2fe78bSCy Schubert if (m_ctx && m_defaultRealm)
376*7f2fe78bSCy Schubert pkrb5_free_default_realm(m_ctx, m_defaultRealm);
377*7f2fe78bSCy Schubert if (m_ctx)
378*7f2fe78bSCy Schubert pkrb5_free_context(m_ctx);
379*7f2fe78bSCy Schubert }
380*7f2fe78bSCy Schubert
ClearHistory()381*7f2fe78bSCy Schubert void ClearHistory()
382*7f2fe78bSCy Schubert {
383*7f2fe78bSCy Schubert if (m_enumString != NULL)
384*7f2fe78bSCy Schubert m_enumString->RemoveAll();
385*7f2fe78bSCy Schubert if (m_acdd != NULL)
386*7f2fe78bSCy Schubert m_acdd->ResetEnumerator();
387*7f2fe78bSCy Schubert if (m_princStr != NULL) {
388*7f2fe78bSCy Schubert delete[] m_princStr;
389*7f2fe78bSCy Schubert m_princStr = NULL;
390*7f2fe78bSCy Schubert }
391*7f2fe78bSCy Schubert }
392*7f2fe78bSCy Schubert
393*7f2fe78bSCy Schubert protected:
394*7f2fe78bSCy Schubert // Convert str to upper case
395*7f2fe78bSCy Schubert // This should be more-or-less _UNICODE-agnostic
StrToUpper(LPTSTR str)396*7f2fe78bSCy Schubert static bool StrToUpper(LPTSTR str)
397*7f2fe78bSCy Schubert {
398*7f2fe78bSCy Schubert bool bChanged = false;
399*7f2fe78bSCy Schubert int c;
400*7f2fe78bSCy Schubert if (str != NULL) {
401*7f2fe78bSCy Schubert while ((c = *str) != NULL) {
402*7f2fe78bSCy Schubert if (__isascii(c) && islower(c)) {
403*7f2fe78bSCy Schubert bChanged = true;
404*7f2fe78bSCy Schubert *str = _toupper(c);
405*7f2fe78bSCy Schubert }
406*7f2fe78bSCy Schubert str++;
407*7f2fe78bSCy Schubert }
408*7f2fe78bSCy Schubert }
409*7f2fe78bSCy Schubert return bChanged;
410*7f2fe78bSCy Schubert }
411*7f2fe78bSCy Schubert
GetDefaultRealm()412*7f2fe78bSCy Schubert void GetDefaultRealm()
413*7f2fe78bSCy Schubert {
414*7f2fe78bSCy Schubert // @TODO: _UNICODE support here
415*7f2fe78bSCy Schubert if ((m_defaultRealm == NULL) && m_ctx) {
416*7f2fe78bSCy Schubert pkrb5_get_default_realm(m_ctx, &m_defaultRealm);
417*7f2fe78bSCy Schubert }
418*7f2fe78bSCy Schubert }
419*7f2fe78bSCy Schubert
420*7f2fe78bSCy Schubert // Append default realm to user and add to the autocomplete enum string
SuggestDefaultRealm(LPTSTR user)421*7f2fe78bSCy Schubert void SuggestDefaultRealm(LPTSTR user)
422*7f2fe78bSCy Schubert {
423*7f2fe78bSCy Schubert if (m_defaultRealm == NULL)
424*7f2fe78bSCy Schubert return;
425*7f2fe78bSCy Schubert
426*7f2fe78bSCy Schubert int princ_len = _tcslen(user) + _tcslen(m_defaultRealm) + 1;
427*7f2fe78bSCy Schubert LPTSTR princStr = new TCHAR[princ_len];
428*7f2fe78bSCy Schubert if (princStr) {
429*7f2fe78bSCy Schubert _sntprintf_s(princStr, princ_len, _TRUNCATE, "%s%s", user,
430*7f2fe78bSCy Schubert m_defaultRealm);
431*7f2fe78bSCy Schubert if (m_princStr != NULL && (_tcscmp(princStr, m_princStr) == 0)) {
432*7f2fe78bSCy Schubert // this string is already added, ok to just bail
433*7f2fe78bSCy Schubert delete[] princStr;
434*7f2fe78bSCy Schubert } else {
435*7f2fe78bSCy Schubert if (m_princStr != NULL) {
436*7f2fe78bSCy Schubert // get rid of the old suggestion
437*7f2fe78bSCy Schubert m_enumString->RemoveString(m_princStr);
438*7f2fe78bSCy Schubert delete[] m_princStr;
439*7f2fe78bSCy Schubert }
440*7f2fe78bSCy Schubert // add the new one
441*7f2fe78bSCy Schubert m_enumString->AddString(princStr);
442*7f2fe78bSCy Schubert if (m_acdd != NULL)
443*7f2fe78bSCy Schubert m_acdd->ResetEnumerator();
444*7f2fe78bSCy Schubert m_princStr = princStr;
445*7f2fe78bSCy Schubert }
446*7f2fe78bSCy Schubert }
447*7f2fe78bSCy Schubert }
448*7f2fe78bSCy Schubert
AdjustRealmCase(LPTSTR princStr,LPTSTR realmStr)449*7f2fe78bSCy Schubert bool AdjustRealmCase(LPTSTR princStr, LPTSTR realmStr)
450*7f2fe78bSCy Schubert {
451*7f2fe78bSCy Schubert bool bChanged = StrToUpper(realmStr);
452*7f2fe78bSCy Schubert if (bChanged) {
453*7f2fe78bSCy Schubert DWORD selStart, selEnd;
454*7f2fe78bSCy Schubert ::SendMessage(m_hwnd, EM_GETSEL, (WPARAM)&selStart,
455*7f2fe78bSCy Schubert (LPARAM)&selEnd);
456*7f2fe78bSCy Schubert ::SetWindowText(m_hwnd, princStr);
457*7f2fe78bSCy Schubert ::SendMessage(m_hwnd, EM_SETSEL, (WPARAM)selStart, (LPARAM)selEnd);
458*7f2fe78bSCy Schubert }
459*7f2fe78bSCy Schubert return bChanged;
460*7f2fe78bSCy Schubert }
461*7f2fe78bSCy Schubert
ProcessText()462*7f2fe78bSCy Schubert bool ProcessText()
463*7f2fe78bSCy Schubert {
464*7f2fe78bSCy Schubert bool bChanged = false;
465*7f2fe78bSCy Schubert int text_len = GetWindowTextLength(m_hwnd);
466*7f2fe78bSCy Schubert if (text_len > 0) {
467*7f2fe78bSCy Schubert LPTSTR str = new TCHAR [++text_len];
468*7f2fe78bSCy Schubert if (str != NULL) {
469*7f2fe78bSCy Schubert GetWindowText(m_hwnd, str, text_len);
470*7f2fe78bSCy Schubert LPTSTR realmStr = strchr(str, '@');
471*7f2fe78bSCy Schubert if (realmStr != NULL) {
472*7f2fe78bSCy Schubert ++realmStr;
473*7f2fe78bSCy Schubert if (*realmStr == 0) {
474*7f2fe78bSCy Schubert SuggestDefaultRealm(str);
475*7f2fe78bSCy Schubert }
476*7f2fe78bSCy Schubert else if (m_bUpperCaseRealm) {
477*7f2fe78bSCy Schubert AdjustRealmCase(str, realmStr);
478*7f2fe78bSCy Schubert bChanged = true;
479*7f2fe78bSCy Schubert }
480*7f2fe78bSCy Schubert }
481*7f2fe78bSCy Schubert delete[] str;
482*7f2fe78bSCy Schubert }
483*7f2fe78bSCy Schubert }
484*7f2fe78bSCy Schubert return bChanged;
485*7f2fe78bSCy Schubert }
486*7f2fe78bSCy Schubert
WindowProc(UINT msg,WPARAM wp,LPARAM lp,LRESULT * lr)487*7f2fe78bSCy Schubert virtual bool WindowProc(UINT msg, WPARAM wp, LPARAM lp, LRESULT *lr)
488*7f2fe78bSCy Schubert {
489*7f2fe78bSCy Schubert bool bChanged = false;
490*7f2fe78bSCy Schubert switch (msg) {
491*7f2fe78bSCy Schubert case WM_COMMAND:
492*7f2fe78bSCy Schubert if ((LOWORD(wp)==m_ctrl_id) &&
493*7f2fe78bSCy Schubert (HIWORD(wp)==EN_CHANGE)) {
494*7f2fe78bSCy Schubert if ((!m_ignore_change++) && ProcessText()) {
495*7f2fe78bSCy Schubert bChanged = true;
496*7f2fe78bSCy Schubert *lr = 0;
497*7f2fe78bSCy Schubert }
498*7f2fe78bSCy Schubert m_ignore_change--;
499*7f2fe78bSCy Schubert }
500*7f2fe78bSCy Schubert default:
501*7f2fe78bSCy Schubert break;
502*7f2fe78bSCy Schubert }
503*7f2fe78bSCy Schubert return bChanged;
504*7f2fe78bSCy Schubert }
505*7f2fe78bSCy Schubert
InitAutocomplete()506*7f2fe78bSCy Schubert void InitAutocomplete()
507*7f2fe78bSCy Schubert {
508*7f2fe78bSCy Schubert CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
509*7f2fe78bSCy Schubert
510*7f2fe78bSCy Schubert // read strings from registry
511*7f2fe78bSCy Schubert LPTSTR *princs = NULL;
512*7f2fe78bSCy Schubert int count = 0;
513*7f2fe78bSCy Schubert getPrincipalList(&princs, &count);
514*7f2fe78bSCy Schubert
515*7f2fe78bSCy Schubert // Create our custom IEnumString implementation
516*7f2fe78bSCy Schubert HRESULT hRes;
517*7f2fe78bSCy Schubert DynEnumString *pEnumString = new DynEnumString(count, princs);
518*7f2fe78bSCy Schubert if (princs)
519*7f2fe78bSCy Schubert freePrincipalList(princs, count);
520*7f2fe78bSCy Schubert
521*7f2fe78bSCy Schubert m_enumString = pEnumString;
522*7f2fe78bSCy Schubert
523*7f2fe78bSCy Schubert // Create and initialize IAutoComplete object using IEnumString
524*7f2fe78bSCy Schubert IAutoComplete *pac = NULL;
525*7f2fe78bSCy Schubert hRes = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER,
526*7f2fe78bSCy Schubert IID_PPV_ARGS(&pac));
527*7f2fe78bSCy Schubert if (pac != NULL) {
528*7f2fe78bSCy Schubert pac->Init(m_hwnd, pEnumString, NULL, NULL);
529*7f2fe78bSCy Schubert
530*7f2fe78bSCy Schubert IAutoCompleteDropDown* pacdd = NULL;
531*7f2fe78bSCy Schubert hRes = pac->QueryInterface(IID_IAutoCompleteDropDown, (LPVOID*)&pacdd);
532*7f2fe78bSCy Schubert pac->Release();
533*7f2fe78bSCy Schubert m_acdd = pacdd;
534*7f2fe78bSCy Schubert }
535*7f2fe78bSCy Schubert }
536*7f2fe78bSCy Schubert
DestroyAutocomplete()537*7f2fe78bSCy Schubert void DestroyAutocomplete()
538*7f2fe78bSCy Schubert {
539*7f2fe78bSCy Schubert if (m_acdd != NULL)
540*7f2fe78bSCy Schubert m_acdd->Release();
541*7f2fe78bSCy Schubert if (m_enumString != NULL)
542*7f2fe78bSCy Schubert m_enumString->Release();
543*7f2fe78bSCy Schubert }
544*7f2fe78bSCy Schubert
545*7f2fe78bSCy Schubert int m_ignore_change;
546*7f2fe78bSCy Schubert bool m_bUpperCaseRealm;
547*7f2fe78bSCy Schubert LPTSTR m_defaultRealm;
548*7f2fe78bSCy Schubert LPTSTR m_princStr;
549*7f2fe78bSCy Schubert krb5_context m_ctx;
550*7f2fe78bSCy Schubert DynEnumString *m_enumString;
551*7f2fe78bSCy Schubert IAutoCompleteDropDown *m_acdd;
552*7f2fe78bSCy Schubert };
553*7f2fe78bSCy Schubert
554*7f2fe78bSCy Schubert
555*7f2fe78bSCy Schubert
Leash_pec_add_principal(char * principal)556*7f2fe78bSCy Schubert extern "C" void Leash_pec_add_principal(char *principal)
557*7f2fe78bSCy Schubert {
558*7f2fe78bSCy Schubert // write princ to registry
559*7f2fe78bSCy Schubert HKEY hKey;
560*7f2fe78bSCy Schubert unsigned long rc = RegCreateKeyEx(HKEY_CURRENT_USER,
561*7f2fe78bSCy Schubert LEASH_REGISTRY_PRINCIPALS_KEY_NAME,
562*7f2fe78bSCy Schubert 0, 0, 0, KEY_WRITE, 0, &hKey, 0);
563*7f2fe78bSCy Schubert if (rc) {
564*7f2fe78bSCy Schubert // TODO: log failure
565*7f2fe78bSCy Schubert return;
566*7f2fe78bSCy Schubert }
567*7f2fe78bSCy Schubert rc = RegSetValueEx(hKey, principal, 0, REG_NONE, NULL, 0);
568*7f2fe78bSCy Schubert if (rc) {
569*7f2fe78bSCy Schubert // TODO: log failure
570*7f2fe78bSCy Schubert }
571*7f2fe78bSCy Schubert if (hKey)
572*7f2fe78bSCy Schubert RegCloseKey(hKey);
573*7f2fe78bSCy Schubert }
574*7f2fe78bSCy Schubert
Leash_pec_clear_history(void * pec)575*7f2fe78bSCy Schubert extern "C" void Leash_pec_clear_history(void *pec)
576*7f2fe78bSCy Schubert {
577*7f2fe78bSCy Schubert // clear princs from registry
578*7f2fe78bSCy Schubert RegDeleteKey(HKEY_CURRENT_USER,
579*7f2fe78bSCy Schubert LEASH_REGISTRY_PRINCIPALS_KEY_NAME);
580*7f2fe78bSCy Schubert // ...and from the specified widget
581*7f2fe78bSCy Schubert static_cast<PrincipalEditControl *>(pec)->ClearHistory();
582*7f2fe78bSCy Schubert }
583*7f2fe78bSCy Schubert
584*7f2fe78bSCy Schubert
Leash_pec_create(HWND hEdit)585*7f2fe78bSCy Schubert extern "C" void *Leash_pec_create(HWND hEdit)
586*7f2fe78bSCy Schubert {
587*7f2fe78bSCy Schubert return new PrincipalEditControl(
588*7f2fe78bSCy Schubert hEdit,
589*7f2fe78bSCy Schubert Leash_get_default_uppercaserealm() ? true : false);
590*7f2fe78bSCy Schubert }
591*7f2fe78bSCy Schubert
Leash_pec_destroy(void * pec)592*7f2fe78bSCy Schubert extern "C" void Leash_pec_destroy(void *pec)
593*7f2fe78bSCy Schubert {
594*7f2fe78bSCy Schubert if (pec != NULL)
595*7f2fe78bSCy Schubert delete ((PrincipalEditControl *)pec);
596*7f2fe78bSCy Schubert }
597