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