xref: /reactos/dll/win32/imm32/ctf.c (revision b3194e32)
1 /*
2  * PROJECT:     ReactOS IMM32
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     Implementing the IMM32 Cicero-aware Text Framework (CTF)
5  * COPYRIGHT:   Copyright 2022-2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6  */
7 
8 #include "precomp.h"
9 
10 WINE_DEFAULT_DEBUG_CHANNEL(imm);
11 
12 /*
13  * NOTE: Microsoft CTF protocol has vulnerability.
14  *       If insecure, we don't follow the dangerous design.
15  *
16  * https://www.zdnet.com/article/vulnerability-in-microsoft-ctf-protocol-goes-back-to-windows-xp/
17  * https://googleprojectzero.blogspot.com/2019/08/down-rabbit-hole.html
18  */
19 
20 /*
21  * TSF stands for "Text Services Framework". "Cicero" is the code name of TSF.
22  * CTF stands for "Cicero-aware Text Framework".
23  *
24  * Comparing with old-style IMM IME, the combination of CTF IME and TSF provides
25  * new-style and high-level input method.
26  *
27  * The CTF IME file is a DLL file that the software developer distributes.
28  * The export functions of the CTF IME file are defined in "CtfImeTable.h" of
29  * this folder.
30  */
31 
32 /* The instance of the CTF IME file */
33 HINSTANCE g_hCtfIme = NULL;
34 
35 /* Define the function types (FN_...) for CTF IME functions */
36 #undef DEFINE_CTF_IME_FN
37 #define DEFINE_CTF_IME_FN(func_name, ret_type, params) \
38     typedef ret_type (WINAPI *FN_##func_name)params;
39 #include "CtfImeTable.h"
40 
41 /* Define the global variables (g_pfn...) for CTF IME functions */
42 #undef DEFINE_CTF_IME_FN
43 #define DEFINE_CTF_IME_FN(func_name, ret_type, params) \
44     FN_##func_name g_pfn##func_name = NULL;
45 #include "CtfImeTable.h"
46 
47 /* The macro that gets the variable name from the CTF IME function name */
48 #define CTF_IME_FN(func_name) g_pfn##func_name
49 
50 /* The type of ApphelpCheckIME function in apphelp.dll */
51 typedef BOOL (WINAPI *FN_ApphelpCheckIME)(_In_z_ LPCWSTR AppName);
52 
53 /* FIXME: This is kernel32 function. We have to declare this in some header. */
54 BOOL WINAPI
55 BaseCheckAppcompatCache(_In_z_ LPCWSTR ApplicationName,
56                         _In_ HANDLE FileHandle,
57                         _In_opt_z_ LPCWSTR Environment,
58                         _Out_ PULONG pdwReason);
59 
60 /***********************************************************************
61  * This function checks whether the app's IME is disabled by application
62  * compatibility patcher.
63  */
64 BOOL
65 Imm32CheckAndApplyAppCompat(
66     _In_ ULONG dwReason,
67     _In_z_ LPCWSTR pszAppName)
68 {
69     HINSTANCE hinstApphelp;
70     FN_ApphelpCheckIME pApphelpCheckIME;
71 
72     /* Query the application compatibility patcher */
73     if (BaseCheckAppcompatCache(pszAppName, INVALID_HANDLE_VALUE, NULL, &dwReason))
74         return TRUE; /* The app's IME is not disabled */
75 
76     /* Load apphelp.dll if necessary */
77     hinstApphelp = GetModuleHandleW(L"apphelp.dll");
78     if (!hinstApphelp)
79     {
80         hinstApphelp = LoadLibraryW(L"apphelp.dll");
81         if (!hinstApphelp)
82             return TRUE; /* There is no apphelp.dll. The app's IME is not disabled */
83     }
84 
85     /* Is ApphelpCheckIME implemented? */
86     pApphelpCheckIME = (FN_ApphelpCheckIME)GetProcAddress(hinstApphelp, "ApphelpCheckIME");
87     if (!pApphelpCheckIME)
88         return TRUE; /* Not implemented. The app's IME is not disabled */
89 
90     /* Is the app's IME disabled or not? */
91     return pApphelpCheckIME(pszAppName);
92 }
93 
94 /***********************************************************************
95  * This function loads the CTF IME file if necessary and establishes
96  * communication with the CTF IME.
97  */
98 HINSTANCE
99 Imm32LoadCtfIme(VOID)
100 {
101     BOOL bSuccess = FALSE;
102     IMEINFOEX ImeInfoEx;
103     WCHAR szImeFile[MAX_PATH];
104 
105     /* Lock the IME interface */
106     RtlEnterCriticalSection(&gcsImeDpi);
107 
108     do
109     {
110         if (g_hCtfIme) /* Already loaded? */
111         {
112             bSuccess = TRUE;
113             break;
114         }
115 
116         /*
117          * NOTE: (HKL)0x04090409 is English US keyboard (default).
118          * The Cicero keyboard logically uses English US keyboard.
119          */
120         if (!ImmLoadLayout((HKL)ULongToHandle(0x04090409), &ImeInfoEx))
121             break;
122 
123         /* Build a path string in system32. The installed IME file must be in system32. */
124         Imm32GetSystemLibraryPath(szImeFile, _countof(szImeFile), ImeInfoEx.wszImeFile);
125 
126         /* Is the CTF IME disabled by app compatibility patcher? */
127         if (!Imm32CheckAndApplyAppCompat(0, szImeFile))
128             break; /* This IME is disabled */
129 
130         /* Load a CTF IME file */
131         g_hCtfIme = LoadLibraryW(szImeFile);
132         if (!g_hCtfIme)
133             break;
134 
135         /* Assume success */
136         bSuccess = TRUE;
137 
138         /* Retrieve the CTF IME functions */
139 #undef DEFINE_CTF_IME_FN
140 #define DEFINE_CTF_IME_FN(func_name, ret_type, params) \
141         CTF_IME_FN(func_name) = (FN_##func_name)GetProcAddress(g_hCtfIme, #func_name); \
142         if (!CTF_IME_FN(func_name)) \
143         { \
144             bSuccess = FALSE; /* Failed */ \
145             break; \
146         }
147 #include "CtfImeTable.h"
148     } while (0);
149 
150     /* Unload the CTF IME if failed */
151     if (!bSuccess)
152     {
153         /* Set NULL to the function pointers */
154 #undef DEFINE_CTF_IME_FN
155 #define DEFINE_CTF_IME_FN(func_name, ret_type, params) CTF_IME_FN(func_name) = NULL;
156 #include "CtfImeTable.h"
157 
158         if (g_hCtfIme)
159         {
160             FreeLibrary(g_hCtfIme);
161             g_hCtfIme = NULL;
162         }
163     }
164 
165     /* Unlock the IME interface */
166     RtlLeaveCriticalSection(&gcsImeDpi);
167 
168     return g_hCtfIme;
169 }
170 
171 /***********************************************************************
172  * This function calls the same name function of the CTF IME side.
173  */
174 HRESULT
175 CtfImeCreateThreadMgr(VOID)
176 {
177     if (!Imm32LoadCtfIme())
178         return E_FAIL;
179 
180     return CTF_IME_FN(CtfImeCreateThreadMgr)();
181 }
182 
183 /***********************************************************************
184  * This function calls the same name function of the CTF IME side.
185  */
186 HRESULT
187 CtfImeDestroyThreadMgr(VOID)
188 {
189     if (!Imm32LoadCtfIme())
190         return E_FAIL;
191 
192     return CTF_IME_FN(CtfImeDestroyThreadMgr)();
193 }
194 
195 /***********************************************************************
196  * This function calls the same name function of the CTF IME side.
197  */
198 HRESULT
199 CtfImeCreateInputContext(
200     _In_ HIMC hIMC)
201 {
202     if (!Imm32LoadCtfIme())
203         return E_FAIL;
204 
205     return CTF_IME_FN(CtfImeCreateInputContext)(hIMC);
206 }
207 
208 /***********************************************************************
209  * This function calls the same name function of the CTF IME side.
210  */
211 HRESULT
212 CtfImeDestroyInputContext(_In_ HIMC hIMC)
213 {
214     if (!Imm32LoadCtfIme())
215         return E_FAIL;
216 
217     return CTF_IME_FN(CtfImeDestroyInputContext)(hIMC);
218 }
219 
220 /***********************************************************************
221  * The callback function to activate CTF IMEs. Used in CtfAImmActivate.
222  */
223 static BOOL CALLBACK
224 Imm32EnumCreateCtfICProc(
225     _In_ HIMC hIMC,
226     _In_ LPARAM lParam)
227 {
228     UNREFERENCED_PARAMETER(lParam);
229     CtfImeCreateInputContext(hIMC);
230     return TRUE; /* Continue */
231 }
232 
233 /***********************************************************************
234  * This function calls CtfImeDestroyInputContext if possible.
235  */
236 HRESULT
237 CtfImmTIMDestroyInputContext(
238     _In_ HIMC hIMC)
239 {
240     if (!IS_CICERO_MODE() || (GetWin32ClientInfo()->dwCompatFlags2 & 2))
241         return E_NOINTERFACE;
242 
243     return CtfImeDestroyInputContext(hIMC);
244 }
245 
246 HRESULT
247 CtfImmTIMCreateInputContext(
248     _In_ HIMC hIMC)
249 {
250     TRACE("(%p)\n", hIMC);
251     return E_NOTIMPL;
252 }
253 
254 /***********************************************************************
255  *      CtfAImmActivate (IMM32.@)
256  *
257  * This function activates "Active IMM" (AIMM) and TSF.
258  */
259 HRESULT WINAPI
260 CtfAImmActivate(
261     _Out_opt_ HINSTANCE *phinstCtfIme)
262 {
263     HRESULT hr;
264     HINSTANCE hinstCtfIme;
265 
266     TRACE("(%p)\n", phinstCtfIme);
267 
268     /* Load a CTF IME file if necessary */
269     hinstCtfIme = Imm32LoadCtfIme();
270 
271     /* Create a thread manager of the CTF IME */
272     hr = CtfImeCreateThreadMgr();
273     if (hr == S_OK)
274     {
275         /* Update CI_... flags of the thread client info */
276         GetWin32ClientInfo()->CI_flags |= CI_AIMMACTIVATED; /* Activate AIMM */
277         GetWin32ClientInfo()->CI_flags &= ~CI_TSFDISABLED;  /* Enable TSF */
278 
279         /* Create the CTF input contexts */
280         ImmEnumInputContext(0, Imm32EnumCreateCtfICProc, 0);
281     }
282 
283     if (phinstCtfIme)
284         *phinstCtfIme = hinstCtfIme;
285 
286     return hr;
287 }
288 
289 /***********************************************************************
290  *      CtfAImmDeactivate (IMM32.@)
291  *
292  * This function de-activates "Active IMM" (AIMM) and TSF.
293  */
294 HRESULT WINAPI
295 CtfAImmDeactivate(
296     _In_ BOOL bDestroy)
297 {
298     HRESULT hr;
299 
300     if (!bDestroy)
301         return E_FAIL;
302 
303     hr = CtfImeDestroyThreadMgr();
304     if (hr == S_OK)
305     {
306         GetWin32ClientInfo()->CI_flags &= ~CI_AIMMACTIVATED; /* Deactivate AIMM */
307         GetWin32ClientInfo()->CI_flags |= CI_TSFDISABLED;    /* Disable TSF */
308     }
309 
310     return hr;
311 }
312 
313 /***********************************************************************
314  *		CtfImmIsCiceroEnabled (IMM32.@)
315  *
316  * @return TRUE if Cicero is enabled.
317  */
318 BOOL WINAPI
319 CtfImmIsCiceroEnabled(VOID)
320 {
321     return IS_CICERO_MODE();
322 }
323 
324 /***********************************************************************
325  *		CtfImmIsTextFrameServiceDisabled(IMM32.@)
326  *
327  * @return TRUE if TSF is disabled.
328  */
329 BOOL WINAPI
330 CtfImmIsTextFrameServiceDisabled(VOID)
331 {
332     return !!(GetWin32ClientInfo()->CI_flags & CI_TSFDISABLED);
333 }
334 
335 /***********************************************************************
336  *		CtfImmTIMActivate(IMM32.@)
337  */
338 HRESULT WINAPI
339 CtfImmTIMActivate(_In_ HKL hKL)
340 {
341     FIXME("(%p)\n", hKL);
342     return E_NOTIMPL;
343 }
344 
345 /***********************************************************************
346  *		CtfImmRestoreToolbarWnd(IMM32.@)
347  */
348 VOID WINAPI
349 CtfImmRestoreToolbarWnd(_In_ DWORD dwStatus)
350 {
351     FIXME("(0x%lx)\n", dwStatus);
352 }
353 
354 /***********************************************************************
355  *		CtfImmHideToolbarWnd(IMM32.@)
356  */
357 DWORD WINAPI
358 CtfImmHideToolbarWnd(VOID)
359 {
360     FIXME("()\n");
361     return 0;
362 }
363 
364 /***********************************************************************
365  *		CtfImmDispatchDefImeMessage(IMM32.@)
366  */
367 LRESULT WINAPI
368 CtfImmDispatchDefImeMessage(
369     _In_ HWND hWnd,
370     _In_ UINT uMsg,
371     _In_ WPARAM wParam,
372     _In_ LPARAM lParam)
373 {
374     /* FIXME("(%p, %u, %p, %p)\n", hWnd, uMsg, wParam, lParam); */
375     return 0;
376 }
377 
378 /***********************************************************************
379  *		CtfImmIsGuidMapEnable(IMM32.@)
380  */
381 BOOL WINAPI
382 CtfImmIsGuidMapEnable(
383     _In_ HIMC hIMC)
384 {
385     DWORD dwThreadId;
386     HKL hKL;
387     PIMEDPI pImeDpi;
388     BOOL ret = FALSE;
389 
390     TRACE("(%p)\n", hIMC);
391 
392     if (!IS_CICERO_MODE() || IS_16BIT_MODE())
393         return ret;
394 
395     dwThreadId = (DWORD)NtUserQueryInputContext(hIMC, QIC_INPUTTHREADID);
396     hKL = GetKeyboardLayout(dwThreadId);
397 
398     if (IS_IME_HKL(hKL))
399         return ret;
400 
401     pImeDpi = Imm32FindOrLoadImeDpi(hKL);
402     if (IS_NULL_UNEXPECTEDLY(pImeDpi))
403         return ret;
404 
405     ret = pImeDpi->CtfImeIsGuidMapEnable(hIMC);
406 
407     ImmUnlockImeDpi(pImeDpi);
408     return ret;
409 }
410 
411 /***********************************************************************
412  *		CtfImmGetGuidAtom(IMM32.@)
413  */
414 HRESULT WINAPI
415 CtfImmGetGuidAtom(
416     _In_ HIMC hIMC,
417     _In_ DWORD dwUnknown,
418     _Out_ LPDWORD pdwGuidAtom)
419 {
420     HRESULT hr = E_FAIL;
421     PIMEDPI pImeDpi;
422     DWORD dwThreadId;
423     HKL hKL;
424 
425     TRACE("(%p, 0xlX, %p)\n", hIMC, dwUnknown, pdwGuidAtom);
426 
427     *pdwGuidAtom = 0;
428 
429     if (!IS_CICERO_MODE() || IS_16BIT_MODE())
430         return hr;
431 
432     dwThreadId = (DWORD)NtUserQueryInputContext(hIMC, QIC_INPUTTHREADID);
433     hKL = GetKeyboardLayout(dwThreadId);
434     if (IS_IME_HKL(hKL))
435         return S_OK;
436 
437     pImeDpi = Imm32FindOrLoadImeDpi(hKL);
438     if (IS_NULL_UNEXPECTEDLY(pImeDpi))
439         return hr;
440 
441     hr = pImeDpi->CtfImeGetGuidAtom(hIMC, dwUnknown, pdwGuidAtom);
442 
443     ImmUnlockImeDpi(pImeDpi);
444     return hr;
445 }
446