Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5326 lines
146 KiB

  1. // iforms.cpp : Implementation of CIntelliForms
  2. #include "priv.h"
  3. #include <iehelpid.h>
  4. #include <pstore.h>
  5. #include "hlframe.h"
  6. #include "iformsp.h"
  7. #include "shldisp.h"
  8. #include "opsprof.h"
  9. #include "resource.h"
  10. #include <mluisupp.h>
  11. // {E161255A-37C3-11d2-BCAA-00C04FD929DB}
  12. static const GUID c_PStoreType =
  13. { 0xe161255a, 0x37c3, 0x11d2, { 0xbc, 0xaa, 0x0, 0xc0, 0x4f, 0xd9, 0x29, 0xdb } };
  14. const TCHAR c_szIntelliForms[] = TEXT("Internet Explorer");
  15. #define TF_IFORMS TF_CUSTOM2
  16. // ALLOW_SHELLUIOC_HOST code will allow us to host intelliforms
  17. // from the Shell UI OC (shuioc.cpp). This is used for the
  18. // HTML Find dialog
  19. #define ALLOW_SHELLUIOC_HOST
  20. CIntelliForms *GetIntelliFormsFromDoc(IHTMLDocument2 *pDoc2);
  21. inline void MyToLower(LPWSTR pwszStr)
  22. {
  23. if (g_fRunningOnNT)
  24. {
  25. CharLowerBuffW(pwszStr, lstrlenW(pwszStr));
  26. }
  27. else
  28. {
  29. // Ideally we would use the code page contained in the string instead of
  30. // the system code page.
  31. CHAR chBuf[MAX_PATH];
  32. SHUnicodeToAnsi(pwszStr, chBuf, ARRAYSIZE(chBuf));
  33. CharLowerBuffA(chBuf, lstrlenA(chBuf));
  34. SHAnsiToUnicode(chBuf, pwszStr, lstrlenW(pwszStr)+1);
  35. }
  36. }
  37. //=================== Exported functions =====================
  38. // Exported for inetCPL
  39. HRESULT ClearAutoSuggestForForms(DWORD dwClear)
  40. {
  41. CIntelliForms *pObj = new CIntelliForms();
  42. if (pObj)
  43. {
  44. HRESULT hr;
  45. hr = pObj->ClearStore(dwClear);
  46. pObj->Release();
  47. return hr;
  48. }
  49. return E_OUTOFMEMORY;
  50. }
  51. HRESULT SetIdAutoSuggestForForms(const GUID *pguidId, void *pIntelliForms)
  52. {
  53. CIntelliForms *pThis = (CIntelliForms *)pIntelliForms;
  54. if (pThis)
  55. {
  56. if (GUID_NULL == *pguidId)
  57. {
  58. pThis->m_guidUserId = c_PStoreType;
  59. }
  60. else
  61. {
  62. pThis->m_guidUserId = *pguidId;
  63. }
  64. return S_OK;
  65. }
  66. return E_FAIL;
  67. }
  68. // called from iedisp.cpp
  69. void AttachIntelliForms(void *pvOmWindow, HWND hwnd, IHTMLDocument2 *pDoc2, void **ppIntelliForms)
  70. {
  71. static DWORD s_dwAdminRestricted = 0xFE;
  72. CIEFrameAuto::COmWindow *pOmWindow = (CIEFrameAuto::COmWindow *)pvOmWindow;
  73. ASSERT(ppIntelliForms && *ppIntelliForms==NULL);
  74. if (s_dwAdminRestricted == 0xFE)
  75. {
  76. s_dwAdminRestricted = CIntelliForms::IsAdminRestricted(c_szRegValFormSuggestRestrict) &&
  77. CIntelliForms::IsAdminRestricted(c_szRegValSavePasswords);
  78. }
  79. if (s_dwAdminRestricted)
  80. {
  81. return;
  82. }
  83. // If we're not hosted by internet explorer, we don't want to enable Intelliforms
  84. // unless dochost explicitly overrides this
  85. if (!IsInternetExplorerApp() &&
  86. !(pOmWindow && (DOCHOSTUIFLAG_ENABLE_FORMS_AUTOCOMPLETE & pOmWindow->IEFrameAuto()->GetDocHostFlags())))
  87. {
  88. return;
  89. }
  90. if (!hwnd && pOmWindow)
  91. {
  92. pOmWindow->IEFrameAuto()->get_HWND((LONG_PTR *)&hwnd);
  93. }
  94. if (!hwnd || !pDoc2 || !ppIntelliForms || (*ppIntelliForms != NULL))
  95. {
  96. return;
  97. }
  98. #ifndef ALLOW_SHELLUIOC_HOST
  99. if (!pOmWindow)
  100. {
  101. return;
  102. }
  103. #else
  104. if (!pOmWindow)
  105. {
  106. // Script is asking to attach to this document
  107. // Deny their request if another CIntelliForms is already attached
  108. if (NULL != GetIntelliFormsFromDoc(pDoc2))
  109. {
  110. return;
  111. }
  112. }
  113. #endif
  114. CIntelliForms *pForms = new CIntelliForms();
  115. if (pForms)
  116. {
  117. if (SUCCEEDED(pForms->Init(pOmWindow, pDoc2, hwnd)))
  118. {
  119. *ppIntelliForms = pForms;
  120. }
  121. else
  122. {
  123. pForms->Release();
  124. }
  125. }
  126. }
  127. void ReleaseIntelliForms(void *pIntelliForms)
  128. {
  129. CIntelliForms *pForms = (CIntelliForms *) pIntelliForms;
  130. if (pForms)
  131. {
  132. pForms->UnInit();
  133. pForms->Release();
  134. }
  135. }
  136. HRESULT IntelliFormsActiveElementChanged(void *pIntelliForms, IHTMLElement * pHTMLElement)
  137. {
  138. CIntelliForms *pForms = (CIntelliForms *) pIntelliForms;
  139. if (pForms)
  140. return pForms->ActiveElementChanged(pHTMLElement);
  141. return E_FAIL;
  142. }
  143. INT_PTR CALLBACK AskUserDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  144. HRESULT IncrementAskCount();
  145. HRESULT IntelliFormsDoAskUser(HWND hwndBrowser, void *pv)
  146. {
  147. // Make sure that we haven't asked them yet
  148. if (S_OK == IncrementAskCount())
  149. {
  150. // Modal dialog to ask the user our little question
  151. SHFusionDialogBoxParam(MLGetHinst(), MAKEINTRESOURCE(IDD_AUTOSUGGEST_ASK_USER),
  152. hwndBrowser, AskUserDlgProc, NULL);
  153. }
  154. return S_OK;
  155. }
  156. // Linked list of active CIntelliform objects to translate from
  157. // IHTMLDocument2->CIntelliforms when script calls window.external.saveforms
  158. // Protected by g_csDll
  159. CIntelliForms *g_pIntelliFormsFirst=NULL;
  160. // Translate this pDoc2 to an existing instance of CIntelliForms
  161. // Will return NULL if no CIntelliForms attached to this doc
  162. // NO REFCOUNT IS ADDED TO THE RETURN
  163. CIntelliForms *GetIntelliFormsFromDoc(IHTMLDocument2 *pDoc2)
  164. {
  165. if (!pDoc2)
  166. {
  167. return NULL;
  168. }
  169. ENTERCRITICAL;
  170. CIntelliForms *pNext = g_pIntelliFormsFirst;
  171. IUnknown *punkDoc;
  172. CIntelliForms *pIForms=NULL;
  173. pDoc2->QueryInterface(IID_IUnknown, (void **)&punkDoc);
  174. if (punkDoc)
  175. {
  176. while (pNext)
  177. {
  178. if (pNext->GetDocument() == punkDoc)
  179. {
  180. pIForms = pNext;
  181. break;
  182. }
  183. pNext=pNext->GetNext();
  184. }
  185. punkDoc->Release();
  186. }
  187. LEAVECRITICAL;
  188. return pIForms;
  189. }
  190. // called from shuioc.cpp
  191. HRESULT IntelliFormsSaveForm(IHTMLDocument2 *pDoc2, VARIANT *pvarForm)
  192. {
  193. HRESULT hrRet = S_FALSE;
  194. IHTMLFormElement *pForm=NULL;
  195. CIntelliForms *pIForms=NULL;
  196. if (pvarForm->vt == VT_DISPATCH)
  197. {
  198. pvarForm->pdispVal->QueryInterface(IID_IHTMLFormElement, (void **)&pForm);
  199. }
  200. if (pForm)
  201. {
  202. pIForms = GetIntelliFormsFromDoc(pDoc2);
  203. if (pIForms)
  204. {
  205. // Should validate that pIForms was created on this thread
  206. hrRet = pIForms->ScriptSubmit(pForm);
  207. }
  208. pForm->Release();
  209. }
  210. return hrRet;
  211. }
  212. const TCHAR c_szYes[] = TEXT("yes");
  213. const TCHAR c_szNo[] = TEXT("no");
  214. INT_PTR AutoSuggestDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
  215. #ifdef CHECKBOX_HELP
  216. const DWORD c_aIFormsHelpIds[] = {
  217. IDC_AUTOSUGGEST_NEVER, IDH_INTELLIFORM_PW_PROMPT,
  218. 0, 0
  219. };
  220. #endif
  221. const WCHAR c_wszVCardPrefix[] = L"vCard.";
  222. BOOL CIntelliForms::CAutoSuggest::s_fRegisteredWndClass = FALSE;
  223. // Must be in same order as EVENT enum type
  224. // All events we need to sink anywhere
  225. CEventSinkCallback::EventSinkEntry CEventSinkCallback::EventsToSink[] =
  226. {
  227. { EVENT_KEYDOWN, L"onkeydown", L"keydown" },
  228. { EVENT_KEYPRESS, L"onkeypress", L"keypress" },
  229. { EVENT_MOUSEDOWN, L"onmousedown", L"mousedown"},
  230. { EVENT_DBLCLICK, L"ondblclick", L"dblclick" },
  231. { EVENT_FOCUS, L"onfocus", L"focus" },
  232. { EVENT_BLUR, L"onblur", L"blur" },
  233. { EVENT_SUBMIT, L"onsubmit", L"submit" },
  234. { EVENT_SCROLL, L"onscroll", L"scroll" },
  235. { EVENT_COMPOSITION,NULL, L"composition"},
  236. { EVENT_NOTIFY, NULL, L"notify" },
  237. };
  238. // Fake edit window class
  239. const WCHAR c_szEditWndClass[] = TEXT("IntelliFormClass");
  240. // Minimum dropdown width
  241. const int MINIMUM_WIDTH=100;
  242. // Submit number to ask user to enable us
  243. const int ASK_USER_ON_SUBMIT_N = 2;
  244. void GetStuffFromEle(IUnknown *punkEle, IHTMLWindow2 **ppWin2, IHTMLDocument2 **ppDoc2)
  245. {
  246. if (ppWin2)
  247. *ppWin2=NULL;
  248. if (ppDoc2)
  249. *ppDoc2=NULL;
  250. IHTMLElement *pEle=NULL;
  251. punkEle->QueryInterface(IID_IHTMLElement, (void **)&pEle);
  252. if (pEle)
  253. {
  254. IDispatch *pDisp=NULL;
  255. pEle->get_document(&pDisp);
  256. if (pDisp)
  257. {
  258. IHTMLDocument2 *pDoc2 = NULL;
  259. pDisp->QueryInterface(IID_IHTMLDocument2, (void **)&pDoc2);
  260. if (pDoc2)
  261. {
  262. if (ppWin2)
  263. {
  264. pDoc2->get_parentWindow(ppWin2);
  265. }
  266. if (ppDoc2)
  267. {
  268. *ppDoc2 = pDoc2;
  269. }
  270. else
  271. {
  272. pDoc2->Release();
  273. }
  274. }
  275. pDisp->Release();
  276. }
  277. pEle->Release();
  278. }
  279. }
  280. void Win3FromDoc2(IHTMLDocument2 *pDoc2, IHTMLWindow3 **ppWin3)
  281. {
  282. *ppWin3=NULL;
  283. IHTMLWindow2 *pWin2=NULL;
  284. if (SUCCEEDED(pDoc2->get_parentWindow(&pWin2)) && pWin2)
  285. {
  286. pWin2->QueryInterface(IID_IHTMLWindow3, (void **)ppWin3);
  287. pWin2->Release();
  288. }
  289. }
  290. // Increment the count of whether we've asked the user to enable us or not. We won't
  291. // ask them on the first form submit since installing ie5.
  292. HRESULT IncrementAskCount()
  293. {
  294. DWORD dwData, dwSize, dwType;
  295. dwSize = sizeof(dwData);
  296. // c_szRegValAskUser contains the number of form submits
  297. // 0 means we've already asked user whether to enable us
  298. // 1 means we've already had one form submit, and should ask the user this time
  299. // value not present means we haven't had any form submits
  300. if ((ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER,
  301. c_szRegKeyIntelliForms, c_szRegValAskUser, &dwType, &dwData, &dwSize)) &&
  302. dwType == REG_DWORD)
  303. {
  304. if (dwData == 0)
  305. {
  306. // Shouldn't get this far
  307. TraceMsg(TF_IFORMS|TF_WARNING, "IntelliFormsDoAskUser: Already asked user");
  308. return E_FAIL; // Already asked user
  309. }
  310. }
  311. else
  312. {
  313. dwData = 0;
  314. }
  315. if (dwData+1 < ASK_USER_ON_SUBMIT_N)
  316. {
  317. dwData ++;
  318. SHSetValue(HKEY_CURRENT_USER, c_szRegKeyIntelliForms, c_szRegValAskUser,
  319. REG_DWORD, &dwData, sizeof(dwData));
  320. TraceMsg(TF_IFORMS, "IntelliFormsDoAskUser incrementing submit count. Not asking user.");
  321. return E_FAIL; // Don't ask the user
  322. }
  323. return S_OK; // Let's ask the user
  324. }
  325. /////////////////////////////////////////////////////////////////////////////
  326. // CIntelliForms
  327. CIntelliForms::CIntelliForms()
  328. {
  329. TraceMsg(TF_IFORMS, "CIntelliForms::CIntelliForms");
  330. m_cRef = 1;
  331. m_iRestoredIndex = -1;
  332. m_fRestricted = IsAdminRestricted(c_szRegValFormSuggestRestrict);
  333. m_fRestrictedPW = IsAdminRestricted(c_szRegValSavePasswords);
  334. m_guidUserId = c_PStoreType;
  335. // Add us to global linked list
  336. ENTERCRITICAL;
  337. m_pNext = g_pIntelliFormsFirst;
  338. g_pIntelliFormsFirst = this;
  339. LEAVECRITICAL;
  340. }
  341. CIntelliForms::~CIntelliForms()
  342. {
  343. // Remove us from global linked list
  344. ENTERCRITICAL;
  345. CIntelliForms *pLast=NULL, *pNext = g_pIntelliFormsFirst;
  346. while (pNext && pNext != this)
  347. {
  348. pLast = pNext;
  349. pNext=pNext->m_pNext;
  350. }
  351. ASSERT(pNext == this);
  352. if (pNext)
  353. {
  354. if (pLast)
  355. {
  356. pLast->m_pNext = m_pNext;
  357. }
  358. else
  359. {
  360. g_pIntelliFormsFirst = m_pNext;
  361. }
  362. }
  363. LEAVECRITICAL;
  364. TraceMsg(TF_IFORMS, "CIntelliForms::~CIntelliForms");
  365. }
  366. // Called when document is ready to attach to
  367. // We don't support re-initting
  368. HRESULT CIntelliForms::Init(CIEFrameAuto::COmWindow *pOmWindow, IHTMLDocument2 *pDoc2, HWND hwndBrowser)
  369. {
  370. HRESULT hr;
  371. ASSERT(pDoc2 && hwndBrowser);
  372. #ifndef ALLOW_SHELLUIOC_HOST
  373. if (pOmWindow == NULL)
  374. {
  375. return E_INVALIDARG;
  376. }
  377. #endif
  378. // Connect to get active element changed notifications
  379. m_pOmWindow = pOmWindow;
  380. if (pOmWindow)
  381. {
  382. pOmWindow->AddRef();
  383. }
  384. m_pDoc2 = pDoc2;
  385. pDoc2->AddRef();
  386. pDoc2->QueryInterface(IID_IUnknown, (void **)&m_punkDoc2);
  387. m_hwndBrowser = hwndBrowser;
  388. m_iRestoredIndex = -1;
  389. hr = S_OK;
  390. #ifdef ALLOW_SHELLUIOC_HOST
  391. if (!pOmWindow && (hr == S_OK))
  392. {
  393. // Check for the current active element since the page is requesting
  394. // us to attach to an existing document
  395. IHTMLElement *pHTMLElement = NULL;
  396. m_pDoc2->get_activeElement(&pHTMLElement);
  397. ActiveElementChanged(pHTMLElement);
  398. if (pHTMLElement)
  399. pHTMLElement->Release();
  400. }
  401. #endif
  402. GetUrl(); // Init Url member variables so we don't get the url on the
  403. // wrong thread in the FillEnumerator call
  404. TraceMsg(TF_IFORMS, "CIntelliForms::Init hr=%08x", hr);
  405. return hr;
  406. }
  407. HRESULT CIntelliForms::UnInit()
  408. {
  409. if (m_fInModalDialog)
  410. {
  411. // Lifetime management. If UnInit is called during modal dialog, we keep ourself
  412. // alive. Use Enter/LeaveModalDialog to ensure correct use
  413. ASSERT(m_fUninitCalled == FALSE); // Should only be called once...
  414. m_fUninitCalled = TRUE;
  415. return S_FALSE;
  416. }
  417. // Destroy this now, before we free other member variables, to ensure CAutoSuggest doesn't
  418. // try to access us on a second thread.
  419. if (m_pAutoSuggest)
  420. {
  421. m_pAutoSuggest->SetParent(NULL);
  422. m_pAutoSuggest->DetachFromInput();
  423. delete m_pAutoSuggest;
  424. m_pAutoSuggest = NULL;
  425. }
  426. if (m_hdpaForms && m_pSink)
  427. {
  428. IHTMLElement2 *pEle2;
  429. EVENTS events[] = { EVENT_SUBMIT };
  430. for (int i=DPA_GetPtrCount(m_hdpaForms)-1; i>=0; i--)
  431. {
  432. ((IHTMLFormElement *)(DPA_FastGetPtr(m_hdpaForms, i)))->QueryInterface(IID_IHTMLElement2, (void **)&pEle2);
  433. m_pSink->UnSinkEvents(pEle2, ARRAYSIZE(events), events);
  434. pEle2->Release();
  435. }
  436. }
  437. SysFreeString(m_bstrFullUrl);
  438. m_bstrFullUrl = NULL;
  439. SysFreeString(m_bstrUrl);
  440. m_bstrUrl = NULL;
  441. if (m_pwszUrlHash)
  442. {
  443. LocalFree((void *)m_pwszUrlHash);
  444. m_pwszUrlHash = NULL;
  445. }
  446. // Unhook regular event sink
  447. if (m_pSink)
  448. {
  449. #ifndef ALLOW_SHELLUIOC_HOST
  450. ASSERT(m_pOmWindow);
  451. #endif
  452. if (m_pOmWindow)
  453. {
  454. IHTMLWindow3 *pWin3=NULL;
  455. Win3FromDoc2(m_pDoc2, &pWin3);
  456. if (pWin3)
  457. {
  458. EVENTS events[] = { EVENT_SCROLL };
  459. m_pSink->UnSinkEvents(pWin3, ARRAYSIZE(events), events);
  460. pWin3->Release();
  461. }
  462. }
  463. m_pSink->SetParent(NULL);
  464. m_pSink->Release();
  465. m_pSink=NULL;
  466. }
  467. // Unhook designer event sink
  468. if (m_pEditSink)
  469. {
  470. m_pEditSink->Attach(NULL);
  471. m_pEditSink->SetParent(NULL);
  472. m_pEditSink->Release();
  473. m_pEditSink=NULL;
  474. }
  475. // SAFERELEASE (and ATOMICRELEASE) macro in shdocvw is actually function which requires IUnknown
  476. ATOMICRELEASET(m_pOmWindow, CIEFrameAuto::COmWindow);
  477. SAFERELEASE(m_pDoc2);
  478. SAFERELEASE(m_punkDoc2);
  479. FreeElementList();
  480. FreeFormList();
  481. if (m_pslPasswords)
  482. {
  483. delete m_pslPasswords;
  484. m_pslPasswords = NULL;
  485. }
  486. ReleasePStore();
  487. TraceMsg(TF_IFORMS, "CIntelliForms::UnInit");
  488. return S_OK;
  489. }
  490. STDMETHODIMP CIntelliForms::QueryInterface(REFIID riid, void **ppv)
  491. {
  492. *ppv = NULL;
  493. if ((IID_IPropertyNotifySink == riid) ||
  494. (IID_IUnknown == riid))
  495. {
  496. *ppv = (IPropertyNotifySink *)this;
  497. }
  498. if (NULL != *ppv)
  499. {
  500. ((IUnknown *)*ppv)->AddRef();
  501. return S_OK;
  502. }
  503. return E_NOINTERFACE;
  504. }
  505. STDMETHODIMP_(ULONG) CIntelliForms::AddRef(void)
  506. {
  507. return ++m_cRef;
  508. }
  509. STDMETHODIMP_(ULONG) CIntelliForms::Release(void)
  510. {
  511. if (--m_cRef == 0)
  512. {
  513. delete this;
  514. return 0;
  515. }
  516. return m_cRef;
  517. }
  518. HRESULT CIntelliForms::ActiveElementChanged(IHTMLElement * pHTMLElement)
  519. {
  520. ASSERT(m_pDoc2);
  521. // Detach the AutoSuggest object and destroy it
  522. if (m_pAutoSuggest)
  523. {
  524. m_pAutoSuggest->DetachFromInput();
  525. delete m_pAutoSuggest;
  526. m_pAutoSuggest=NULL;
  527. }
  528. if (m_pDoc2)
  529. {
  530. IHTMLElement *pEle=pHTMLElement;
  531. if (pEle)
  532. {
  533. BOOL fPassword=FALSE;
  534. IHTMLInputTextElement *pTextEle = NULL;
  535. if (SUCCEEDED(ShouldAttachToElement(pEle, TRUE, NULL, &pTextEle, NULL, &fPassword)))
  536. {
  537. BOOL fEnabledInCPL = IsEnabledInCPL();
  538. BOOL fEnabledPW = IsEnabledRestorePW();
  539. // We need to watch user activity if...
  540. if (fEnabledInCPL || // Intelliforms is enabled
  541. fEnabledPW || // Or Restore Passwords is enabled
  542. !AskedUserToEnable()) // Or we may ask them to enable us
  543. {
  544. m_pAutoSuggest = new CAutoSuggest(this, fEnabledInCPL, fEnabledPW);
  545. if (m_pAutoSuggest)
  546. {
  547. if (!m_pSink)
  548. {
  549. m_pSink = new CEventSink(this);
  550. if (m_pSink)
  551. {
  552. #ifndef ALLOW_SHELLUIOC_HOST
  553. // Don't sink scroll event if hosted by ShellUIOC
  554. // or jscript.dll asserts on unload
  555. ASSERT(m_pOmWindow);
  556. #endif
  557. if (m_pOmWindow)
  558. {
  559. IHTMLWindow3 *pWin3=NULL;
  560. Win3FromDoc2(m_pDoc2, &pWin3);
  561. if (pWin3)
  562. {
  563. EVENTS events[] = { EVENT_SCROLL };
  564. m_pSink->SinkEvents(pWin3, ARRAYSIZE(events), events);
  565. pWin3->Release();
  566. }
  567. }
  568. }
  569. }
  570. // Hook up designer sink for IME event
  571. if (!m_pEditSink)
  572. {
  573. m_pEditSink = new CEditEventSink(this);
  574. if (m_pEditSink)
  575. {
  576. m_pEditSink->Attach(pEle);
  577. }
  578. }
  579. if (!m_pSink || FAILED(m_pAutoSuggest->AttachToInput(pTextEle)))
  580. {
  581. delete m_pAutoSuggest;
  582. m_pAutoSuggest = NULL;
  583. }
  584. }
  585. }
  586. pTextEle->Release();
  587. }
  588. else
  589. {
  590. ASSERT(!pTextEle);
  591. if (fPassword)
  592. {
  593. m_fHitPWField = TRUE;
  594. }
  595. }
  596. //
  597. // Don't release pEle
  598. }
  599. }
  600. return S_OK;
  601. }
  602. // Helper functions
  603. BOOL CIntelliForms::AskedUserToEnable()
  604. {
  605. DWORD dwType, dwSize;
  606. DWORD dwVal;
  607. DWORD dwRet;
  608. dwSize = sizeof(dwVal);
  609. dwRet = SHGetValue(HKEY_CURRENT_USER, c_szRegKeyIntelliForms, c_szRegValAskUser,
  610. &dwType, &dwVal, &dwSize);
  611. if ((dwRet == ERROR_SUCCESS) && (dwType == REG_DWORD))
  612. {
  613. return (dwVal == 0) ? TRUE : FALSE;
  614. }
  615. return FALSE;
  616. }
  617. BOOL CIntelliForms::IsEnabledInRegistry(LPCTSTR pszKey, LPCTSTR pszValue, BOOL fDefault)
  618. {
  619. DWORD dwType, dwSize;
  620. TCHAR szEnabled[16];
  621. DWORD dwRet;
  622. dwSize = sizeof(szEnabled);
  623. dwRet = SHGetValue(HKEY_CURRENT_USER, pszKey, pszValue, &dwType, szEnabled, &dwSize);
  624. if (dwRet == ERROR_INSUFFICIENT_BUFFER)
  625. {
  626. // Invalid value in registry.
  627. ASSERT(dwRet == ERROR_SUCCESS);
  628. return FALSE;
  629. }
  630. if (dwRet == ERROR_SUCCESS)
  631. {
  632. if ((dwType == REG_SZ) &&
  633. (!StrCmp(szEnabled, TEXT("yes"))))
  634. {
  635. // Enabled
  636. return TRUE;
  637. }
  638. else
  639. {
  640. // Disabled
  641. return FALSE;
  642. }
  643. }
  644. // Value not found
  645. return fDefault;
  646. }
  647. BOOL CIntelliForms::IsAdminRestricted(LPCTSTR pszRegVal)
  648. {
  649. DWORD lSize;
  650. DWORD lValue;
  651. lValue = 0; // clear it
  652. lSize = sizeof(lValue);
  653. if (ERROR_SUCCESS !=
  654. SHGetValue(HKEY_CURRENT_USER, c_szRegKeyRestrict, pszRegVal, NULL, (LPBYTE)&lValue, &lSize ))
  655. {
  656. return FALSE;
  657. }
  658. ASSERT(lSize == sizeof(lValue));
  659. return (0 != lValue) ? TRUE : FALSE;
  660. }
  661. BOOL CIntelliForms::IsEnabledForPage()
  662. {
  663. if (!m_fCheckedIfEnabled)
  664. {
  665. m_fCheckedIfEnabled = TRUE;
  666. // We will have our Url in m_bstrFullUrl, only if it is https: protocol
  667. if (m_bstrFullUrl)
  668. {
  669. ASSERT(!StrCmpNIW(m_bstrFullUrl, L"https:", 5));
  670. m_fEnabledForPage = TRUE;
  671. // See if this page is in the internet cache. If not, we won't intelliform
  672. // for this page either.
  673. if (!GetUrlCacheEntryInfoW(m_bstrFullUrl, NULL, NULL) && (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
  674. {
  675. // Failed - it's not in the cache
  676. m_fEnabledForPage = FALSE;
  677. }
  678. }
  679. else
  680. {
  681. // Url is not https: so always enable Intelliforms
  682. m_fEnabledForPage = TRUE;
  683. }
  684. }
  685. return m_fEnabledForPage;
  686. }
  687. HRESULT CIntelliForms::GetBodyEle(IHTMLElement2 **ppEle2)
  688. {
  689. if (!m_pDoc2 || !ppEle2)
  690. {
  691. return E_INVALIDARG;
  692. }
  693. *ppEle2=NULL;
  694. IHTMLElement *pBodyEle=NULL;
  695. m_pDoc2->get_body(&pBodyEle);
  696. if (pBodyEle)
  697. {
  698. pBodyEle->QueryInterface(IID_IHTMLElement2, (void **)ppEle2);
  699. pBodyEle->Release();
  700. }
  701. return (*ppEle2) ? S_OK : E_FAIL;
  702. }
  703. // static
  704. BOOL CIntelliForms::IsElementEnabled(IHTMLElement *pEle)
  705. {
  706. BOOL fEnabled=TRUE;
  707. BSTR bstrAttribute;
  708. VARIANT varVal;
  709. varVal.vt = VT_EMPTY;
  710. // First check "AutoComplete=OFF"
  711. bstrAttribute=SysAllocString(L"AutoComplete");
  712. if (bstrAttribute &&
  713. SUCCEEDED(pEle->getAttribute(bstrAttribute, 0, &varVal)))
  714. {
  715. if (varVal.vt == VT_BSTR)
  716. {
  717. if (!StrCmpIW(varVal.bstrVal, L"off"))
  718. {
  719. // We are disabled.
  720. fEnabled=FALSE;
  721. }
  722. }
  723. VariantClear(&varVal);
  724. }
  725. SysFreeString(bstrAttribute);
  726. // Then check "READONLY" attribute
  727. if (fEnabled)
  728. {
  729. IHTMLInputElement *pInputEle=NULL;
  730. pEle->QueryInterface(IID_IHTMLInputElement, (void **)&pInputEle);
  731. if (pInputEle)
  732. {
  733. VARIANT_BOOL vbReadOnly=VARIANT_FALSE;
  734. pInputEle->get_readOnly(&vbReadOnly);
  735. if (vbReadOnly)
  736. {
  737. // We are read only.
  738. fEnabled=FALSE;
  739. }
  740. pInputEle->Release();
  741. }
  742. }
  743. return fEnabled;
  744. }
  745. // static
  746. HRESULT CIntelliForms::ShouldAttachToElement(IUnknown *punkEle,
  747. BOOL fCheckForm,
  748. IHTMLElement2 **ppEle2,
  749. IHTMLInputTextElement **ppITE,
  750. IHTMLFormElement **ppFormEle,
  751. BOOL *pfPassword)
  752. {
  753. IHTMLInputTextElement *pITE = NULL;
  754. if (ppEle2)
  755. {
  756. *ppEle2 = NULL;
  757. }
  758. if (ppITE)
  759. {
  760. *ppITE = NULL;
  761. }
  762. if (ppFormEle)
  763. {
  764. *ppFormEle = NULL;
  765. }
  766. punkEle->QueryInterface(IID_IHTMLInputTextElement, (void **)&pITE);
  767. if (NULL == pITE)
  768. {
  769. // Not an input text element. Do not attach.
  770. return E_FAIL;
  771. }
  772. HRESULT hr = E_FAIL;
  773. IHTMLElement2 *pEle2 = NULL;
  774. IHTMLElement *pEle = NULL;
  775. IHTMLFormElement *pFormEle = NULL;
  776. punkEle->QueryInterface(IID_IHTMLElement2, (void **)&pEle2);
  777. punkEle->QueryInterface(IID_IHTMLElement, (void **)&pEle);
  778. if (pEle2 && pEle)
  779. {
  780. // type=text is all that's allowed
  781. BSTR bstrType=NULL;
  782. if (SUCCEEDED(pITE->get_type(&bstrType)) && bstrType)
  783. {
  784. if (!StrCmpICW(bstrType, L"text"))
  785. {
  786. // FormSuggest=off attribute turns us off for this element
  787. if (IsElementEnabled(pEle))
  788. {
  789. IHTMLElement *pFormHTMLEle=NULL;
  790. if (fCheckForm || ppFormEle)
  791. {
  792. pITE->get_form(&pFormEle);
  793. if (pFormEle)
  794. {
  795. pFormEle->QueryInterface(IID_IHTMLElement, (void **)&pFormHTMLEle);
  796. }
  797. else
  798. {
  799. // This may be valid if element is not in form
  800. TraceMsg(TF_IFORMS, "Iforms: pITE->get_form() returned NULL");
  801. }
  802. }
  803. // FormSuggest=off for form turns us off for this form
  804. if (pFormEle &&
  805. (!fCheckForm || (pFormHTMLEle && IsElementEnabled(pFormHTMLEle))))
  806. {
  807. hr = S_OK;
  808. if (ppEle2)
  809. {
  810. *ppEle2 = pEle2;
  811. pEle2->AddRef();
  812. }
  813. if (ppFormEle)
  814. {
  815. *ppFormEle = pFormEle;
  816. pFormEle->AddRef();
  817. }
  818. if (ppITE)
  819. {
  820. *ppITE = pITE;
  821. pITE->AddRef();
  822. }
  823. }
  824. SAFERELEASE(pFormHTMLEle);
  825. SAFERELEASE(pFormEle);
  826. }
  827. }
  828. else
  829. {
  830. if (pfPassword && !StrCmpICW(bstrType, L"password") && IsElementEnabled(pEle))
  831. {
  832. TraceMsg(TF_IFORMS, "IForms: Password field detected.");
  833. *pfPassword = TRUE;
  834. }
  835. }
  836. SysFreeString(bstrType);
  837. }
  838. else
  839. {
  840. TraceMsg(TF_IFORMS, "IntelliForms disabled for single element via attribute");
  841. }
  842. }
  843. SAFERELEASE(pITE);
  844. SAFERELEASE(pEle2);
  845. SAFERELEASE(pEle);
  846. return hr;
  847. }
  848. // Get the URL that we're located at, with query string/anchor stripped.
  849. LPCWSTR CIntelliForms::GetUrl()
  850. {
  851. if (m_bstrUrl)
  852. {
  853. return m_bstrUrl;
  854. }
  855. if (m_pOmWindow)
  856. {
  857. m_pOmWindow->IEFrameAuto()->get_LocationURL(&m_bstrUrl);
  858. }
  859. #ifdef ALLOW_SHELLUIOC_HOST
  860. else
  861. {
  862. IHTMLLocation *pHTMLLocation=NULL;
  863. m_pDoc2->get_location(&pHTMLLocation);
  864. if (NULL != pHTMLLocation)
  865. {
  866. pHTMLLocation->get_href(&m_bstrUrl);
  867. pHTMLLocation->Release();
  868. }
  869. }
  870. #endif
  871. if (m_bstrUrl)
  872. {
  873. PARSEDURLW puW = {0};
  874. puW.cbSize = sizeof(puW);
  875. // Save the full url for a security check, if we are https protocol
  876. if (SUCCEEDED(ParseURLW(m_bstrUrl, &puW)))
  877. {
  878. if (puW.nScheme == URL_SCHEME_HTTPS)
  879. {
  880. m_bstrFullUrl = SysAllocString(m_bstrUrl);
  881. if (!m_bstrFullUrl)
  882. {
  883. SysFreeString(m_bstrUrl);
  884. m_bstrUrl=NULL;
  885. }
  886. }
  887. }
  888. }
  889. if (m_bstrUrl)
  890. {
  891. // Strip off any query string or anchor
  892. LPWSTR lpUrl = m_bstrUrl;
  893. while (*lpUrl)
  894. {
  895. if ((*lpUrl == L'?') || (*lpUrl == L'#'))
  896. {
  897. *lpUrl = L'\0';
  898. break;
  899. }
  900. lpUrl ++;
  901. }
  902. return m_bstrUrl;
  903. }
  904. TraceMsg(TF_WARNING|TF_IFORMS, "CIntelliForms::GetUrl() failing!");
  905. return L""; // We can assume non-NULL pointer
  906. }
  907. // hook our "Submit" event sink to this form
  908. HRESULT CIntelliForms::AttachToForm(IHTMLFormElement *pFormEle)
  909. {
  910. ASSERT(m_pSink);
  911. if (m_pSink)
  912. {
  913. IHTMLElement2 *pEle2 = NULL;
  914. pFormEle->QueryInterface(IID_IHTMLElement2, (void **)&pEle2);
  915. if (pEle2)
  916. {
  917. // Sink event for the form
  918. EVENTS events[] = { EVENT_SUBMIT };
  919. m_pSink->SinkEvents(pEle2, ARRAYSIZE(events), events);
  920. }
  921. SAFERELEASE(pEle2);
  922. return S_OK;
  923. }
  924. return E_OUTOFMEMORY;
  925. }
  926. // Returns TRUE if nothing but spaces in string
  927. inline BOOL IsEmptyString(LPCWSTR lpwstr)
  928. {
  929. while (*lpwstr && (*lpwstr == L' ')) lpwstr++;
  930. return (*lpwstr == 0);
  931. }
  932. // called for each element in the form we are submitting
  933. HRESULT CIntelliForms::SubmitElement(IHTMLInputTextElement *pITE, FILETIME ftSubmit, BOOL fEnabledInCPL)
  934. {
  935. if (m_fRestricted) return E_FAIL;
  936. HRESULT hrRet = S_OK;
  937. BSTR bstrName;
  938. CIntelliForms::GetName(pITE, &bstrName);
  939. if (bstrName && bstrName[0])
  940. {
  941. BSTR bstrValue=NULL;
  942. pITE->get_value(&bstrValue);
  943. if (bstrValue && bstrValue[0] && !IsEmptyString(bstrValue))
  944. {
  945. if (fEnabledInCPL)
  946. {
  947. TraceMsg(TF_IFORMS, "IForms: Saving field \"%ws\" as \"%ws\"", bstrName, bstrValue);
  948. CStringList *psl;
  949. if (FAILED(ReadFromStore(bstrName, &psl)))
  950. {
  951. CStringList_New(&psl);
  952. }
  953. if (psl)
  954. {
  955. HRESULT hr;
  956. if (SUCCEEDED(hr = psl->AddString(bstrValue, ftSubmit)))
  957. {
  958. if ((S_OK == hr) ||
  959. (psl->NumStrings() > CStringList::MAX_STRINGS / 4))
  960. {
  961. // We added a non-duplicate string, or we updated the
  962. // last submit time of an existing string
  963. WriteToStore(bstrName, psl);
  964. }
  965. }
  966. delete psl;
  967. }
  968. }
  969. else
  970. {
  971. hrRet = S_FALSE; // Tell caller that we didn't save because we were disabled
  972. }
  973. }
  974. SysFreeString(bstrValue);
  975. }
  976. SysFreeString(bstrName);
  977. return hrRet;
  978. }
  979. HRESULT CIntelliForms::HandleFormSubmit(IHTMLFormElement *pForm)
  980. {
  981. IUnknown *punkForm=NULL;
  982. if (!pForm)
  983. {
  984. // We currently require a form element even from script
  985. return E_INVALIDARG;
  986. }
  987. if (!m_hdpaElements || !m_hdpaForms)
  988. {
  989. return S_OK;
  990. }
  991. // Make sure we're enabled
  992. BOOL fEnabledInCPL = IsEnabledInCPL();
  993. if (fEnabledInCPL || IsEnabledRestorePW() || !AskedUserToEnable())
  994. {
  995. pForm->QueryInterface(IID_IUnknown, (void **)&punkForm);
  996. if (punkForm)
  997. {
  998. IHTMLFormElement *pThisFormEle;
  999. IUnknown *punkThisForm;
  1000. FILETIME ftSubmit;
  1001. int iCount=0;
  1002. BOOL fShouldAskUser=FALSE;
  1003. IHTMLInputTextElement *pFirstEle=NULL;
  1004. GetSystemTimeAsFileTime(&ftSubmit);
  1005. // Go through list of 'changed' elements and save their values
  1006. // make sure we loop backwards since we nuke elements as we find them
  1007. for (int i=DPA_GetPtrCount(m_hdpaElements)-1; i>=0; i--)
  1008. {
  1009. IHTMLInputTextElement *pITE = ((IHTMLInputTextElement *)(DPA_FastGetPtr(m_hdpaElements, i)));
  1010. if (SUCCEEDED(pITE->get_form(&pThisFormEle)) && pThisFormEle)
  1011. {
  1012. if (SUCCEEDED(pThisFormEle->QueryInterface(IID_IUnknown, (void **)&punkThisForm)))
  1013. {
  1014. if (punkThisForm == punkForm)
  1015. {
  1016. // Verify that we're still allowed to save this element
  1017. if (SUCCEEDED(ShouldAttachToElement(pITE, TRUE, NULL, NULL, NULL, NULL)))
  1018. {
  1019. iCount ++;
  1020. if (!pFirstEle)
  1021. {
  1022. pFirstEle = pITE;
  1023. pFirstEle->AddRef();
  1024. }
  1025. // Don't save non-password stuff for non-cached pages
  1026. if (IsEnabledForPage())
  1027. {
  1028. // Won't actually save the value if fEnabledInCPL is FALSE
  1029. if (S_FALSE == SubmitElement(pITE, ftSubmit, fEnabledInCPL))
  1030. {
  1031. // We would have saved this if we were enabled
  1032. fShouldAskUser = TRUE;
  1033. }
  1034. }
  1035. // Remove this element from the DPA to prevent any possibility of
  1036. // saving before more user input takes place
  1037. pITE->Release();
  1038. DPA_DeletePtr(m_hdpaElements, i);
  1039. }
  1040. }
  1041. else
  1042. {
  1043. TraceMsg(TF_IFORMS, "IForms: User input in different form than was submitted...?");
  1044. }
  1045. punkThisForm->Release();
  1046. }
  1047. pThisFormEle->Release();
  1048. }
  1049. else
  1050. {
  1051. // It shouldn't be in our DPA if it isn't in a form...
  1052. TraceMsg(TF_WARNING|TF_IFORMS, "Iforms: pITE->get_form() returned NULL!");
  1053. }
  1054. }
  1055. if (0 == DPA_GetPtrCount(m_hdpaElements))
  1056. {
  1057. DPA_Destroy(m_hdpaElements);
  1058. m_hdpaElements=NULL;
  1059. }
  1060. if (m_fHitPWField || (m_iRestoredIndex != -1))
  1061. {
  1062. // ?? why not check iCount==1 here?
  1063. if (pFirstEle)
  1064. {
  1065. // May have restored PW and may have changed or entered it
  1066. SavePassword(pForm, ftSubmit, pFirstEle);
  1067. // WARNING - after returning from "SavePassword" our object may be invalid
  1068. // if we got released/detached during modal dialog
  1069. }
  1070. }
  1071. else if (fShouldAskUser)
  1072. {
  1073. // Possibly ask user if they want to enable intelliforms, only if
  1074. // this isn't a login
  1075. if (m_pOmWindow)
  1076. {
  1077. m_pOmWindow->IntelliFormsAskUser(NULL);
  1078. }
  1079. fShouldAskUser = FALSE;
  1080. }
  1081. if (fShouldAskUser)
  1082. {
  1083. // If we should ask the user but we're not going to (login form),
  1084. // increment our count anyway so that we ask them as soon as we can
  1085. IncrementAskCount();
  1086. }
  1087. punkForm->Release();
  1088. SAFERELEASE(pFirstEle);
  1089. }
  1090. }
  1091. return S_OK;
  1092. }
  1093. HRESULT CIntelliForms::HandleEvent(IHTMLElement *pEle, EVENTS Event, IHTMLEventObj *pEventObj)
  1094. {
  1095. TraceMsg(TF_IFORMS, "CIntelliForms::HandleEvent Event=%ws", EventsToSink[Event].pwszEventName);
  1096. if (Event == EVENT_SUBMIT)
  1097. {
  1098. // Save strings for modified text inputs when appropriate
  1099. IHTMLFormElement *pFormEle = NULL;
  1100. if (pEle)
  1101. {
  1102. pEle->QueryInterface(IID_IHTMLFormElement, (void **)&pFormEle);
  1103. if (pFormEle)
  1104. {
  1105. HandleFormSubmit(pFormEle);
  1106. // Warning - "this" may be detached/destroyed at this point
  1107. pFormEle->Release();
  1108. }
  1109. }
  1110. }
  1111. else
  1112. {
  1113. ASSERT(Event == EVENT_SCROLL);
  1114. if (m_pAutoSuggest)
  1115. m_pAutoSuggest->UpdateDropdownPosition();
  1116. }
  1117. return S_OK;
  1118. }
  1119. HRESULT CIntelliForms::PreHandleEvent(DISPID inEvtDispId, IHTMLEventObj* pIEventObj)
  1120. {
  1121. if ((inEvtDispId == 0) &&
  1122. (m_pAutoSuggest != NULL) &&
  1123. (m_pAutoSuggest->AttachedElement() != NULL))
  1124. {
  1125. BSTR bstrType = NULL;
  1126. CEventSinkCallback::EVENTS Event = EVENT_BOGUS;
  1127. pIEventObj->get_type(&bstrType);
  1128. if (bstrType)
  1129. {
  1130. #if 0
  1131. // Spew wParam and lParam
  1132. IHTMLEventObj3 *pObj3 = NULL;
  1133. pIEventObj->QueryInterface(IID_PPV_ARG(IHTMLEventObj3, &pObj3));
  1134. if (pObj3)
  1135. {
  1136. long lLong=0;
  1137. long wWord=0;
  1138. pObj3->get_imeCompositionChange(&lLong);
  1139. pObj3->get_imeNotifyCommand(&wWord);
  1140. TraceMsg(TF_ALWAYS, "PreHandleEvent: %ws - wWord=0x%04x lLong=0x%08x", bstrType, wWord, lLong);
  1141. pObj3->Release();
  1142. }
  1143. #endif
  1144. if (!StrCmp(bstrType, L"composition"))
  1145. {
  1146. Event = EVENT_COMPOSITION;
  1147. }
  1148. else if (!StrCmp(bstrType, L"notify"))
  1149. {
  1150. Event = EVENT_NOTIFY;
  1151. }
  1152. if (Event != EVENT_BOGUS)
  1153. {
  1154. // Trident doesn't set srcElement on eventobj, so just use the one
  1155. // we're attached to
  1156. IHTMLElement *pEle;
  1157. m_pAutoSuggest->AttachedElement()->QueryInterface(IID_IHTMLElement, (void **)&pEle);
  1158. if (pEle)
  1159. {
  1160. m_pAutoSuggest->HandleEvent(pEle, Event, pIEventObj);
  1161. pEle->Release();
  1162. }
  1163. }
  1164. SysFreeString(bstrType);
  1165. }
  1166. }
  1167. return S_FALSE; // S_FALSE so that Trident will still process this
  1168. }
  1169. // Our passwords are stored in username/value pairs
  1170. // Search every other string for the username
  1171. HRESULT CIntelliForms::FindPasswordEntry(LPCWSTR pwszValue, int *piIndex)
  1172. {
  1173. ASSERT(m_pslPasswords);
  1174. ASSERT(!(m_pslPasswords->NumStrings() & 1)); // Should be even number
  1175. int i;
  1176. for (i=0; i<m_pslPasswords->NumStrings(); i += 2)
  1177. {
  1178. if (!StrCmpIW(pwszValue, m_pslPasswords->GetString(i)))
  1179. {
  1180. // Found it
  1181. *piIndex = i+1;
  1182. return S_OK;
  1183. }
  1184. }
  1185. return E_FAIL;
  1186. }
  1187. // Convert url to string based on shlwapi UrlHash return
  1188. LPCWSTR CIntelliForms::GetUrlHash()
  1189. {
  1190. BYTE bBuf[15];
  1191. if (m_pwszUrlHash)
  1192. {
  1193. return m_pwszUrlHash;
  1194. }
  1195. LPCWSTR pwszUrl = GetUrl();
  1196. if (!pwszUrl || !*pwszUrl)
  1197. {
  1198. return NULL;
  1199. }
  1200. if (SUCCEEDED(UrlHashW(pwszUrl, bBuf, ARRAYSIZE(bBuf))))
  1201. {
  1202. // Translate this array of bytes into 7-bit chars
  1203. m_pwszUrlHash = (LPWSTR)LocalAlloc(LMEM_FIXED, sizeof(WCHAR)*(ARRAYSIZE(bBuf)+1));
  1204. if (m_pwszUrlHash)
  1205. {
  1206. for (int i=0; i<ARRAYSIZE(bBuf); i++)
  1207. {
  1208. // Translate each char into 32-96 range
  1209. ((LPWSTR)m_pwszUrlHash)[i] = (WCHAR)((bBuf[i] & 0x3F) + 0x20);
  1210. }
  1211. ((LPWSTR)m_pwszUrlHash)[i] = L'\0';
  1212. }
  1213. return m_pwszUrlHash;
  1214. }
  1215. return NULL;
  1216. }
  1217. // Tells us if passwords are present for this url
  1218. BOOL CIntelliForms::ArePasswordsSaved()
  1219. {
  1220. if (!m_fRestrictedPW)
  1221. {
  1222. DWORD dwVal, dwSize=sizeof(dwVal);
  1223. LPCWSTR pwsz = GetUrlHash();
  1224. if (pwsz && (ERROR_SUCCESS == SHGetValueW(HKEY_CURRENT_USER, c_wszRegKeyIntelliFormsSPW, pwsz, NULL, &dwVal, &dwSize)))
  1225. {
  1226. return TRUE;
  1227. }
  1228. }
  1229. return FALSE;
  1230. }
  1231. // Will return password list in m_pslPasswords, if passwords are saved
  1232. BOOL CIntelliForms::LoadPasswords()
  1233. {
  1234. if (!m_fCheckedPW)
  1235. {
  1236. m_fCheckedPW = TRUE;
  1237. // Check if passwords are present without hitting pstore
  1238. if (ArePasswordsSaved())
  1239. {
  1240. // We should have passwords for this url. Hit PStore.
  1241. ReadFromStore(GetUrl(), &m_pslPasswords, TRUE);
  1242. m_iRestoredIndex = -1;
  1243. }
  1244. }
  1245. else if (m_pslPasswords)
  1246. {
  1247. // If we already have passwords, double check the registry in case the user
  1248. // nuked saved stuff via inetcpl
  1249. if (!ArePasswordsSaved())
  1250. {
  1251. delete m_pslPasswords;
  1252. m_pslPasswords=NULL;
  1253. m_iRestoredIndex = -1;
  1254. }
  1255. }
  1256. return (m_pslPasswords != NULL);
  1257. }
  1258. void CIntelliForms::SavePasswords()
  1259. {
  1260. if (m_pslPasswords && m_bstrUrl)
  1261. {
  1262. WriteToStore(m_bstrUrl, m_pslPasswords);
  1263. SetPasswordsAreSaved(TRUE);
  1264. }
  1265. }
  1266. // Mark that we have passwords saved for this url
  1267. void CIntelliForms::SetPasswordsAreSaved(BOOL fSaved)
  1268. {
  1269. LPCWSTR pwsz = GetUrlHash();
  1270. if (pwsz)
  1271. {
  1272. if (fSaved)
  1273. {
  1274. DWORD dwSize = sizeof(DWORD);
  1275. DWORD dw = 0;
  1276. SHSetValueW(HKEY_CURRENT_USER, c_wszRegKeyIntelliFormsSPW, pwsz, REG_DWORD, &dw, sizeof(dw));
  1277. }
  1278. else
  1279. {
  1280. SHDeleteValueW(HKEY_CURRENT_USER, c_wszRegKeyIntelliFormsSPW, pwsz);
  1281. }
  1282. }
  1283. }
  1284. // enumerates form & gets password fields
  1285. class CDetectLoginForm
  1286. {
  1287. public:
  1288. CDetectLoginForm() { m_pNameEle=m_pPasswordEle=m_pPasswordEle2=NULL; }
  1289. ~CDetectLoginForm() { SAFERELEASE(m_pNameEle); SAFERELEASE(m_pPasswordEle); }
  1290. HRESULT ParseForm(IHTMLFormElement *pFormEle, BOOL fRestoring);
  1291. IHTMLInputTextElement *GetNameEle() { return m_pNameEle; }
  1292. IHTMLInputTextElement *GetPasswordEle() { return m_pPasswordEle; }
  1293. protected:
  1294. IHTMLInputTextElement *m_pNameEle;
  1295. IHTMLInputTextElement *m_pPasswordEle;
  1296. IHTMLInputTextElement *m_pPasswordEle2;
  1297. static HRESULT s_PasswordCB(IDispatch *pDispEle, DWORD_PTR dwCBData);
  1298. };
  1299. // if SUCCEEDED(hr), GetNameEle and GetPasswordEle are guaranteed non-NULL
  1300. HRESULT CDetectLoginForm::ParseForm(IHTMLFormElement *pFormEle, BOOL fRestoring)
  1301. {
  1302. if (m_pPasswordEle || m_pNameEle || m_pPasswordEle2)
  1303. {
  1304. return E_FAIL;
  1305. }
  1306. CIntelliForms::CEnumCollection<IHTMLFormElement>::EnumCollection(pFormEle, s_PasswordCB, (DWORD_PTR)this);
  1307. // For forms with two password fields (possibly used for login *and* new accounts)
  1308. // we clear the second field on PW restore and require it to be blank for saving.
  1309. // Ideally, we would detect this as a password change situation as well.
  1310. if (m_pPasswordEle2)
  1311. {
  1312. if (fRestoring)
  1313. {
  1314. BSTR bstrEmpty=SysAllocString(L"");
  1315. if (bstrEmpty)
  1316. {
  1317. m_pPasswordEle2->put_value(bstrEmpty);
  1318. SysFreeString(bstrEmpty);
  1319. }
  1320. }
  1321. else
  1322. {
  1323. BSTR bstrVal=NULL;
  1324. m_pPasswordEle2->get_value(&bstrVal);
  1325. if (bstrVal && bstrVal[0])
  1326. {
  1327. // Failure! Second password field isn't empty.
  1328. SAFERELEASE(m_pNameEle);
  1329. SAFERELEASE(m_pPasswordEle);
  1330. }
  1331. SysFreeString(bstrVal);
  1332. }
  1333. SAFERELEASE(m_pPasswordEle2); // Always release this
  1334. }
  1335. if (m_pPasswordEle && m_pNameEle)
  1336. {
  1337. return S_OK;
  1338. }
  1339. SAFERELEASE(m_pNameEle);
  1340. SAFERELEASE(m_pPasswordEle);
  1341. ASSERT(!m_pPasswordEle2);
  1342. return E_FAIL;
  1343. }
  1344. // Password callback for CEnumCollection to find username and password fields
  1345. // in a login form
  1346. HRESULT CDetectLoginForm::s_PasswordCB(IDispatch *pDispEle, DWORD_PTR dwCBData)
  1347. {
  1348. CDetectLoginForm *pThis = (CDetectLoginForm *)dwCBData;
  1349. HRESULT hr=S_OK;
  1350. IHTMLInputTextElement *pTextEle=NULL;
  1351. pDispEle->QueryInterface(IID_IHTMLInputTextElement, (void **)&pTextEle);
  1352. if (pTextEle)
  1353. {
  1354. BSTR bstrType;
  1355. pTextEle->get_type(&bstrType);
  1356. if (bstrType)
  1357. {
  1358. if (!StrCmpICW(bstrType, L"text"))
  1359. {
  1360. // Assume this is the 'name' field
  1361. if (pThis->m_pNameEle)
  1362. {
  1363. // Whoops, we've already got a name field. Can't have two...
  1364. hr = E_ABORT;
  1365. }
  1366. else
  1367. {
  1368. pThis->m_pNameEle = pTextEle;
  1369. pTextEle->AddRef();
  1370. }
  1371. }
  1372. else if (!StrCmpICW(bstrType, L"password"))
  1373. {
  1374. // Assume this is the 'password' field
  1375. if (pThis->m_pPasswordEle)
  1376. {
  1377. // Whoops, we've already got a password field. Can't have two...
  1378. // ...oh wait, yes we can...
  1379. if (pThis->m_pPasswordEle2)
  1380. {
  1381. // ...but we definitely can't have three!!!
  1382. hr = E_ABORT;
  1383. }
  1384. else
  1385. {
  1386. pThis->m_pPasswordEle2 = pTextEle;
  1387. pTextEle->AddRef();
  1388. }
  1389. }
  1390. else
  1391. {
  1392. pThis->m_pPasswordEle = pTextEle;
  1393. pTextEle->AddRef();
  1394. }
  1395. }
  1396. SysFreeString(bstrType);
  1397. }
  1398. pTextEle->Release();
  1399. }
  1400. if (hr == E_ABORT)
  1401. {
  1402. SAFERELEASE(pThis->m_pNameEle);
  1403. SAFERELEASE(pThis->m_pPasswordEle);
  1404. SAFERELEASE(pThis->m_pPasswordEle2);
  1405. }
  1406. return hr;
  1407. }
  1408. // Fill in passwords for this username, if one is available
  1409. HRESULT CIntelliForms::AutoFillPassword(IHTMLInputTextElement *pTextEle, LPCWSTR pwszUsername)
  1410. {
  1411. BSTR bstrUrl = NULL;
  1412. if (!pTextEle || !pwszUsername)
  1413. return E_INVALIDARG;
  1414. if (!IsEnabledRestorePW() || !LoadPasswords())
  1415. {
  1416. // We have no passwords for this url
  1417. return S_FALSE;
  1418. }
  1419. int iIndex;
  1420. if (SUCCEEDED(FindPasswordEntry(pwszUsername, &iIndex)))
  1421. {
  1422. // Returns index of password in m_pslPasswords
  1423. ASSERT(iIndex>=0 && iIndex<m_pslPasswords->NumStrings() && (iIndex&1));
  1424. FILETIME ft;
  1425. // StringTime==0 indicates user said "no" to saving password
  1426. if (SUCCEEDED(m_pslPasswords->GetStringTime(iIndex, &ft)) && (FILETIME_TO_INT(ft) != 0))
  1427. {
  1428. TraceMsg(TF_IFORMS, "IntelliForms found saved password");
  1429. // We have a password saved for this specific username. Fill it in.
  1430. CDetectLoginForm LoginForm;
  1431. IHTMLFormElement *pFormEle=NULL;
  1432. HRESULT hr = E_FAIL;
  1433. pTextEle->get_form(&pFormEle);
  1434. if (pFormEle)
  1435. {
  1436. // See if this is a valid form: One plain text input, One password input. Find the fields.
  1437. hr = LoginForm.ParseForm(pFormEle, TRUE);
  1438. pFormEle->Release();
  1439. }
  1440. else
  1441. {
  1442. // Shouldn't get this far if we don't have a form for this element
  1443. TraceMsg(TF_WARNING|TF_IFORMS, "Iforms: pITE->get_form() returned NULL!");
  1444. }
  1445. if (SUCCEEDED(hr))
  1446. {
  1447. BSTR bstrPW=NULL;
  1448. m_pslPasswords->GetBSTR(iIndex, &bstrPW);
  1449. if (bstrPW)
  1450. {
  1451. LoginForm.GetPasswordEle()->put_value(bstrPW);
  1452. SysFreeString(bstrPW);
  1453. m_iRestoredIndex = iIndex;
  1454. // We restored this password. sink the SUBMIT for this form (if we haven't yet)
  1455. UserInput(pTextEle);
  1456. }
  1457. }
  1458. }
  1459. else
  1460. {
  1461. // User previously said 'no' to remembering passwords
  1462. m_iRestoredIndex = -1;
  1463. }
  1464. }
  1465. return S_OK;
  1466. }
  1467. HRESULT CIntelliForms::DeletePassword(LPCWSTR pwszUsername)
  1468. {
  1469. // If we have a password, ask them if they want to delete it.
  1470. if (LoadPasswords())
  1471. {
  1472. int iIndex;
  1473. if (SUCCEEDED(FindPasswordEntry(pwszUsername, &iIndex)))
  1474. {
  1475. // If they previously said "no", delete without asking - they don't actually
  1476. // have a password saved
  1477. // Otherwise, ask and delete only if they say "yes"
  1478. FILETIME ft;
  1479. if (FAILED(m_pslPasswords->GetStringTime(iIndex, &ft)) ||
  1480. (0 == FILETIME_TO_INT(ft)) ||
  1481. (IDYES == DialogBoxParam(MLGetHinst(), MAKEINTRESOURCE(IDD_AUTOSUGGEST_DELETEPASSWORD),
  1482. m_hwndBrowser, AutoSuggestDlgProc, IDD_AUTOSUGGEST_DELETEPASSWORD)))
  1483. {
  1484. // Delete username then password from string list
  1485. if (SUCCEEDED(m_pslPasswords->DeleteString(iIndex-1)) &&
  1486. SUCCEEDED(m_pslPasswords->DeleteString(iIndex-1)))
  1487. {
  1488. TraceMsg(TF_IFORMS, "Deleting password for user \"%ws\"", pwszUsername);
  1489. ASSERT(!(m_pslPasswords->NumStrings() & 1));
  1490. if (m_iRestoredIndex == iIndex)
  1491. {
  1492. m_iRestoredIndex = -1;
  1493. }
  1494. else if (m_iRestoredIndex > iIndex)
  1495. {
  1496. m_iRestoredIndex -= 2;
  1497. }
  1498. if (m_pslPasswords->NumStrings() == 0)
  1499. {
  1500. // No more strings for this url. Nuke it.
  1501. DeleteFromStore(GetUrl());
  1502. SetPasswordsAreSaved(FALSE);
  1503. delete m_pslPasswords;
  1504. m_pslPasswords = NULL;
  1505. ASSERT(m_iRestoredIndex == -1);
  1506. }
  1507. else
  1508. {
  1509. SavePasswords();
  1510. }
  1511. }
  1512. }
  1513. }
  1514. }
  1515. return S_OK;
  1516. }
  1517. HRESULT CIntelliForms::SavePassword(IHTMLFormElement *pFormEle, FILETIME ftSubmit, IHTMLInputTextElement *pFirstEle)
  1518. {
  1519. if (m_fRestrictedPW ||
  1520. !IsEnabledRestorePW())
  1521. {
  1522. return S_FALSE;
  1523. }
  1524. BOOL fAskUser = TRUE;
  1525. // First let's check for previously saved entries for this username
  1526. if (LoadPasswords())
  1527. {
  1528. int iIndex;
  1529. BSTR bstrUserName=NULL;
  1530. pFirstEle->get_value(&bstrUserName);
  1531. if (bstrUserName)
  1532. {
  1533. if (SUCCEEDED(FindPasswordEntry(bstrUserName, &iIndex)))
  1534. {
  1535. FILETIME ft;
  1536. if (SUCCEEDED(m_pslPasswords->GetStringTime(iIndex, &ft)))
  1537. {
  1538. if (FILETIME_TO_INT(ft) == 0)
  1539. {
  1540. // StringTime==0 means user previously said "no".
  1541. TraceMsg(TF_IFORMS, "IForms not asking about saving password");
  1542. fAskUser = FALSE;
  1543. }
  1544. else if (m_iRestoredIndex != iIndex)
  1545. {
  1546. // User previously said "yes" - but we didn't restore it for some reason
  1547. // Can happen with "back" button then submit
  1548. TraceMsg(TF_WARNING|TF_IFORMS, "IForms - user saved password and we didn't restore it");
  1549. // Write regkey in case that was the problem - we'll work next time
  1550. SetPasswordsAreSaved(TRUE);
  1551. m_iRestoredIndex = iIndex;
  1552. }
  1553. }
  1554. }
  1555. else
  1556. {
  1557. m_iRestoredIndex = -1;
  1558. }
  1559. SysFreeString(bstrUserName);
  1560. }
  1561. }
  1562. // Then lets ask the user if they'd like to save the password for this username
  1563. if (fAskUser)
  1564. {
  1565. CDetectLoginForm LoginForm;
  1566. // See if this is a valid form: One plain text input, One password input. Find the fields.
  1567. if (SUCCEEDED(LoginForm.ParseForm(pFormEle, FALSE)))
  1568. {
  1569. TraceMsg(TF_IFORMS, "IForms Successfully detected 'save password' form");
  1570. BSTR bstrUsername=NULL;
  1571. BSTR bstrPassword=NULL;
  1572. LoginForm.GetNameEle()->get_value(&bstrUsername);
  1573. LoginForm.GetPasswordEle()->get_value(&bstrPassword);
  1574. if (bstrUsername && bstrPassword)
  1575. {
  1576. if (m_iRestoredIndex != -1)
  1577. {
  1578. // We have a previously saved password. See if our current entry is the same.
  1579. if (!StrCmpW(bstrPassword, m_pslPasswords->GetString(m_iRestoredIndex)))
  1580. {
  1581. // They're the same... nothing to do...
  1582. TraceMsg(TF_IFORMS, "IForms - user entered PW same as saved PW - nothing to do");
  1583. // Check to see that the username case is the same, just to be sure
  1584. if (StrCmpW(bstrUsername, m_pslPasswords->GetString(m_iRestoredIndex-1)))
  1585. {
  1586. TraceMsg(TF_IFORMS, "IForms - except change the username's case");
  1587. if (SUCCEEDED(m_pslPasswords->ReplaceString(m_iRestoredIndex-1, bstrUsername)))
  1588. {
  1589. SavePasswords();
  1590. }
  1591. else
  1592. {
  1593. // Something went horribly wrong!
  1594. delete m_pslPasswords;
  1595. m_pslPasswords=NULL;
  1596. }
  1597. }
  1598. }
  1599. else
  1600. {
  1601. // Ask the user if we want to change the saved password
  1602. INT_PTR iMB;
  1603. EnterModalDialog();
  1604. iMB = DialogBoxParam(MLGetHinst(), MAKEINTRESOURCE(IDD_AUTOSUGGEST_CHANGEPASSWORD),
  1605. m_hwndBrowser, AutoSuggestDlgProc, IDD_AUTOSUGGEST_CHANGEPASSWORD);
  1606. if (IDYES == iMB)
  1607. {
  1608. // Delete the old one and add the new one. Update filetimes.
  1609. if (SUCCEEDED(m_pslPasswords->ReplaceString(m_iRestoredIndex, bstrPassword)))
  1610. {
  1611. m_pslPasswords->SetStringTime(m_iRestoredIndex, ftSubmit);
  1612. SavePasswords();
  1613. TraceMsg(TF_IFORMS, "IForms successfully saved changed password");
  1614. }
  1615. else
  1616. {
  1617. TraceMsg(TF_IFORMS|TF_WARNING, "IForms couldn't change password!");
  1618. delete m_pslPasswords;
  1619. m_pslPasswords = NULL;
  1620. }
  1621. }
  1622. LeaveModalDialog();
  1623. }
  1624. }
  1625. else
  1626. {
  1627. // We don't have a previously saved password for this user. See if they want to save it.
  1628. // If the password is empty, don't bother asking or saving
  1629. if (IsEnabledAskPW() && bstrPassword[0])
  1630. {
  1631. EnterModalDialog();
  1632. INT_PTR iMB = DialogBoxParam(MLGetHinst(), MAKEINTRESOURCE(IDD_AUTOSUGGEST_SAVEPASSWORD),
  1633. m_hwndBrowser, AutoSuggestDlgProc, IDD_AUTOSUGGEST_SAVEPASSWORD);
  1634. // If we can't load passwords, then create a new list
  1635. if (!LoadPasswords())
  1636. {
  1637. CStringList_New(&m_pslPasswords);
  1638. if (m_pslPasswords)
  1639. m_pslPasswords->SetListData(LIST_DATA_PASSWORD);
  1640. }
  1641. if (m_pslPasswords)
  1642. {
  1643. if ((IDCANCEL == iMB) || ((IDNO == iMB) && (!IsEnabledAskPW())))
  1644. {
  1645. // If they hit the close box or said "no" and checked "don't ask",
  1646. // don't even save the username; we may ask them again next time
  1647. }
  1648. else
  1649. {
  1650. if (IDYES != iMB)
  1651. {
  1652. // User said "no" but we save the username (no password) and
  1653. // set filetime to 0 which means they said "no"
  1654. bstrPassword[0] = L'\0';
  1655. ftSubmit.dwLowDateTime = ftSubmit.dwHighDateTime = 0;
  1656. }
  1657. else
  1658. {
  1659. TraceMsg(TF_IFORMS, "IForms saving password for user %ws", bstrUsername);
  1660. }
  1661. m_pslPasswords->SetAutoScavenge(FALSE);
  1662. // Save the username and password, or just the username if they said "no"
  1663. if (SUCCEEDED(m_pslPasswords->AppendString(bstrUsername, ftSubmit)) &&
  1664. SUCCEEDED(m_pslPasswords->AppendString(bstrPassword, ftSubmit)))
  1665. {
  1666. SavePasswords();
  1667. }
  1668. else
  1669. {
  1670. TraceMsg(TF_WARNING, "IForms couldn't save username/password");
  1671. delete m_pslPasswords;
  1672. m_pslPasswords=NULL;
  1673. }
  1674. }
  1675. }
  1676. LeaveModalDialog();
  1677. }
  1678. }
  1679. }
  1680. SysFreeString(bstrUsername);
  1681. SysFreeString(bstrPassword);
  1682. } // if (SUCCEEDED(ParseForm()))
  1683. }
  1684. return S_OK;
  1685. }
  1686. // Returns reference to password string list if present. Return value must be used
  1687. // immediately and not destroyed. Used only by CEnumString.
  1688. HRESULT CIntelliForms::GetPasswordStringList(CStringList **ppslPasswords)
  1689. {
  1690. if (LoadPasswords())
  1691. {
  1692. *ppslPasswords = m_pslPasswords;
  1693. return S_OK;
  1694. }
  1695. *ppslPasswords = NULL;
  1696. return E_FAIL;
  1697. }
  1698. HRESULT CIntelliForms::CreatePStore()
  1699. {
  1700. if (!m_pPStore)
  1701. {
  1702. if (!m_hinstPStore)
  1703. {
  1704. m_hinstPStore = LoadLibrary(TEXT("PSTOREC.DLL"));
  1705. }
  1706. if (m_hinstPStore)
  1707. {
  1708. HRESULT (* pfn)(IPStore **, PST_PROVIDERID *, void *, DWORD) = NULL;
  1709. *(FARPROC *)&pfn = GetProcAddress(m_hinstPStore, "PStoreCreateInstance");
  1710. if (pfn)
  1711. {
  1712. pfn(&m_pPStore, NULL, NULL, 0);
  1713. }
  1714. }
  1715. }
  1716. return m_pPStore ? S_OK : E_FAIL;
  1717. }
  1718. void CIntelliForms::ReleasePStore()
  1719. {
  1720. SAFERELEASE(m_pPStore);
  1721. if (m_hinstPStore)
  1722. {
  1723. FreeLibrary(m_hinstPStore);
  1724. m_hinstPStore = NULL;
  1725. }
  1726. m_fPStoreTypeInit=FALSE;
  1727. }
  1728. HRESULT CIntelliForms::CreatePStoreAndType()
  1729. {
  1730. HRESULT hr;
  1731. hr = CreatePStore();
  1732. if (SUCCEEDED(hr) && !m_fPStoreTypeInit)
  1733. {
  1734. PST_TYPEINFO typeInfo;
  1735. typeInfo.cbSize = sizeof(typeInfo);
  1736. typeInfo.szDisplayName = (LPTSTR)c_szIntelliForms;
  1737. hr = m_pPStore->CreateType(PST_KEY_CURRENT_USER, &c_PStoreType, &typeInfo, 0);
  1738. if (hr == PST_E_TYPE_EXISTS)
  1739. {
  1740. hr = S_OK;
  1741. }
  1742. if (SUCCEEDED(hr))
  1743. {
  1744. hr = m_pPStore->CreateSubtype(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, &typeInfo, NULL, 0);
  1745. if (hr == PST_E_TYPE_EXISTS)
  1746. {
  1747. hr = S_OK;
  1748. }
  1749. }
  1750. if (SUCCEEDED(hr))
  1751. {
  1752. m_fPStoreTypeInit = TRUE;
  1753. }
  1754. }
  1755. return hr;
  1756. }
  1757. const WCHAR c_szBlob1Value[] = L"StringIndex";
  1758. const WCHAR c_szBlob2Value[] = L"StringData";
  1759. HRESULT CIntelliForms::WriteToStore(LPCWSTR pwszName, CStringList *psl)
  1760. {
  1761. HRESULT hr = E_FAIL;
  1762. TraceMsg(TF_IFORMS, "+WriteToStore");
  1763. if (SUCCEEDED(CreatePStoreAndType()))
  1764. {
  1765. LPBYTE pBlob1, pBlob2;
  1766. DWORD cbBlob1, cbBlob2;
  1767. if (SUCCEEDED(psl->WriteToBlobs(&pBlob1, &cbBlob1, &pBlob2, &cbBlob2)))
  1768. {
  1769. PST_PROMPTINFO promptInfo;
  1770. promptInfo.cbSize = sizeof(promptInfo);
  1771. promptInfo.dwPromptFlags = 0;
  1772. promptInfo.hwndApp = NULL;
  1773. promptInfo.szPrompt = NULL;
  1774. LPWSTR pwszValue;
  1775. int iValLen = lstrlenW(c_szBlob1Value) + lstrlenW(pwszName) + 10;
  1776. pwszValue = (LPWSTR) LocalAlloc(LMEM_FIXED, iValLen * sizeof(WCHAR));
  1777. if (pwszValue)
  1778. {
  1779. // Write Index
  1780. wnsprintfW(pwszValue, iValLen, L"%s:%s", pwszName, c_szBlob1Value);
  1781. hr = m_pPStore->WriteItem(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, pwszValue,
  1782. cbBlob1,
  1783. pBlob1,
  1784. &promptInfo, PST_CF_NONE, 0);
  1785. if (FAILED(hr))
  1786. {
  1787. TraceMsg(TF_WARNING | TF_IFORMS, "Failure writing Blob1 (Index). hr=%x", hr);
  1788. }
  1789. else
  1790. {
  1791. // Wrote Index successfully. Write data.
  1792. wnsprintfW(pwszValue, iValLen, L"%s:%s", pwszName, c_szBlob2Value);
  1793. hr = m_pPStore->WriteItem(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, pwszValue,
  1794. cbBlob2,
  1795. pBlob2,
  1796. &promptInfo, PST_CF_NONE, 0);
  1797. if (FAILED(hr))
  1798. {
  1799. // IE6#16676: This call failed on 64-bit Windows. Added a warning trace here to facilitate
  1800. // future debugging.
  1801. TraceMsg(TF_WARNING | TF_IFORMS, "Failure writing Blob2 (Data). hr=%x", hr);
  1802. }
  1803. }
  1804. // If *either* WriteItem failed, we really need to delete both the Index and the Data.
  1805. //
  1806. if (FAILED(hr))
  1807. {
  1808. // Delete bogus Blobs
  1809. // Delete Index Blob
  1810. wnsprintfW(pwszValue, iValLen, L"%s:%s", pwszName, c_szBlob1Value);
  1811. if (FAILED(m_pPStore->DeleteItem(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, pwszValue, &promptInfo, 0)))
  1812. {
  1813. TraceMsg(TF_ERROR | TF_IFORMS, "Failure deleting Blob1 (Index). hr=%x", hr);
  1814. }
  1815. // Delete Data Blob
  1816. wnsprintfW(pwszValue, iValLen, L"%s:%s", pwszName, c_szBlob2Value);
  1817. if (FAILED(m_pPStore->DeleteItem(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, pwszValue, &promptInfo, 0)))
  1818. {
  1819. TraceMsg(TF_ERROR | TF_IFORMS, "Failure deleting Blob2 (Data). hr=%x", hr);
  1820. }
  1821. }
  1822. LocalFree(pwszValue);
  1823. pwszValue = NULL;
  1824. }
  1825. if (pBlob1)
  1826. {
  1827. LocalFree(pBlob1);
  1828. pBlob1 = NULL;
  1829. }
  1830. if (pBlob2)
  1831. {
  1832. LocalFree(pBlob2);
  1833. pBlob2 = NULL;
  1834. }
  1835. }
  1836. }
  1837. TraceMsg(TF_IFORMS, "-WriteToStore");
  1838. return hr;
  1839. }
  1840. HRESULT CIntelliForms::ReadFromStore(LPCWSTR pwszName, CStringList **ppsl, BOOL fPasswordList/*=FALSE*/)
  1841. {
  1842. HRESULT hr = E_FAIL;
  1843. TraceMsg(TF_IFORMS, "+ReadFromStore");
  1844. *ppsl=NULL;
  1845. if (SUCCEEDED(CreatePStore()))
  1846. {
  1847. PST_PROMPTINFO promptInfo;
  1848. promptInfo.cbSize = sizeof(promptInfo);
  1849. promptInfo.dwPromptFlags = 0;
  1850. promptInfo.hwndApp = NULL;
  1851. promptInfo.szPrompt = NULL;
  1852. LPWSTR pwszValue;
  1853. int iValLen = lstrlenW(c_szBlob1Value) + lstrlenW(pwszName) + 10;
  1854. pwszValue = (LPWSTR) LocalAlloc(LMEM_FIXED, iValLen * sizeof(WCHAR));
  1855. if (pwszValue)
  1856. {
  1857. DWORD dwBlob1Size, dwBlob2Size;
  1858. LPBYTE pBlob1=NULL, pBlob2=NULL;
  1859. wnsprintfW(pwszValue, iValLen, L"%s:%s", pwszName, c_szBlob1Value);
  1860. hr = m_pPStore->ReadItem(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, pwszValue,
  1861. &dwBlob1Size,
  1862. &pBlob1,
  1863. &promptInfo, 0);
  1864. if (SUCCEEDED(hr))
  1865. {
  1866. wnsprintfW(pwszValue, iValLen, L"%s:%s", pwszName, c_szBlob2Value);
  1867. hr = m_pPStore->ReadItem(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, pwszValue,
  1868. &dwBlob2Size,
  1869. &pBlob2,
  1870. &promptInfo, 0);
  1871. if (SUCCEEDED(hr))
  1872. {
  1873. // bogus... have to reallocate here... bogus... bogus...
  1874. LPBYTE pBlob1b, pBlob2b;
  1875. pBlob1b=(LPBYTE)LocalAlloc(LMEM_FIXED, dwBlob1Size);
  1876. pBlob2b=(LPBYTE)LocalAlloc(LMEM_FIXED, dwBlob2Size);
  1877. if (pBlob1b && pBlob2b)
  1878. {
  1879. memcpy(pBlob1b, pBlob1, dwBlob1Size);
  1880. memcpy(pBlob2b, pBlob2, dwBlob2Size);
  1881. CStringList_New(ppsl);
  1882. if (*ppsl)
  1883. {
  1884. hr = (*ppsl)->ReadFromBlobs(&pBlob1b, dwBlob1Size, &pBlob2b, dwBlob2Size);
  1885. if (SUCCEEDED(hr))
  1886. {
  1887. INT64 i;
  1888. if (FAILED((*ppsl)->GetListData(&i)) ||
  1889. ((fPasswordList && !(i & LIST_DATA_PASSWORD)) ||
  1890. (!fPasswordList && (i & LIST_DATA_PASSWORD))))
  1891. {
  1892. TraceMsg(TF_WARNING|TF_IFORMS, "IForms: Password/nonpassword lists mixed up");
  1893. hr = E_FAIL; // don't allow malicious site to access PW data
  1894. }
  1895. }
  1896. if (FAILED(hr))
  1897. {
  1898. delete *ppsl;
  1899. *ppsl=NULL;
  1900. }
  1901. }
  1902. }
  1903. else
  1904. {
  1905. if (pBlob1b)
  1906. {
  1907. LocalFree(pBlob1b);
  1908. pBlob1b = NULL;
  1909. }
  1910. if (pBlob2b)
  1911. {
  1912. LocalFree(pBlob2b);
  1913. pBlob2b = NULL;
  1914. }
  1915. }
  1916. }
  1917. else
  1918. {
  1919. TraceMsg(TF_IFORMS, "Failed reading Blob2. hr=%x", hr);
  1920. }
  1921. }
  1922. else
  1923. {
  1924. TraceMsg(TF_IFORMS, "Failed reading Blob1. hr=%x", hr);
  1925. }
  1926. LocalFree(pwszValue);
  1927. pwszValue = NULL;
  1928. if (pBlob1)
  1929. {
  1930. CoTaskMemFree(pBlob1);
  1931. pBlob1 = NULL;
  1932. }
  1933. if (pBlob2)
  1934. {
  1935. CoTaskMemFree(pBlob2);
  1936. pBlob2 = NULL;
  1937. }
  1938. }
  1939. }
  1940. TraceMsg(TF_IFORMS, "-ReadFromStore");
  1941. return hr;
  1942. }
  1943. HRESULT CIntelliForms::DeleteFromStore(LPCWSTR pwszName)
  1944. {
  1945. HRESULT hr=E_FAIL;
  1946. if (SUCCEEDED(CreatePStore()))
  1947. {
  1948. HRESULT hr1, hr2;
  1949. LPWSTR pwszValue;
  1950. int iValLen = lstrlenW(c_szBlob1Value) + lstrlenW(pwszName) + 10;
  1951. pwszValue = (LPWSTR) LocalAlloc(LMEM_FIXED, iValLen * sizeof(WCHAR));
  1952. if (pwszValue)
  1953. {
  1954. PST_PROMPTINFO promptInfo;
  1955. promptInfo.cbSize = sizeof(promptInfo);
  1956. promptInfo.dwPromptFlags = 0;
  1957. promptInfo.hwndApp = NULL;
  1958. promptInfo.szPrompt = NULL;
  1959. wnsprintfW(pwszValue, iValLen, L"%s:%s", pwszName, c_szBlob1Value);
  1960. hr1 = m_pPStore->DeleteItem(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, pwszValue, &promptInfo, 0);
  1961. wnsprintfW(pwszValue, iValLen, L"%s:%s", pwszName, c_szBlob2Value);
  1962. hr2 = m_pPStore->DeleteItem(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, pwszValue, &promptInfo, 0);
  1963. if (SUCCEEDED(hr1) && SUCCEEDED(hr2))
  1964. {
  1965. hr = S_OK;
  1966. }
  1967. LocalFree(pwszValue);
  1968. pwszValue = NULL;
  1969. }
  1970. }
  1971. return hr;
  1972. }
  1973. const int c_iEnumSize=256;
  1974. HRESULT CIntelliForms::ClearStore(DWORD dwClear)
  1975. {
  1976. BOOL fReleasePStore = (m_pPStore == NULL);
  1977. ASSERT(dwClear <= 2);
  1978. if (dwClear > 2)
  1979. {
  1980. return E_INVALIDARG;
  1981. }
  1982. if (SUCCEEDED(CreatePStoreAndType()))
  1983. {
  1984. IEnumPStoreItems *pEnumItems;
  1985. ULONG cFetched=0;
  1986. do
  1987. {
  1988. if (SUCCEEDED(m_pPStore->EnumItems(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, 0, &pEnumItems)))
  1989. {
  1990. LPWSTR pwszName[c_iEnumSize];
  1991. PST_PROMPTINFO promptInfo;
  1992. promptInfo.cbSize = sizeof(promptInfo);
  1993. promptInfo.dwPromptFlags = 0;
  1994. promptInfo.hwndApp = NULL;
  1995. promptInfo.szPrompt = NULL;
  1996. // Enumerator doesn't keep its state - deleting items while we enumerate makes us
  1997. // miss some. It does support celt>1... but returns failure codes when it succeeds.
  1998. cFetched = 0;
  1999. pEnumItems->Next(c_iEnumSize, pwszName, &cFetched);
  2000. if (cFetched)
  2001. {
  2002. for (ULONG i=0; i<cFetched; i++)
  2003. {
  2004. ASSERT(pwszName[i]);
  2005. if (pwszName[i])
  2006. {
  2007. BOOL fDelete = TRUE;
  2008. // Hack to work around PStore string-case bug: first take their
  2009. // enum value literally, then convert to lowercase and do it
  2010. // again; IE5.0 #71001
  2011. for (int iHack=0; iHack<2; iHack++)
  2012. {
  2013. if (iHack == 1)
  2014. {
  2015. // Convert the pwszName[i] to lowercase... only before
  2016. // the colon...
  2017. WCHAR *pwch = StrRChrW(pwszName[i], NULL, L':');
  2018. if (pwch)
  2019. {
  2020. *pwch = L'\0';
  2021. MyToLower(pwszName[i]);
  2022. *pwch = L':';
  2023. }
  2024. else
  2025. break;
  2026. }
  2027. if (dwClear != IECMDID_ARG_CLEAR_FORMS_ALL)
  2028. {
  2029. fDelete = FALSE;
  2030. // See if this is a password item or not
  2031. // This is pretty annoying. Since our string lists are split
  2032. // into two blobs, we need to find out which one this is and
  2033. // load the index for it.
  2034. WCHAR *pwch = StrRChrW(pwszName[i], NULL, L':');
  2035. if (pwch)
  2036. {
  2037. LPWSTR pwszIndexName=NULL;
  2038. if (!StrCmpCW(pwch+1, c_szBlob2Value))
  2039. {
  2040. int iSize = sizeof(WCHAR) * (lstrlenW(pwszName[i])+10);
  2041. pwszIndexName = (LPWSTR) LocalAlloc(LMEM_FIXED, iSize);
  2042. if (pwszIndexName)
  2043. {
  2044. *pwch = L'\0';
  2045. wnsprintfW(pwszIndexName, iSize, L"%s:%s", pwszName[i], c_szBlob1Value);
  2046. *pwch = L':';
  2047. }
  2048. }
  2049. DWORD dwBlob1Size;
  2050. LPBYTE pBlob1=NULL;
  2051. INT64 iFlags;
  2052. if (SUCCEEDED(m_pPStore->ReadItem(
  2053. PST_KEY_CURRENT_USER,
  2054. &c_PStoreType, &m_guidUserId,
  2055. (pwszIndexName) ? pwszIndexName : pwszName[i],
  2056. &dwBlob1Size,
  2057. &pBlob1,
  2058. &promptInfo, 0)) && pBlob1)
  2059. {
  2060. if (SUCCEEDED(CStringList::GetFlagsFromIndex(pBlob1, &iFlags)))
  2061. {
  2062. if (((iFlags & LIST_DATA_PASSWORD) && (dwClear == IECMDID_ARG_CLEAR_FORMS_PASSWORDS_ONLY)) ||
  2063. (!(iFlags & LIST_DATA_PASSWORD) && (dwClear == IECMDID_ARG_CLEAR_FORMS_ALL_BUT_PASSWORDS)))
  2064. {
  2065. // Delete this item
  2066. fDelete = TRUE;
  2067. }
  2068. }
  2069. CoTaskMemFree(pBlob1);
  2070. }
  2071. else
  2072. {
  2073. // The index is already deleted
  2074. fDelete = TRUE;
  2075. }
  2076. if (pwszIndexName)
  2077. {
  2078. LocalFree(pwszIndexName);
  2079. pwszIndexName = NULL;
  2080. }
  2081. }
  2082. } // if (dwClear != CLEAR_INTELLIFORMS_ALL)
  2083. if (fDelete)
  2084. {
  2085. m_pPStore->DeleteItem(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, pwszName[i], &promptInfo, 0);
  2086. }
  2087. } // for (iHack)
  2088. CoTaskMemFree(pwszName[i]);
  2089. } // if (pwszName[i])
  2090. }
  2091. }
  2092. pEnumItems->Release();
  2093. }
  2094. }
  2095. while (cFetched == c_iEnumSize); // In case we didn't cover everything in one pass
  2096. if (dwClear == IECMDID_ARG_CLEAR_FORMS_ALL)
  2097. {
  2098. m_pPStore->DeleteSubtype(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, 0);
  2099. m_pPStore->DeleteType(PST_KEY_CURRENT_USER, &c_PStoreType, 0);
  2100. }
  2101. if ((dwClear == IECMDID_ARG_CLEAR_FORMS_ALL) ||
  2102. (dwClear == IECMDID_ARG_CLEAR_FORMS_PASSWORDS_ONLY))
  2103. {
  2104. // Delete the urlhash key storing which urls we have passwords saved for
  2105. SHDeleteKey(HKEY_CURRENT_USER, c_szRegKeyIntelliForms);
  2106. }
  2107. TraceMsg(TF_IFORMS, "IForms: ClearStore cleared at least %d entries", cFetched);
  2108. }
  2109. if (fReleasePStore)
  2110. {
  2111. ReleasePStore();
  2112. }
  2113. return S_OK;
  2114. }
  2115. // static: Get the name from an input element - uses VCARD_NAME attribute if present.
  2116. HRESULT CIntelliForms::GetName(IHTMLInputTextElement *pTextEle, BSTR *pbstrName)
  2117. {
  2118. IHTMLElement *pEle=NULL;
  2119. *pbstrName = NULL;
  2120. pTextEle->QueryInterface(IID_IHTMLElement, (void **)&pEle);
  2121. if (pEle)
  2122. {
  2123. BSTR bstrAttr = SysAllocString(L"VCARD_NAME");
  2124. if (bstrAttr)
  2125. {
  2126. VARIANT var;
  2127. var.vt = VT_EMPTY;
  2128. pEle->getAttribute(bstrAttr, 0, &var);
  2129. if (var.vt == VT_BSTR && var.bstrVal)
  2130. {
  2131. *pbstrName = var.bstrVal;
  2132. }
  2133. else
  2134. {
  2135. VariantClear(&var);
  2136. }
  2137. SysFreeString(bstrAttr);
  2138. }
  2139. pEle->Release();
  2140. }
  2141. if (!*pbstrName)
  2142. {
  2143. pTextEle->get_name(pbstrName);
  2144. }
  2145. // Convert the name to lowercase
  2146. if (*pbstrName)
  2147. {
  2148. // Call "MyToLower" instead
  2149. if (g_fRunningOnNT)
  2150. {
  2151. CharLowerBuffW(*pbstrName, lstrlenW(*pbstrName));
  2152. }
  2153. else
  2154. {
  2155. // Ideally we would use the code page contained in the string instead of
  2156. // the system code page.
  2157. CHAR chBuf[MAX_PATH];
  2158. SHUnicodeToAnsi(*pbstrName, chBuf, ARRAYSIZE(chBuf));
  2159. CharLowerBuffA(chBuf, lstrlenA(chBuf));
  2160. SHAnsiToUnicode(chBuf, *pbstrName, SysStringLen(*pbstrName)+1);
  2161. }
  2162. }
  2163. return (*pbstrName) ? S_OK : E_FAIL;
  2164. }
  2165. // Called when script calls window.external.AutoCompleteSaveForm
  2166. HRESULT CIntelliForms::ScriptSubmit(IHTMLFormElement *pForm)
  2167. {
  2168. HRESULT hr = E_FAIL;
  2169. if (pForm)
  2170. {
  2171. hr = HandleFormSubmit(pForm);
  2172. }
  2173. return SUCCEEDED(hr) ? S_OK : S_FALSE;
  2174. }
  2175. // Called when user changes a text field. Mark it "dirty" and sink submit event for form
  2176. HRESULT CIntelliForms::UserInput(IHTMLInputTextElement *pTextEle)
  2177. {
  2178. AddToElementList(pTextEle);
  2179. IHTMLFormElement *pForm=NULL;
  2180. pTextEle->get_form(&pForm);
  2181. if (pForm)
  2182. {
  2183. if (S_OK == AddToFormList(pForm))
  2184. {
  2185. AttachToForm(pForm);
  2186. }
  2187. pForm->Release();
  2188. }
  2189. else
  2190. {
  2191. TraceMsg(TF_WARNING|TF_IFORMS, "Iforms: pITE->get_form() returned NULL!");
  2192. }
  2193. return S_OK;
  2194. }
  2195. HRESULT CIntelliForms::AddToElementList(IHTMLInputTextElement *pITE)
  2196. {
  2197. if (m_hdpaElements)
  2198. {
  2199. if (SUCCEEDED(FindInElementList(pITE)))
  2200. {
  2201. return S_FALSE;
  2202. }
  2203. }
  2204. else
  2205. {
  2206. m_hdpaElements = DPA_Create(4);
  2207. }
  2208. if (m_hdpaElements)
  2209. {
  2210. TraceMsg(TF_IFORMS, "CIntelliForms::AddToElementList adding");
  2211. if (DPA_AppendPtr(m_hdpaElements, pITE) >= 0)
  2212. {
  2213. pITE->AddRef();
  2214. return S_OK;
  2215. }
  2216. }
  2217. return E_OUTOFMEMORY;
  2218. }
  2219. HRESULT CIntelliForms::FindInElementList(IHTMLInputTextElement *pITE)
  2220. {
  2221. IUnknown *punk;
  2222. HRESULT hr = E_FAIL;
  2223. pITE->QueryInterface(IID_IUnknown, (void **)&punk);
  2224. if (m_hdpaElements)
  2225. {
  2226. for (int i=DPA_GetPtrCount(m_hdpaElements)-1; i>=0; i--)
  2227. {
  2228. IUnknown *punk2;
  2229. ((IUnknown *)DPA_FastGetPtr(m_hdpaElements, i))->QueryInterface(IID_IUnknown, (void **)&punk2);
  2230. if (punk == punk2)
  2231. {
  2232. punk2->Release();
  2233. break;
  2234. }
  2235. punk2->Release();
  2236. }
  2237. if (i >= 0)
  2238. {
  2239. hr = S_OK;
  2240. }
  2241. }
  2242. punk->Release();
  2243. return hr;
  2244. }
  2245. void CIntelliForms::FreeElementList()
  2246. {
  2247. if (m_hdpaElements)
  2248. {
  2249. for (int i=DPA_GetPtrCount(m_hdpaElements)-1; i>=0; i--)
  2250. {
  2251. ((IUnknown *)(DPA_FastGetPtr(m_hdpaElements, i)))->Release();
  2252. }
  2253. DPA_Destroy(m_hdpaElements);
  2254. m_hdpaElements=NULL;
  2255. }
  2256. }
  2257. HRESULT CIntelliForms::AddToFormList(IHTMLFormElement *pFormEle)
  2258. {
  2259. if (m_hdpaForms)
  2260. {
  2261. if (SUCCEEDED(FindInFormList(pFormEle)))
  2262. {
  2263. return S_FALSE;
  2264. }
  2265. }
  2266. else
  2267. {
  2268. m_hdpaForms = DPA_Create(2);
  2269. }
  2270. if (m_hdpaForms)
  2271. {
  2272. if (DPA_AppendPtr(m_hdpaForms, pFormEle) >= 0)
  2273. {
  2274. TraceMsg(TF_IFORMS, "CIntelliForms::AddToFormList adding");
  2275. pFormEle->AddRef();
  2276. return S_OK;
  2277. }
  2278. }
  2279. return E_OUTOFMEMORY;
  2280. }
  2281. HRESULT CIntelliForms::FindInFormList(IHTMLFormElement *pFormEle)
  2282. {
  2283. IUnknown *punk;
  2284. HRESULT hr = E_FAIL;
  2285. pFormEle->QueryInterface(IID_IUnknown, (void **)&punk);
  2286. if (m_hdpaForms)
  2287. {
  2288. for (int i=DPA_GetPtrCount(m_hdpaForms)-1; i>=0; i--)
  2289. {
  2290. IUnknown *punk2;
  2291. ((IUnknown *)DPA_FastGetPtr(m_hdpaForms, i))->QueryInterface(IID_IUnknown, (void **)&punk2);
  2292. if (punk == punk2)
  2293. {
  2294. punk2->Release();
  2295. break;
  2296. }
  2297. punk2->Release();
  2298. }
  2299. if (i >= 0)
  2300. {
  2301. hr = S_OK;
  2302. }
  2303. }
  2304. punk->Release();
  2305. return hr;
  2306. }
  2307. void CIntelliForms::FreeFormList()
  2308. {
  2309. if (m_hdpaForms)
  2310. {
  2311. for (int i=DPA_GetPtrCount(m_hdpaForms)-1; i>=0; i--)
  2312. {
  2313. ((IUnknown *)(DPA_FastGetPtr(m_hdpaForms, i)))->Release();
  2314. }
  2315. DPA_Destroy(m_hdpaForms);
  2316. m_hdpaForms = NULL;
  2317. }
  2318. }
  2319. //=========================================================================
  2320. //
  2321. // Event sinking class
  2322. //
  2323. // We simply implement IDispatch and make a call into our parent when
  2324. // we receive a sinked event.
  2325. //
  2326. //=========================================================================
  2327. CIntelliForms::CEventSink::CEventSink(CEventSinkCallback *pParent)
  2328. {
  2329. TraceMsg(TF_IFORMS, "CIntelliForms::CEventSink::CEventSink");
  2330. DllAddRef();
  2331. m_cRef = 1;
  2332. m_pParent = pParent;
  2333. }
  2334. CIntelliForms::CEventSink::~CEventSink()
  2335. {
  2336. TraceMsg(TF_IFORMS, "CIntelliForms::CEventSink::~CEventSink");
  2337. ASSERT( m_cRef == 0 );
  2338. DllRelease();
  2339. }
  2340. STDMETHODIMP CIntelliForms::CEventSink::QueryInterface(REFIID riid, void **ppv)
  2341. {
  2342. *ppv = NULL;
  2343. if ((IID_IDispatch == riid) ||
  2344. (IID_IUnknown == riid))
  2345. {
  2346. *ppv = (IDispatch *)this;
  2347. }
  2348. if (NULL != *ppv)
  2349. {
  2350. ((IUnknown *)*ppv)->AddRef();
  2351. return S_OK;
  2352. }
  2353. return E_NOINTERFACE;
  2354. }
  2355. STDMETHODIMP_(ULONG) CIntelliForms::CEventSink::AddRef(void)
  2356. {
  2357. return ++m_cRef;
  2358. }
  2359. STDMETHODIMP_(ULONG) CIntelliForms::CEventSink::Release(void)
  2360. {
  2361. if (--m_cRef == 0)
  2362. {
  2363. delete this;
  2364. return 0;
  2365. }
  2366. return m_cRef;
  2367. }
  2368. HRESULT CIntelliForms::CEventSink::SinkEvents(IHTMLElement2 *pEle2, int iNum, EVENTS *pEvents)
  2369. {
  2370. VARIANT_BOOL bSuccess = VARIANT_TRUE;
  2371. for (int i=0; i<iNum; i++)
  2372. {
  2373. BSTR bstrEvent = SysAllocString(CEventSinkCallback::EventsToSink[(int)(pEvents[i])].pwszEventSubscribe);
  2374. if (bstrEvent)
  2375. {
  2376. pEle2->attachEvent(bstrEvent, (IDispatch *)this, &bSuccess);
  2377. SysFreeString(bstrEvent);
  2378. }
  2379. else
  2380. {
  2381. bSuccess = VARIANT_FALSE;
  2382. }
  2383. if (!bSuccess)
  2384. break;
  2385. }
  2386. return (bSuccess) ? S_OK : E_FAIL;
  2387. }
  2388. HRESULT CIntelliForms::CEventSink::SinkEvents(IHTMLWindow3 *pWin3, int iNum, EVENTS *pEvents)
  2389. {
  2390. VARIANT_BOOL bSuccess = VARIANT_TRUE;
  2391. for (int i=0; i<iNum; i++)
  2392. {
  2393. BSTR bstrEvent = SysAllocString(CEventSinkCallback::EventsToSink[(int)(pEvents[i])].pwszEventSubscribe);
  2394. if (bstrEvent)
  2395. {
  2396. pWin3->attachEvent(bstrEvent, (IDispatch *)this, &bSuccess);
  2397. SysFreeString(bstrEvent);
  2398. }
  2399. else
  2400. {
  2401. bSuccess = VARIANT_FALSE;
  2402. }
  2403. if (!bSuccess)
  2404. break;
  2405. }
  2406. return (bSuccess) ? S_OK : E_FAIL;
  2407. }
  2408. HRESULT CIntelliForms::CEventSink::UnSinkEvents(IHTMLElement2 *pEle2, int iNum, EVENTS *pEvents)
  2409. {
  2410. for (int i=0; i<iNum; i++)
  2411. {
  2412. BSTR bstrEvent = SysAllocString(CEventSinkCallback::EventsToSink[(int)(pEvents[i])].pwszEventSubscribe);
  2413. if (bstrEvent)
  2414. {
  2415. pEle2->detachEvent(bstrEvent, (IDispatch *)this);
  2416. SysFreeString(bstrEvent);
  2417. }
  2418. }
  2419. return S_OK;
  2420. }
  2421. HRESULT CIntelliForms::CEventSink::UnSinkEvents(IHTMLWindow3 *pWin3, int iNum, EVENTS *pEvents)
  2422. {
  2423. for (int i=0; i<iNum; i++)
  2424. {
  2425. BSTR bstrEvent = SysAllocString(CEventSinkCallback::EventsToSink[(int)(pEvents[i])].pwszEventSubscribe);
  2426. if (bstrEvent)
  2427. {
  2428. pWin3->detachEvent(bstrEvent, (IDispatch *)this);
  2429. SysFreeString(bstrEvent);
  2430. }
  2431. }
  2432. return S_OK;
  2433. }
  2434. // IDispatch
  2435. STDMETHODIMP CIntelliForms::CEventSink::GetTypeInfoCount(UINT* /*pctinfo*/)
  2436. {
  2437. return E_NOTIMPL;
  2438. }
  2439. STDMETHODIMP CIntelliForms::CEventSink::GetTypeInfo(/* [in] */ UINT /*iTInfo*/,
  2440. /* [in] */ LCID /*lcid*/,
  2441. /* [out] */ ITypeInfo** /*ppTInfo*/)
  2442. {
  2443. return E_NOTIMPL;
  2444. }
  2445. STDMETHODIMP CIntelliForms::CEventSink::GetIDsOfNames(
  2446. REFIID riid,
  2447. OLECHAR** rgszNames,
  2448. UINT cNames,
  2449. LCID lcid,
  2450. DISPID* rgDispId)
  2451. {
  2452. return E_NOTIMPL;
  2453. }
  2454. STDMETHODIMP CIntelliForms::CEventSink::Invoke(
  2455. DISPID dispIdMember,
  2456. REFIID, LCID,
  2457. WORD wFlags,
  2458. DISPPARAMS* pDispParams,
  2459. VARIANT* pVarResult,
  2460. EXCEPINFO*,
  2461. UINT* puArgErr)
  2462. {
  2463. if (m_pParent && pDispParams && pDispParams->cArgs>=1)
  2464. {
  2465. if (pDispParams->rgvarg[0].vt == VT_DISPATCH)
  2466. {
  2467. IHTMLEventObj *pObj=NULL;
  2468. if (SUCCEEDED(pDispParams->rgvarg[0].pdispVal->QueryInterface(IID_IHTMLEventObj, (void **)&pObj) && pObj))
  2469. {
  2470. EVENTS Event=EVENT_BOGUS;
  2471. BSTR bstrEvent=NULL;
  2472. pObj->get_type(&bstrEvent);
  2473. if (bstrEvent)
  2474. {
  2475. for (int i=0; i<ARRAYSIZE(CEventSinkCallback::EventsToSink); i++)
  2476. {
  2477. if (!StrCmpCW(bstrEvent, CEventSinkCallback::EventsToSink[i].pwszEventName))
  2478. {
  2479. Event = (EVENTS) i;
  2480. break;
  2481. }
  2482. }
  2483. SysFreeString(bstrEvent);
  2484. }
  2485. if (Event != EVENT_BOGUS)
  2486. {
  2487. IHTMLElement *pEle=NULL;
  2488. pObj->get_srcElement(&pEle);
  2489. // EVENT_SCROLL comes from our window so we won't have an
  2490. // element for it
  2491. if (pEle || (Event == EVENT_SCROLL))
  2492. {
  2493. // Call the event handler here
  2494. m_pParent->HandleEvent(pEle, Event, pObj);
  2495. if (pEle)
  2496. {
  2497. pEle->Release();
  2498. }
  2499. }
  2500. }
  2501. pObj->Release();
  2502. }
  2503. }
  2504. }
  2505. return S_OK;
  2506. }
  2507. //=========================================================================
  2508. //
  2509. // Event sinking class
  2510. //
  2511. // We implement IHTMLEditDesigner and make a call into our parent when
  2512. // we receive any event.
  2513. //
  2514. //=========================================================================
  2515. CIntelliForms::CEditEventSink::CEditEventSink(CEditEventSinkCallback *pParent)
  2516. {
  2517. TraceMsg(TF_IFORMS, "CIntelliForms::CEditEventSink::CEditEventSink");
  2518. DllAddRef();
  2519. m_cRef = 1;
  2520. m_pParent = pParent;
  2521. }
  2522. CIntelliForms::CEditEventSink::~CEditEventSink()
  2523. {
  2524. TraceMsg(TF_IFORMS, "CIntelliForms::CEditEventSink::~CEditEventSink");
  2525. ASSERT(m_cRef == 0);
  2526. ASSERT(!m_pEditServices);
  2527. DllRelease();
  2528. }
  2529. STDMETHODIMP CIntelliForms::CEditEventSink::QueryInterface(REFIID riid, void **ppv)
  2530. {
  2531. if ((IID_IHTMLEditDesigner == riid) ||
  2532. (IID_IUnknown == riid))
  2533. {
  2534. *ppv = SAFECAST(this, IHTMLEditDesigner *);
  2535. }
  2536. else
  2537. {
  2538. *ppv = NULL;
  2539. return E_NOINTERFACE;
  2540. }
  2541. AddRef();
  2542. return S_OK;
  2543. }
  2544. STDMETHODIMP_(ULONG) CIntelliForms::CEditEventSink::AddRef(void)
  2545. {
  2546. return ++m_cRef;
  2547. }
  2548. STDMETHODIMP_(ULONG) CIntelliForms::CEditEventSink::Release(void)
  2549. {
  2550. if (--m_cRef == 0)
  2551. {
  2552. delete this;
  2553. return 0;
  2554. }
  2555. return m_cRef;
  2556. }
  2557. HRESULT CIntelliForms::CEditEventSink::Attach(IUnknown *punkElement)
  2558. {
  2559. HRESULT hr = S_OK;
  2560. // Detach from any existing element
  2561. if (m_pEditServices)
  2562. {
  2563. m_pEditServices->RemoveDesigner(this);
  2564. m_pEditServices->Release();
  2565. m_pEditServices = NULL;
  2566. }
  2567. // Attach to any new element
  2568. if (punkElement)
  2569. {
  2570. hr = E_FAIL;
  2571. IHTMLDocument2 *pDoc2 = NULL;
  2572. GetStuffFromEle(punkElement, NULL, &pDoc2);
  2573. if (pDoc2)
  2574. {
  2575. IServiceProvider *pSP = NULL;
  2576. pDoc2->QueryInterface(IID_IServiceProvider, (void **)&pSP);
  2577. if (pSP)
  2578. {
  2579. pSP->QueryService(SID_SHTMLEditServices, IID_IHTMLEditServices, (void **)&m_pEditServices);
  2580. pSP->Release();
  2581. }
  2582. if (m_pEditServices)
  2583. {
  2584. hr = m_pEditServices->AddDesigner(this);
  2585. }
  2586. pDoc2->Release();
  2587. }
  2588. }
  2589. return hr;
  2590. }
  2591. HRESULT CIntelliForms::CEditEventSink::PreHandleEvent(DISPID inEvtDispId, IHTMLEventObj *pIEventObj)
  2592. {
  2593. if (m_pParent)
  2594. {
  2595. return m_pParent->PreHandleEvent(inEvtDispId, pIEventObj);
  2596. }
  2597. return S_FALSE;
  2598. }
  2599. HRESULT CIntelliForms::CEditEventSink::PostHandleEvent(DISPID inEvtDispId, IHTMLEventObj *pIEventObj)
  2600. {
  2601. return S_FALSE;
  2602. }
  2603. HRESULT CIntelliForms::CEditEventSink::TranslateAccelerator(DISPID inEvtDispId, IHTMLEventObj *pIEventObj)
  2604. {
  2605. return S_FALSE;
  2606. }
  2607. //=========================================================================
  2608. //
  2609. // AutoSuggest class
  2610. //
  2611. // Handles connecting and disconnecting the AutoComplete object, as well
  2612. // as translating between Trident OM and Edit window messages
  2613. //=========================================================================
  2614. CIntelliForms::CAutoSuggest::CAutoSuggest(CIntelliForms *pParent, BOOL fEnabled, BOOL fEnabledPW)
  2615. {
  2616. TraceMsg(TF_IFORMS, "CIntelliForms::CAutoSuggest::CAutoSuggest");
  2617. m_pParent = pParent;
  2618. m_fEnabled = fEnabled;
  2619. m_fEnabledPW = fEnabledPW;
  2620. ASSERT(m_pEventSink == NULL);
  2621. ASSERT(m_pAutoComplete == NULL);
  2622. ASSERT(m_hwndEdit == NULL);
  2623. ASSERT(m_pTextEle == NULL);
  2624. //
  2625. // bug 81414 : To avoid clashing with app messages used by the edit window, we
  2626. // use registered messages.
  2627. //
  2628. m_uMsgItemActivate = RegisterWindowMessageA("AC_ItemActivate");
  2629. if (m_uMsgItemActivate == 0)
  2630. {
  2631. m_uMsgItemActivate = WM_APP + 301;
  2632. }
  2633. // Register our window class if necessary
  2634. if (!s_fRegisteredWndClass)
  2635. {
  2636. s_fRegisteredWndClass = TRUE;
  2637. WNDCLASSEXW wndclass =
  2638. {
  2639. sizeof(WNDCLASSEX),
  2640. 0,
  2641. CIntelliForms::CAutoSuggest::WndProc,
  2642. 0,
  2643. sizeof(DWORD_PTR),
  2644. g_hinst,
  2645. NULL,
  2646. NULL,
  2647. NULL,
  2648. NULL,
  2649. c_szEditWndClass
  2650. };
  2651. if (!RegisterClassEx(&wndclass))
  2652. {
  2653. TraceMsg(TF_IFORMS, "Intelliforms failed to register wnd class!");
  2654. }
  2655. }
  2656. }
  2657. CIntelliForms::CAutoSuggest::~CAutoSuggest()
  2658. {
  2659. TraceMsg(TF_IFORMS, "CIntelliForms::CAutoSuggest::~CAutoSuggest");
  2660. CleanUp();
  2661. }
  2662. HRESULT CIntelliForms::CAutoSuggest::CleanUp()
  2663. {
  2664. SetParent(NULL);
  2665. DetachFromInput();
  2666. return S_OK;
  2667. }
  2668. // List of all events we sink for an individual INPUT tag
  2669. // post IE5.5 we can use CEditEventSink instead of CEventSink for all of these events.
  2670. CEventSinkCallback::EVENTS CIntelliForms::CAutoSuggest::s_EventsToSink[] =
  2671. {
  2672. EVENT_KEYPRESS,
  2673. EVENT_KEYDOWN,
  2674. EVENT_MOUSEDOWN,
  2675. EVENT_DBLCLICK,
  2676. EVENT_FOCUS,
  2677. EVENT_BLUR,
  2678. };
  2679. HRESULT CIntelliForms::CAutoSuggest::AttachToInput(IHTMLInputTextElement *pTextEle)
  2680. {
  2681. HRESULT hr;
  2682. TraceMsg(TF_IFORMS, "CIntelliForms::CAutoSuggest::AttachToInput");
  2683. if (!pTextEle)
  2684. return E_INVALIDARG;
  2685. hr = DetachFromInput();
  2686. if (SUCCEEDED(hr))
  2687. {
  2688. m_pTextEle = pTextEle;
  2689. pTextEle->AddRef();
  2690. if (!m_pEventSink)
  2691. {
  2692. m_pEventSink = new CEventSink(this);
  2693. if (!m_pEventSink)
  2694. {
  2695. hr = E_OUTOFMEMORY;
  2696. }
  2697. }
  2698. if (SUCCEEDED(hr))
  2699. {
  2700. // Hook up our event sink
  2701. IHTMLElement2 *pEle2=NULL;
  2702. hr = pTextEle->QueryInterface(IID_IHTMLElement2, (void **)&pEle2);
  2703. if (pEle2)
  2704. {
  2705. hr = m_pEventSink->SinkEvents(pEle2, ARRAYSIZE(s_EventsToSink), s_EventsToSink);
  2706. pEle2->Release();
  2707. }
  2708. }
  2709. }
  2710. if (FAILED(hr))
  2711. {
  2712. TraceMsg(TF_IFORMS, "IForms: AttachToInput failed");
  2713. DetachFromInput();
  2714. }
  2715. return hr;
  2716. }
  2717. HRESULT CIntelliForms::CAutoSuggest::DetachFromInput()
  2718. {
  2719. if (!m_pTextEle)
  2720. {
  2721. return S_FALSE;
  2722. }
  2723. TraceMsg(TF_IFORMS, "CIntelliForms::CAutoSuggest::DetachFromInput");
  2724. // Auto Fill Password here, since we get ACTIVEELEMENT change before blur event
  2725. BSTR bstrUsername=NULL;
  2726. m_pTextEle->get_value(&bstrUsername);
  2727. if (bstrUsername)
  2728. {
  2729. CheckAutoFillPassword(bstrUsername);
  2730. SysFreeString(bstrUsername);
  2731. }
  2732. if (m_bstrLastUsername)
  2733. {
  2734. SysFreeString(m_bstrLastUsername);
  2735. m_bstrLastUsername=NULL;
  2736. }
  2737. if (m_hwndEdit)
  2738. {
  2739. // This is for subclass wndproc
  2740. SendMessage(m_hwndEdit, WM_KILLFOCUS, 0, 0);
  2741. }
  2742. if (m_pEnumString)
  2743. {
  2744. m_pEnumString->UnInit();
  2745. m_pEnumString->Release();
  2746. m_pEnumString = NULL;
  2747. }
  2748. if (m_pEventSink)
  2749. {
  2750. IHTMLElement2 *pEle2=NULL;
  2751. m_pTextEle->QueryInterface(IID_IHTMLElement2, (void **)&pEle2);
  2752. if (pEle2)
  2753. {
  2754. m_pEventSink->UnSinkEvents(pEle2, ARRAYSIZE(s_EventsToSink), s_EventsToSink);
  2755. pEle2->Release();
  2756. }
  2757. m_pEventSink->SetParent(NULL);
  2758. m_pEventSink->Release();
  2759. m_pEventSink=NULL;
  2760. }
  2761. SAFERELEASE(m_pAutoComplete);
  2762. SAFERELEASE(m_pAutoCompleteDD);
  2763. if (m_hwndEdit)
  2764. {
  2765. DestroyWindow(m_hwndEdit);
  2766. m_hwndEdit = NULL;
  2767. }
  2768. SAFERELEASE(m_pTextEle);
  2769. m_fInitAutoComplete = FALSE;
  2770. return S_OK;
  2771. }
  2772. // Creates autocomplete and string enumerator.
  2773. HRESULT CIntelliForms::CAutoSuggest::CreateAutoComplete()
  2774. {
  2775. if (m_fInitAutoComplete)
  2776. {
  2777. return (m_pAutoCompleteDD != NULL) ? S_OK : E_FAIL;
  2778. }
  2779. HRESULT hr = S_OK;
  2780. ASSERT(!m_hwndEdit && !m_pEnumString && !m_pAutoComplete && !m_pAutoCompleteDD);
  2781. // Create the edit window
  2782. #ifndef UNIX
  2783. m_hwndEdit = CreateWindowEx(0, c_szEditWndClass, TEXT("IntelliFormProxy"), WS_POPUP,
  2784. #else
  2785. m_hwndEdit = CreateWindowEx(WS_EX_MW_UNMANAGED_WINDOW, c_szEditWndClass, TEXT("IntelliFormProxy"), WS_POPUP,
  2786. #endif
  2787. 300, 200, 200, 50, m_pParent->m_hwndBrowser, NULL, g_hinst, this);
  2788. if (!m_hwndEdit)
  2789. {
  2790. hr = E_OUTOFMEMORY;
  2791. }
  2792. if (SUCCEEDED(hr))
  2793. {
  2794. // Create our enumerator
  2795. m_pEnumString = new CEnumString();
  2796. if (m_pEnumString)
  2797. {
  2798. m_pEnumString->Init(m_pTextEle, m_pParent);
  2799. // Create the AutoComplete Object
  2800. if (!m_pAutoComplete)
  2801. {
  2802. hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, IID_IAutoComplete2, (void **)&m_pAutoComplete);
  2803. if (m_pAutoComplete)
  2804. {
  2805. m_pAutoComplete->QueryInterface(IID_IAutoCompleteDropDown, (void **)&m_pAutoCompleteDD);
  2806. if (!m_pAutoCompleteDD)
  2807. {
  2808. SAFERELEASE(m_pAutoComplete);
  2809. }
  2810. }
  2811. }
  2812. if (m_pAutoComplete)
  2813. {
  2814. hr = m_pAutoComplete->Init(m_hwndEdit, (IUnknown *) m_pEnumString, NULL, NULL);
  2815. DWORD dwOptions = ACO_AUTOSUGGEST | ACO_UPDOWNKEYDROPSLIST;
  2816. // Add the RTLREADING option to the dropdown, if the element is RTL
  2817. BSTR bstrDir = NULL;
  2818. IHTMLElement2 *pEle2=NULL;
  2819. m_pTextEle->QueryInterface(IID_IHTMLElement2, (void **)&pEle2);
  2820. if (pEle2)
  2821. {
  2822. pEle2->get_dir(&bstrDir);
  2823. pEle2->Release();
  2824. }
  2825. if (bstrDir)
  2826. {
  2827. if (!StrCmpIW(bstrDir, L"RTL"))
  2828. {
  2829. dwOptions |= ACO_RTLREADING;
  2830. }
  2831. SysFreeString(bstrDir);
  2832. }
  2833. m_pAutoComplete->SetOptions(dwOptions);
  2834. }
  2835. }
  2836. }
  2837. m_fInitAutoComplete = TRUE;
  2838. ASSERT_MSG(SUCCEEDED(hr), "IForms: CreateAutoComplete failed");
  2839. return hr;
  2840. }
  2841. void CIntelliForms::CAutoSuggest::CheckAutoFillPassword(LPCWSTR pwszUsername)
  2842. {
  2843. // We don't autofill their password unless we know they've hit a key
  2844. if (m_pParent && m_fEnabledPW && m_fAllowAutoFillPW)
  2845. {
  2846. if (m_bstrLastUsername && !StrCmpCW(pwszUsername, m_bstrLastUsername))
  2847. {
  2848. return;
  2849. }
  2850. SysFreeString(m_bstrLastUsername);
  2851. m_bstrLastUsername = SysAllocString(pwszUsername);
  2852. m_pParent->AutoFillPassword(m_pTextEle, pwszUsername);
  2853. }
  2854. }
  2855. HRESULT GetScreenCoordinates(IUnknown *punkEle, HWND hwnd, long *plLeft, long *plTop, long *plWidth, long *plHeight)
  2856. {
  2857. long lScreenLeft=0, lScreenTop=0;
  2858. HRESULT hr = E_FAIL;
  2859. *plLeft = *plTop = *plWidth = *plHeight = 0;
  2860. IHTMLElement2 *pEle2;
  2861. if (SUCCEEDED(punkEle->QueryInterface(IID_IHTMLElement2, (void **)&pEle2)) && pEle2)
  2862. {
  2863. IHTMLRect *pRect=NULL;
  2864. if (SUCCEEDED(pEle2->getBoundingClientRect(&pRect)) && pRect)
  2865. {
  2866. IHTMLWindow2 *pWin2;
  2867. long lLeft, lRight, lTop, lBottom;
  2868. pRect->get_left(&lLeft);
  2869. pRect->get_right(&lRight);
  2870. pRect->get_top(&lTop);
  2871. pRect->get_bottom(&lBottom);
  2872. lBottom -= 2; // put dropdown on top of edit box
  2873. if (lBottom < lTop)
  2874. {
  2875. lBottom = lTop;
  2876. }
  2877. if (lTop >= 0 && lLeft >= 0)
  2878. {
  2879. GetStuffFromEle(punkEle, &pWin2, NULL);
  2880. if (pWin2)
  2881. {
  2882. IHTMLWindow3 *pWin3;
  2883. if (SUCCEEDED(pWin2->QueryInterface(IID_IHTMLWindow3, (void **)&pWin3)) && pWin3)
  2884. {
  2885. IHTMLScreen *pScreen = NULL;
  2886. RECT rcBrowserWnd;
  2887. pWin3->get_screenLeft(&lScreenLeft);
  2888. pWin3->get_screenTop(&lScreenTop);
  2889. // GetClientRect & the screen_* APIs return document coordinates.
  2890. // We're position using device coordinates.
  2891. // Use document (currently 96DPI) and device resolutions & transform
  2892. pWin2->get_screen(&pScreen);
  2893. if (pScreen)
  2894. {
  2895. IHTMLScreen2 * pScreen2 = NULL;
  2896. if (SUCCEEDED(pScreen->QueryInterface(IID_IHTMLScreen2, (void **)&pScreen2)))
  2897. {
  2898. if (pScreen2)
  2899. {
  2900. long xDeviceDPI, yDeviceDPI, xLogicalDPI, yLogicalDPI;
  2901. pScreen2->get_deviceXDPI(&xDeviceDPI);
  2902. pScreen2->get_deviceYDPI(&yDeviceDPI);
  2903. pScreen2->get_logicalXDPI(&xLogicalDPI);
  2904. pScreen2->get_logicalYDPI(&yLogicalDPI);
  2905. lBottom = (lBottom * yDeviceDPI) / yLogicalDPI;
  2906. lTop = (lTop * yDeviceDPI) / yLogicalDPI;
  2907. lScreenTop = (lScreenTop * yDeviceDPI) / yLogicalDPI;
  2908. lLeft = (lLeft * xDeviceDPI) / xLogicalDPI;
  2909. lRight = (lRight * xDeviceDPI) / xLogicalDPI;
  2910. lScreenLeft = (lScreenLeft * xDeviceDPI) / xLogicalDPI;
  2911. pScreen2->Release();
  2912. }
  2913. }
  2914. pScreen->Release();
  2915. }
  2916. if (GetWindowRect(hwnd, &rcBrowserWnd))
  2917. {
  2918. // Clip the right edge to the window
  2919. if (lRight+lScreenLeft > rcBrowserWnd.right)
  2920. {
  2921. lRight = rcBrowserWnd.right - lScreenLeft;
  2922. }
  2923. *plLeft = lScreenLeft + lLeft;
  2924. *plWidth = lRight-lLeft;
  2925. *plTop = lScreenTop + lTop;
  2926. *plHeight = lBottom-lTop;
  2927. hr = S_OK;
  2928. if (*plWidth < MINIMUM_WIDTH)
  2929. {
  2930. // Primitive minimum width for now
  2931. *plWidth = MINIMUM_WIDTH;
  2932. }
  2933. }
  2934. pWin3->Release();
  2935. }
  2936. pWin2->Release();
  2937. }
  2938. }
  2939. pRect->Release();
  2940. }
  2941. pEle2->Release();
  2942. }
  2943. return hr;
  2944. }
  2945. HRESULT CIntelliForms::CAutoSuggest::UpdateDropdownPosition()
  2946. {
  2947. if (m_pTextEle && m_pParent && m_hwndEdit)
  2948. {
  2949. long lLeft, lTop, lWidth, lHeight;
  2950. if (SUCCEEDED(GetScreenCoordinates(m_pTextEle, m_pParent->m_hwndBrowser, &lLeft, &lTop, &lWidth, &lHeight)))
  2951. {
  2952. MoveWindow(m_hwndEdit, lLeft, lTop, lWidth, lHeight, FALSE);
  2953. }
  2954. else
  2955. {
  2956. // Send "escape" key to autocomplete so that it hides the dropdown.
  2957. // This will happen if dropdown moves outside of parent window, for example.
  2958. SendMessage(m_hwndEdit, IF_CHAR, (WPARAM) VK_ESCAPE, 0);
  2959. }
  2960. }
  2961. return S_OK;
  2962. }
  2963. HRESULT CIntelliForms::CAutoSuggest::HandleEvent(IHTMLElement *pEle, EVENTS Event, IHTMLEventObj *pEventObj)
  2964. {
  2965. TraceMsg(TF_IFORMS, "CIntelliForms::CAutoSuggest::HandleEvent Event=%ws", EventsToSink[Event].pwszEventName);
  2966. ASSERT(SHIsSameObject(pEle, m_pTextEle));
  2967. long lKey = 0;
  2968. BOOL fIsComposition = FALSE;
  2969. if (!m_pParent)
  2970. {
  2971. TraceMsg(TF_WARNING|TF_IFORMS, "IForms autosuggest receiving events while invalid");
  2972. return E_FAIL;
  2973. }
  2974. if (Event == EVENT_KEYPRESS || Event == EVENT_KEYDOWN)
  2975. {
  2976. pEventObj->get_keyCode(&lKey);
  2977. }
  2978. if (Event == EVENT_COMPOSITION)
  2979. {
  2980. fIsComposition = TRUE;
  2981. Event = EVENT_KEYPRESS; // Pretend to be a "keypress" for various processing below
  2982. }
  2983. if (Event == EVENT_NOTIFY)
  2984. {
  2985. // Send WM_IME_NOTIFY to AutoComplete so it can hide the dropdown
  2986. // if necessary
  2987. IHTMLEventObj3 *pObj3 = NULL;
  2988. pEventObj->QueryInterface(IID_PPV_ARG(IHTMLEventObj3, &pObj3));
  2989. if (pObj3)
  2990. {
  2991. LONG_PTR wParam = 0;
  2992. pObj3->get_imeNotifyCommand(&wParam);
  2993. SendMessage(m_hwndEdit, WM_IME_NOTIFY, (WPARAM)wParam, 0);
  2994. pObj3->Release();
  2995. }
  2996. return S_OK;
  2997. }
  2998. if (!m_fEnabled && !m_fEnabledPW)
  2999. {
  3000. // If the dropdown isn't enabled, our only purpose is to tell Intelliforms when
  3001. // user activity occurs for the first-time enable dialog box.
  3002. if (Event == EVENT_KEYPRESS && lKey != VK_TAB)
  3003. {
  3004. // Add this element to the master list so we save it when we submit
  3005. // and sink the submit event for this element's form
  3006. MarkDirty();
  3007. }
  3008. return S_OK;
  3009. }
  3010. if (Event == EVENT_KEYDOWN || Event == EVENT_KEYPRESS ||
  3011. Event == EVENT_MOUSEDOWN || Event == EVENT_DBLCLICK)
  3012. {
  3013. m_fAllowAutoFillPW = TRUE;
  3014. // Create our autocomplete object if it hasn't happened yet.
  3015. // If it's "tab" we don't create it; we're leaving the field
  3016. if (lKey != VK_TAB)
  3017. {
  3018. if (FAILED(CreateAutoComplete()))
  3019. return E_FAIL;
  3020. }
  3021. else
  3022. {
  3023. // Add this element to the master list so we save it when we submit
  3024. // and sink the submit event for this element's form
  3025. MarkDirty();
  3026. }
  3027. ASSERT((m_pEnumString && m_hwndEdit) || (lKey==VK_TAB));
  3028. }
  3029. // If AutoComplete hasn't been initialized there's nothing for us to do
  3030. if (!m_pAutoCompleteDD || !m_hwndEdit)
  3031. {
  3032. return E_FAIL;
  3033. }
  3034. // Update the position of our hidden edit box
  3035. long lLeft, lTop, lWidth, lHeight;
  3036. // call UpdateDropdownPosition instead
  3037. if (SUCCEEDED(GetScreenCoordinates(pEle, m_pParent->m_hwndBrowser, &lLeft, &lTop, &lWidth, &lHeight)))
  3038. {
  3039. MoveWindow(m_hwndEdit, lLeft, lTop, lWidth, lHeight, FALSE);
  3040. }
  3041. switch (Event)
  3042. {
  3043. case EVENT_FOCUS :
  3044. SendMessage(m_hwndEdit, WM_SETFOCUS, 0, 0);
  3045. break;
  3046. case EVENT_BLUR:
  3047. {
  3048. if (m_hwndEdit)
  3049. {
  3050. SendMessage(m_hwndEdit, WM_KILLFOCUS, 0, 0);
  3051. }
  3052. // ensure that script hasn't changed value of edit field?
  3053. BSTR bstrUsername=NULL;
  3054. m_pTextEle->get_value(&bstrUsername);
  3055. if (bstrUsername)
  3056. {
  3057. CheckAutoFillPassword(bstrUsername);
  3058. SysFreeString(bstrUsername);
  3059. }
  3060. }
  3061. break;
  3062. case EVENT_MOUSEDOWN:
  3063. case EVENT_DBLCLICK:
  3064. {
  3065. // If the dropdown is invisible, give AutoComplete a downarrow
  3066. long lButton=0;
  3067. pEventObj->get_button(&lButton);
  3068. if ((Event == EVENT_DBLCLICK) ||
  3069. (lButton & 1)) // Left button down?
  3070. {
  3071. DWORD dwFlags;
  3072. if (SUCCEEDED(m_pAutoCompleteDD->GetDropDownStatus(&dwFlags, NULL)) &&
  3073. !(dwFlags & ACDD_VISIBLE))
  3074. {
  3075. TraceMsg(TF_IFORMS, "IForms sending downarrow because of mouse click");
  3076. PostMessage(m_hwndEdit, IF_KEYDOWN, (WPARAM)VK_DOWN, 0);
  3077. m_fEscapeHit = FALSE;
  3078. }
  3079. }
  3080. }
  3081. break;
  3082. case EVENT_KEYPRESS:
  3083. {
  3084. // Add this element to the master list so we save it when we submit
  3085. // and sink the submit event for this element's form
  3086. MarkDirty();
  3087. // Ignore ctrl-enter (quickcomplete) (may be unnecessary)
  3088. if (lKey == VK_RETURN)
  3089. {
  3090. VARIANT_BOOL bCtrl;
  3091. if (SUCCEEDED(pEventObj->get_ctrlKey(&bCtrl)) && bCtrl)
  3092. {
  3093. lKey = 0;
  3094. }
  3095. }
  3096. if (lKey != 0)
  3097. {
  3098. if (lKey == m_lCancelKeyPress)
  3099. {
  3100. // tell MSHTML to ignore this keystroke (may be tab, enter, escape)
  3101. TraceMsg(TF_IFORMS, "Intelliforms cancelling default action for EVENT_KEYPRESS=%d", lKey);
  3102. VARIANT v;
  3103. v.vt = VT_BOOL;
  3104. v.boolVal = VARIANT_FALSE;
  3105. pEventObj->put_returnValue(v);
  3106. if(!(lKey == VK_DOWN || lKey == VK_UP))
  3107. pEventObj->put_cancelBubble(VARIANT_TRUE);
  3108. }
  3109. m_lCancelKeyPress = 0;
  3110. // Tell AutoComplete about this keystroke
  3111. if (!m_fEscapeHit)
  3112. {
  3113. PostMessage(m_hwndEdit, IF_CHAR, (WPARAM)lKey, 0);
  3114. }
  3115. }
  3116. if (fIsComposition)
  3117. {
  3118. // Tell AutoComplete about the new string. This must be a Post so that
  3119. // Trident handles the event before we send the WM_CHAR to browseui.
  3120. PostMessage(m_hwndEdit, IF_IME_COMPOSITION, 0, 0);
  3121. }
  3122. }
  3123. break;
  3124. case EVENT_KEYDOWN:
  3125. {
  3126. long lKey;
  3127. BOOL fCancelEvent=FALSE, // Cancel default MSHTML action?
  3128. fForwardKeystroke=TRUE; // Forward keystroke to AutoComplete?
  3129. pEventObj->get_keyCode(&lKey);
  3130. if (m_fEscapeHit)
  3131. {
  3132. // They dismissed the dropdown; don't bring it back unless they ask for it
  3133. if (lKey == VK_DOWN)
  3134. {
  3135. m_fEscapeHit = FALSE;
  3136. }
  3137. else
  3138. {
  3139. fForwardKeystroke = FALSE;
  3140. }
  3141. }
  3142. if (lKey != 0)
  3143. {
  3144. if ((lKey == VK_RETURN) || (lKey == VK_TAB))
  3145. {
  3146. fForwardKeystroke=FALSE;
  3147. LPWSTR pwszString=NULL;
  3148. if (SUCCEEDED(m_pAutoCompleteDD->GetDropDownStatus(NULL, &pwszString)) && pwszString)
  3149. {
  3150. // User is inside dropdown
  3151. fForwardKeystroke=TRUE;
  3152. // Set this value into our edit field
  3153. SetText(pwszString);
  3154. // We will fill in their password if they asked for it in m_uMsgItemActivate
  3155. if (lKey == VK_RETURN)
  3156. {
  3157. // Avoid submitting this form
  3158. fCancelEvent = TRUE;
  3159. }
  3160. CoTaskMemFree(pwszString);
  3161. }
  3162. else if (lKey == VK_RETURN)
  3163. {
  3164. // User's gonna submit. Give 'em their password first.
  3165. // ensure that script hasn't changed value of edit field?
  3166. BSTR bstrUsername=NULL;
  3167. m_pTextEle->get_value(&bstrUsername);
  3168. if (bstrUsername)
  3169. {
  3170. CheckAutoFillPassword(bstrUsername);
  3171. SysFreeString(bstrUsername);
  3172. }
  3173. }
  3174. }
  3175. else if (lKey == VK_DELETE)
  3176. {
  3177. LPWSTR pwszString=NULL;
  3178. if (SUCCEEDED(m_pAutoCompleteDD->GetDropDownStatus(NULL, &pwszString)) && pwszString)
  3179. {
  3180. // User is inside dropdown
  3181. fForwardKeystroke=FALSE;
  3182. // Delete this value from our string lists
  3183. CStringList *psl=NULL;
  3184. BSTR bstrName;
  3185. CIntelliForms::GetName(m_pTextEle, &bstrName);
  3186. if (bstrName)
  3187. {
  3188. int iIndex;
  3189. if (SUCCEEDED(m_pParent->ReadFromStore(bstrName, &psl)) &&
  3190. SUCCEEDED(psl->FindString(pwszString, -1, &iIndex, FALSE)))
  3191. {
  3192. TraceMsg(TF_IFORMS, "IForms: Deleting string \"%ws\"", pwszString);
  3193. psl->DeleteString(iIndex);
  3194. // We deleted string.
  3195. if (psl->NumStrings() > 0)
  3196. {
  3197. m_pParent->WriteToStore(bstrName, psl);
  3198. }
  3199. else
  3200. {
  3201. m_pParent->DeleteFromStore(bstrName);
  3202. }
  3203. }
  3204. }
  3205. SysFreeString(bstrName);
  3206. if (psl) delete psl;
  3207. // avoid deleting a character from the edit window; user was inside dropdown
  3208. fCancelEvent = TRUE;
  3209. // Check this url to see if we should maybe delete a password entry
  3210. m_pParent->DeletePassword(pwszString);
  3211. // Get AutoComplete to fill in the dropdown again
  3212. m_pEnumString->ResetEnum();
  3213. m_pAutoCompleteDD->ResetEnumerator();
  3214. CoTaskMemFree(pwszString);
  3215. }
  3216. }
  3217. if (lKey == VK_ESCAPE)
  3218. {
  3219. DWORD dwFlags;
  3220. if (SUCCEEDED(m_pAutoCompleteDD->GetDropDownStatus(&dwFlags, NULL)) &&
  3221. (dwFlags & ACDD_VISIBLE))
  3222. {
  3223. fCancelEvent = TRUE;
  3224. m_fEscapeHit = TRUE;
  3225. }
  3226. }
  3227. if (lKey == VK_DOWN || lKey == VK_UP)
  3228. {
  3229. // Cancel the MSHTML events. This will cause MSHTML to return
  3230. // S_OK instead of S_FALSE from its TranslateAccelerator, and we
  3231. // won't get multiple keystrokes in different panes
  3232. fCancelEvent = TRUE;
  3233. }
  3234. if (fForwardKeystroke)
  3235. {
  3236. PostMessage(m_hwndEdit, IF_KEYDOWN, lKey, 0);
  3237. if (lKey == VK_BACK)
  3238. {
  3239. // Never get OnKeyPress for this guy
  3240. PostMessage(m_hwndEdit, IF_CHAR, lKey, 0);
  3241. }
  3242. }
  3243. if (fCancelEvent)
  3244. {
  3245. TraceMsg(TF_IFORMS, "Intelliforms cancelling default action for EVENT_KEYDOWN=%d", lKey);
  3246. m_lCancelKeyPress = lKey; // Cancel the EVENT_KEYPRESS when it comes
  3247. VARIANT v;
  3248. v.vt = VT_BOOL;
  3249. v.boolVal = VARIANT_FALSE;
  3250. pEventObj->put_returnValue(v);
  3251. if(!(lKey == VK_DOWN || lKey == VK_UP))
  3252. pEventObj->put_cancelBubble(VARIANT_TRUE);
  3253. }
  3254. else
  3255. {
  3256. m_lCancelKeyPress = 0;
  3257. }
  3258. }
  3259. }
  3260. break;
  3261. }
  3262. return S_OK;
  3263. }
  3264. HRESULT CIntelliForms::CAutoSuggest::GetText(int cchTextMax, LPWSTR pszTextOut, LRESULT *lcchCopied)
  3265. {
  3266. *pszTextOut = TEXT('\0');
  3267. *lcchCopied = 0;
  3268. if (m_pTextEle)
  3269. {
  3270. BSTR bstr=NULL;
  3271. m_pTextEle->get_value(&bstr);
  3272. if (bstr)
  3273. {
  3274. StrCpyN(pszTextOut, bstr, cchTextMax);
  3275. *lcchCopied = lstrlenW(pszTextOut); // needed for NT
  3276. SysFreeString(bstr);
  3277. }
  3278. }
  3279. return (*pszTextOut) ? S_OK : E_FAIL;
  3280. }
  3281. HRESULT CIntelliForms::CAutoSuggest::GetTextLength(int *pcch)
  3282. {
  3283. *pcch = 0;
  3284. if (m_pTextEle)
  3285. {
  3286. BSTR bstr=NULL;
  3287. m_pTextEle->get_value(&bstr);
  3288. if (bstr)
  3289. {
  3290. *pcch = SysStringLen(bstr);
  3291. SysFreeString(bstr);
  3292. }
  3293. }
  3294. return S_OK;
  3295. }
  3296. HRESULT CIntelliForms::CAutoSuggest::SetText(LPCWSTR pszTextIn)
  3297. {
  3298. if (m_pTextEle && pszTextIn)
  3299. {
  3300. BSTR bstr=SysAllocString(pszTextIn);
  3301. if (bstr)
  3302. {
  3303. // Even though we know we already have this string in our dropdown, mark
  3304. // it as dirty so that we sink submit event; can be necessary in saved
  3305. // password situation.
  3306. MarkDirty();
  3307. // Make sure we don't put a string longer than the max length in this field
  3308. long lMaxLen=-1;
  3309. m_pTextEle->get_maxLength(&lMaxLen);
  3310. if ((lMaxLen >= 0) && (lstrlenW(bstr) > lMaxLen))
  3311. {
  3312. bstr[lMaxLen] = L'\0';
  3313. }
  3314. m_pTextEle->put_value(bstr);
  3315. SysFreeString(bstr);
  3316. }
  3317. }
  3318. TraceMsg(TF_IFORMS, "CIntelliForms::CAutoSuggest::SetText \"%ws\"", pszTextIn);
  3319. return S_OK;
  3320. }
  3321. #define MY_GWL_THISPTR 0
  3322. LRESULT CALLBACK CIntelliForms::CAutoSuggest::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  3323. {
  3324. CIntelliForms::CAutoSuggest *pThis = (CIntelliForms::CAutoSuggest *)GetWindowLongPtr(hwnd, MY_GWL_THISPTR);
  3325. switch (uMsg)
  3326. {
  3327. case WM_CREATE:
  3328. {
  3329. LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
  3330. if (!pcs || !(pcs->lpCreateParams))
  3331. {
  3332. return -1;
  3333. }
  3334. SetWindowLongPtr(hwnd, MY_GWL_THISPTR, (LONG_PTR) pcs->lpCreateParams);
  3335. return 0;
  3336. }
  3337. case WM_GETTEXT:
  3338. if (pThis)
  3339. {
  3340. LRESULT lcchCopied=0;
  3341. if (g_fRunningOnNT)
  3342. {
  3343. pThis->GetText((int)wParam, (LPWSTR) lParam, &lcchCopied);
  3344. }
  3345. else
  3346. {
  3347. // We are actually an ANSI window. Convert.
  3348. LPWSTR pwszOutBuf = (LPWSTR) LocalAlloc(LPTR, (wParam+1)*sizeof(WCHAR));
  3349. if (pwszOutBuf)
  3350. {
  3351. pThis->GetText((int)wParam, pwszOutBuf, &lcchCopied);
  3352. SHUnicodeToAnsi(pwszOutBuf, (LPSTR) lParam, (int)(wParam+1));
  3353. LocalFree((HLOCAL)pwszOutBuf);
  3354. pwszOutBuf = NULL;
  3355. }
  3356. }
  3357. return lcchCopied;
  3358. }
  3359. return 0;
  3360. case WM_GETTEXTLENGTH:
  3361. if (pThis)
  3362. {
  3363. int iLen;
  3364. pThis->GetTextLength(&iLen);
  3365. return iLen;
  3366. }
  3367. return 0;
  3368. case EM_GETSEL:
  3369. // Must return zeroes here or autocomp will use uninitialized
  3370. // values and crash
  3371. if (wParam) (*(DWORD *)wParam) = 0;
  3372. if (lParam) (*(DWORD *)lParam) = 0;
  3373. break;
  3374. case IF_IME_COMPOSITION:
  3375. // Forward a WM_CHAR. Autocomplete will notice that the rest of the string
  3376. // has changed if necessary (it does a GetText)
  3377. SendMessage(hwnd, WM_CHAR, 32, 0);
  3378. break;
  3379. case IF_CHAR:
  3380. SendMessage(hwnd, WM_CHAR, wParam, lParam);
  3381. break;
  3382. case IF_KEYDOWN:
  3383. SendMessage(hwnd, WM_KEYDOWN, wParam, lParam);
  3384. break;
  3385. case WM_KEYDOWN:
  3386. case WM_CHAR:
  3387. return 0; // eat it (see notes at top of file)
  3388. default:
  3389. // Check registered message
  3390. if (pThis && uMsg == pThis->m_uMsgItemActivate)
  3391. {
  3392. TraceMsg(TF_IFORMS, "IForms: Received AM_ITEMACTIVATE(WM_APP+2)");
  3393. pThis->SetText((LPCWSTR)lParam);
  3394. pThis->CheckAutoFillPassword((LPCWSTR)lParam);
  3395. return 0;
  3396. }
  3397. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  3398. }
  3399. return 1;
  3400. }
  3401. CIntelliForms::CAutoSuggest::CEnumString::CEnumString()
  3402. {
  3403. // TraceMsg(TF_IFORMS, "CIntelliForms::CAutoSuggest::CEnumString::CEnumString");
  3404. DllAddRef();
  3405. InitializeCriticalSection(&m_crit);
  3406. m_cRef = 1;
  3407. }
  3408. CIntelliForms::CAutoSuggest::CEnumString::~CEnumString()
  3409. {
  3410. // TraceMsg(TF_IFORMS, "CIntelliForms::CAutoSuggest::CEnumString::~CEnumString");
  3411. if (m_pslMain)
  3412. {
  3413. delete m_pslMain;
  3414. }
  3415. SysFreeString(m_bstrName);
  3416. if (m_pszOpsValue)
  3417. {
  3418. CoTaskMemFree(m_pszOpsValue);
  3419. }
  3420. DeleteCriticalSection(&m_crit);
  3421. DllRelease();
  3422. }
  3423. HRESULT CIntelliForms::CAutoSuggest::CEnumString::Init(IHTMLInputTextElement *pInputEle, CIntelliForms *pIntelliForms)
  3424. {
  3425. if (m_fInit || // Can only init once
  3426. !pInputEle || !pIntelliForms) // Need both pointers
  3427. {
  3428. return E_FAIL;
  3429. }
  3430. m_fInit=TRUE;
  3431. m_pIntelliForms = pIntelliForms;
  3432. // Take care of things that must be done on the main thread. Autocomplete will
  3433. // call us on a secondary thread to do the enumeration.
  3434. CIntelliForms::GetName(pInputEle, &m_bstrName);
  3435. if (m_bstrName && m_bstrName[0])
  3436. {
  3437. // See if this specifies the "vcard." format
  3438. if (IsEnabledInCPL() &&
  3439. !StrCmpNICW(m_bstrName, c_wszVCardPrefix, ARRAYSIZE(c_wszVCardPrefix)-1))
  3440. {
  3441. // It does. Retrieve string from the profile assistant store.
  3442. IHTMLWindow2 *pWin2 = NULL;
  3443. IServiceProvider *pQS = NULL;
  3444. // QS up to get the shdocvw IHTMLWindow2 instead of NF trident's
  3445. pInputEle->QueryInterface(IID_IServiceProvider, (void **)&pQS);
  3446. if (pQS)
  3447. {
  3448. pQS->QueryService(IID_IHTMLWindow2, IID_IHTMLWindow2, (void **)&pWin2);
  3449. pQS->Release();
  3450. }
  3451. if (pWin2)
  3452. {
  3453. IOmNavigator *pNav=NULL;
  3454. pWin2->get_navigator(&pNav);
  3455. if (pNav)
  3456. {
  3457. IHTMLOpsProfile *pProfile=NULL;
  3458. pNav->get_userProfile(&pProfile);
  3459. if (pProfile)
  3460. {
  3461. IOpsProfileSimple *pSimple=NULL;
  3462. pProfile->QueryInterface(IID_IOpsProfileSimple, (void **)&pSimple);
  3463. if (pSimple)
  3464. {
  3465. pSimple->ReadProperties(1, &m_bstrName, &m_pszOpsValue);
  3466. pSimple->Release();
  3467. }
  3468. pProfile->Release();
  3469. }
  3470. pNav->Release();
  3471. }
  3472. pWin2->Release();
  3473. }
  3474. }
  3475. }
  3476. return S_OK;
  3477. }
  3478. void CIntelliForms::CAutoSuggest::CEnumString::UnInit()
  3479. {
  3480. EnterCriticalSection(&m_crit);
  3481. m_pIntelliForms = NULL;
  3482. LeaveCriticalSection(&m_crit);
  3483. }
  3484. HRESULT CIntelliForms::CAutoSuggest::CEnumString::ResetEnum()
  3485. {
  3486. EnterCriticalSection(&m_crit);
  3487. if (m_pslMain)
  3488. {
  3489. delete m_pslMain;
  3490. m_pslMain = NULL;
  3491. }
  3492. m_fFilledStrings = FALSE;
  3493. LeaveCriticalSection(&m_crit);
  3494. return S_OK;
  3495. }
  3496. STDMETHODIMP CIntelliForms::CAutoSuggest::CEnumString::QueryInterface(REFIID riid, void **ppv)
  3497. {
  3498. *ppv = NULL;
  3499. if ((IID_IEnumString == riid) ||
  3500. (IID_IUnknown == riid))
  3501. {
  3502. *ppv = (IEnumString *)this;
  3503. }
  3504. if (NULL != *ppv)
  3505. {
  3506. ((IUnknown *)*ppv)->AddRef();
  3507. return S_OK;
  3508. }
  3509. return E_NOINTERFACE;
  3510. }
  3511. STDMETHODIMP_(ULONG) CIntelliForms::CAutoSuggest::CEnumString::AddRef(void)
  3512. {
  3513. return InterlockedIncrement(&m_cRef);
  3514. }
  3515. STDMETHODIMP_(ULONG) CIntelliForms::CAutoSuggest::CEnumString::Release(void)
  3516. {
  3517. if (InterlockedDecrement(&m_cRef) != 0)
  3518. {
  3519. return 1;
  3520. }
  3521. delete this;
  3522. return 0;
  3523. }
  3524. STDMETHODIMP CIntelliForms::CAutoSuggest::CEnumString::Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched)
  3525. {
  3526. EnterCriticalSection(&m_crit);
  3527. if (!m_fFilledStrings)
  3528. {
  3529. FillEnumerator();
  3530. }
  3531. if (m_pslMain)
  3532. {
  3533. int iNewPtr = m_iPtr + celt;
  3534. if (iNewPtr > m_pslMain->NumStrings())
  3535. {
  3536. iNewPtr = m_pslMain->NumStrings();
  3537. }
  3538. *pceltFetched = iNewPtr - m_iPtr;
  3539. LPOLESTR lpstr;
  3540. for (; m_iPtr < iNewPtr; m_iPtr ++)
  3541. {
  3542. m_pslMain->GetTaskAllocString(m_iPtr, &lpstr);
  3543. if (!lpstr) break;
  3544. *(rgelt ++) = lpstr;
  3545. }
  3546. if (m_iPtr < iNewPtr)
  3547. {
  3548. *pceltFetched += (m_iPtr - iNewPtr);
  3549. }
  3550. }
  3551. LeaveCriticalSection(&m_crit);
  3552. if (!m_pslMain)
  3553. {
  3554. return E_FAIL;
  3555. }
  3556. return (*pceltFetched) ? S_OK : S_FALSE;
  3557. }
  3558. STDMETHODIMP CIntelliForms::CAutoSuggest::CEnumString::Reset()
  3559. {
  3560. EnterCriticalSection(&m_crit);
  3561. m_iPtr = 0;
  3562. LeaveCriticalSection(&m_crit);
  3563. return S_OK;
  3564. }
  3565. HRESULT CIntelliForms::CAutoSuggest::CEnumString::FillEnumerator()
  3566. {
  3567. // Already in critical section
  3568. ASSERT(!m_pslMain);
  3569. if (m_fFilledStrings)
  3570. {
  3571. return S_FALSE;
  3572. }
  3573. if (!m_bstrName || !m_bstrName[0] || !m_pIntelliForms)
  3574. {
  3575. return E_FAIL;
  3576. }
  3577. m_fFilledStrings = TRUE;
  3578. m_iPtr = 0;
  3579. // Fill the enumerator based on our name
  3580. TraceMsg(TF_IFORMS, "IForms: Intelliforms filling enumerator");
  3581. // Open any previously saved strings
  3582. if (!m_pIntelliForms->IsRestricted() &&
  3583. IsEnabledInCPL() &&
  3584. m_pIntelliForms->IsEnabledForPage())
  3585. {
  3586. m_pIntelliForms->ReadFromStore(m_bstrName, &m_pslMain);
  3587. // Add in profile assistant value, if any
  3588. if (m_pszOpsValue && m_pszOpsValue[0])
  3589. {
  3590. if (!m_pslMain)
  3591. {
  3592. CStringList_New(&m_pslMain);
  3593. }
  3594. else
  3595. {
  3596. // don't risk a scavenge (perf)
  3597. m_pslMain->SetMaxStrings(CStringList::MAX_STRINGS+4);
  3598. }
  3599. if (m_pslMain)
  3600. {
  3601. m_pslMain->AddString(m_pszOpsValue);
  3602. }
  3603. }
  3604. }
  3605. // Next fill with any usernames that have saved passwords
  3606. CStringList *pslPasswords;
  3607. if (!m_pIntelliForms->IsRestrictedPW() &&
  3608. CIntelliForms::IsEnabledRestorePW() &&
  3609. SUCCEEDED(m_pIntelliForms->GetPasswordStringList(&pslPasswords)))
  3610. {
  3611. ASSERT(!(pslPasswords->NumStrings() & 1));
  3612. FILETIME ft;
  3613. if (pslPasswords->NumStrings() > 0)
  3614. {
  3615. if (!m_pslMain)
  3616. {
  3617. CStringList_New(&m_pslMain);
  3618. }
  3619. else
  3620. {
  3621. // avoid expensive scavenging while adding usernames to string list
  3622. m_pslMain->SetMaxStrings(m_pslMain->GetMaxStrings() + pslPasswords->NumStrings()/2);
  3623. }
  3624. if (m_pslMain)
  3625. {
  3626. for (int i=0; i<pslPasswords->NumStrings(); i+=2)
  3627. {
  3628. if (SUCCEEDED(pslPasswords->GetStringTime(i, &ft)) &&
  3629. FILETIME_TO_INT(ft) != 0)
  3630. {
  3631. // We have a saved password for this username. Add username to enumerator.
  3632. m_pslMain->AddString(pslPasswords->GetString(i));
  3633. }
  3634. }
  3635. }
  3636. }
  3637. // do not delete pslPasswords
  3638. }
  3639. return (m_pslMain) ? ((m_pslMain->NumStrings()) ? S_OK : S_FALSE) : E_FAIL;
  3640. }
  3641. // Static helper. Pretty basic.
  3642. HRESULT CStringList_New(CStringList **ppNew, BOOL fAutoDelete/*=TRUE*/)
  3643. {
  3644. *ppNew = new CStringList();
  3645. if (*ppNew)
  3646. {
  3647. (*ppNew)->SetAutoScavenge(fAutoDelete);
  3648. }
  3649. return (*ppNew) ? S_OK : E_OUTOFMEMORY;
  3650. }
  3651. CStringList::CStringList()
  3652. {
  3653. TraceMsg(TF_IFORMS, "IForms: CStringList::CStringList");
  3654. m_fAutoScavenge = TRUE;
  3655. m_dwMaxStrings = MAX_STRINGS;
  3656. }
  3657. CStringList::~CStringList()
  3658. {
  3659. TraceMsg(TF_IFORMS, "IForms: CStringList::~CStringList");
  3660. CleanUp();
  3661. }
  3662. void CStringList::CleanUp()
  3663. {
  3664. if (m_psiIndex)
  3665. {
  3666. LocalFree(m_psiIndex);
  3667. m_psiIndex = NULL;
  3668. }
  3669. if (m_pBuffer)
  3670. {
  3671. LocalFree(m_pBuffer);
  3672. m_pBuffer = NULL;
  3673. }
  3674. m_dwIndexSize = 0;
  3675. m_dwBufEnd = m_dwBufSize = 0;
  3676. }
  3677. HRESULT CStringList::WriteToBlobs(LPBYTE *ppBlob1, DWORD *pcbBlob1, LPBYTE *ppBlob2, DWORD *pcbBlob2)
  3678. {
  3679. HRESULT hr = E_FAIL;
  3680. TraceMsg(TF_IFORMS, "+WriteToBlobs");
  3681. if (SUCCEEDED(Validate()))
  3682. {
  3683. DWORD dwIndexSize;
  3684. dwIndexSize = INDEX_SIZE(m_psiIndex->dwNumStrings);
  3685. ASSERT(dwIndexSize <= m_dwIndexSize);
  3686. *ppBlob1 = (LPBYTE) LocalAlloc(LMEM_FIXED, dwIndexSize);
  3687. if (*ppBlob1)
  3688. {
  3689. *ppBlob2 = (LPBYTE) LocalAlloc(LMEM_FIXED, m_dwBufEnd);
  3690. if (*ppBlob2)
  3691. {
  3692. memcpy(*ppBlob1, m_psiIndex, dwIndexSize);
  3693. *pcbBlob1=dwIndexSize;
  3694. memcpy(*ppBlob2, m_pBuffer, m_dwBufEnd);
  3695. *pcbBlob2=m_dwBufEnd;
  3696. hr = S_OK;
  3697. }
  3698. }
  3699. }
  3700. else
  3701. {
  3702. // Validate failed.
  3703. TraceMsg(TF_ERROR | TF_IFORMS, "Validate FAILED in WriteToBlobs");
  3704. *ppBlob1=NULL;
  3705. *ppBlob2=NULL;
  3706. }
  3707. if (FAILED(hr))
  3708. {
  3709. if (*ppBlob1)
  3710. {
  3711. LocalFree(*ppBlob1);
  3712. *ppBlob1=NULL;
  3713. }
  3714. if (*ppBlob2)
  3715. {
  3716. LocalFree(*ppBlob2);
  3717. *ppBlob2=NULL;
  3718. }
  3719. *pcbBlob1=0;
  3720. *pcbBlob2=0;
  3721. }
  3722. TraceMsg(TF_IFORMS, "-WriteToBlobs");
  3723. return hr;
  3724. }
  3725. // Take the blobs and use as our buffer
  3726. HRESULT CStringList::ReadFromBlobs(LPBYTE *ppBlob1, DWORD cbBlob1, LPBYTE *ppBlob2, DWORD cbBlob2)
  3727. {
  3728. HRESULT hr = E_FAIL;
  3729. TraceMsg(TF_IFORMS, "+ReadFromBlobs");
  3730. if (m_psiIndex)
  3731. {
  3732. TraceMsg(TF_IFORMS, "IForms: CStringList::ReadFromRegistry called with initialized instance.");
  3733. CleanUp();
  3734. }
  3735. // Allocate our buffers.
  3736. m_psiIndex = (StringIndex *) (*ppBlob1);
  3737. m_pBuffer = (LPBYTE) (*ppBlob2);
  3738. *ppBlob1 = NULL;
  3739. *ppBlob2 = NULL;
  3740. if (!m_psiIndex || !m_pBuffer || !cbBlob1 || !cbBlob2)
  3741. {
  3742. // Nothing to do
  3743. CleanUp();
  3744. return S_FALSE;
  3745. }
  3746. // Validate our string index.
  3747. if ((m_psiIndex->dwSignature == INDEX_SIGNATURE) &&
  3748. (m_psiIndex->cbSize == STRINGINDEX_CBSIZE) &&
  3749. (m_psiIndex->dwNumStrings <= MAX_STRINGS))
  3750. {
  3751. m_dwBufEnd = m_dwBufSize = cbBlob2;
  3752. m_dwIndexSize = cbBlob1;
  3753. if (SUCCEEDED(Validate()))
  3754. {
  3755. // Everything worked. Amazing.
  3756. hr = S_OK;
  3757. }
  3758. }
  3759. if (FAILED(hr))
  3760. {
  3761. // Release buffers if necessary.
  3762. CleanUp();
  3763. }
  3764. TraceMsg(TF_IFORMS, "-ReadFromBlobs");
  3765. return hr;
  3766. }
  3767. // static
  3768. HRESULT CStringList::GetFlagsFromIndex(LPBYTE pBlob1, INT64 *piFlags)
  3769. {
  3770. StringIndex *psiIndex = (StringIndex *)pBlob1;
  3771. if ((psiIndex->dwSignature == INDEX_SIGNATURE) &&
  3772. (psiIndex->cbSize == STRINGINDEX_CBSIZE))
  3773. {
  3774. *piFlags = psiIndex->iData;
  3775. return S_OK;
  3776. }
  3777. return E_FAIL;
  3778. }
  3779. HRESULT CStringList::Validate()
  3780. {
  3781. TraceMsg(TF_IFORMS, "+CStringList::Validate");
  3782. if (!m_psiIndex || !m_pBuffer)
  3783. {
  3784. return E_FAIL;
  3785. }
  3786. for (DWORD dw=0; dw < m_psiIndex->dwNumStrings; dw++)
  3787. {
  3788. DWORD dwPtr = m_psiIndex->StringEntry[dw].dwStringPtr;
  3789. DWORD dwSize = (GetStringLen(dw)+1) * sizeof(WCHAR);
  3790. if (dwPtr + dwSize > m_dwBufSize)
  3791. {
  3792. return E_FAIL;
  3793. }
  3794. }
  3795. TraceMsg(TF_IFORMS, "-CStringList::Validate");
  3796. return S_OK;
  3797. }
  3798. HRESULT CStringList::Init(DWORD dwBufSize /* =0 */)
  3799. {
  3800. DWORD dwMaxStrings=0;
  3801. DWORD dwIndexSize=0;
  3802. if (m_psiIndex)
  3803. {
  3804. TraceMsg(TF_IFORMS, "IForms: CStringList::Init called when already initialized");
  3805. CleanUp();
  3806. }
  3807. if (dwBufSize == 0)
  3808. {
  3809. dwBufSize = INIT_BUF_SIZE;
  3810. }
  3811. dwMaxStrings = dwBufSize >> 5; // this is relatively arbitrary but doesn't matter much
  3812. if (dwMaxStrings == 0)
  3813. dwMaxStrings = 1;
  3814. dwIndexSize = INDEX_SIZE(dwMaxStrings);
  3815. m_pBuffer = (LPBYTE)LocalAlloc(LMEM_FIXED, dwBufSize);
  3816. m_psiIndex = (StringIndex *)LocalAlloc(LMEM_FIXED, dwIndexSize);
  3817. if ((NULL == m_psiIndex) ||
  3818. (NULL == m_pBuffer))
  3819. {
  3820. TraceMsg(TF_IFORMS, "IForms: CStringList::Init memory allocation failed");
  3821. CleanUp();
  3822. return E_OUTOFMEMORY;
  3823. }
  3824. *((WCHAR *)m_pBuffer) = L'\0';
  3825. m_dwBufSize = dwBufSize;
  3826. m_dwBufEnd = 0;
  3827. m_psiIndex->dwSignature = INDEX_SIGNATURE;
  3828. m_psiIndex->cbSize = STRINGINDEX_CBSIZE;
  3829. m_psiIndex->dwNumStrings = 0;
  3830. m_psiIndex->iData = 0;
  3831. m_dwIndexSize = dwIndexSize;
  3832. TraceMsg(TF_IFORMS, "IForms: CStringList::Init succeeded");
  3833. return S_OK;
  3834. }
  3835. HRESULT CStringList::GetBSTR(int iIndex, BSTR *pbstrRet)
  3836. {
  3837. LPCWSTR lpwstr = GetString(iIndex);
  3838. if (!lpwstr)
  3839. {
  3840. *pbstrRet = NULL;
  3841. return E_INVALIDARG;
  3842. }
  3843. *pbstrRet = SysAllocString(lpwstr);
  3844. return (*pbstrRet) ? S_OK : E_OUTOFMEMORY;
  3845. }
  3846. HRESULT CStringList::GetTaskAllocString(int iIndex, LPOLESTR *pRet)
  3847. {
  3848. LPCWSTR lpwstr = GetString(iIndex);
  3849. if (!lpwstr)
  3850. {
  3851. *pRet = NULL;
  3852. return E_INVALIDARG;
  3853. }
  3854. DWORD dwSize = (GetStringLen(iIndex)+1) * sizeof(WCHAR);
  3855. *pRet = (LPOLESTR)CoTaskMemAlloc(dwSize);
  3856. if (!*pRet)
  3857. {
  3858. return E_OUTOFMEMORY;
  3859. }
  3860. memcpy(*pRet, lpwstr, dwSize);
  3861. return S_OK;
  3862. }
  3863. HRESULT CStringList::FindString(LPCWSTR lpwstr, int iLen, int *piNum, BOOL fCaseSensitive)
  3864. {
  3865. if (!m_psiIndex) return E_FAIL;
  3866. DWORD dw;
  3867. if (!lpwstr)
  3868. {
  3869. return E_INVALIDARG;
  3870. }
  3871. if (iLen <= 0)
  3872. {
  3873. iLen = lstrlenW(lpwstr);
  3874. }
  3875. if (piNum)
  3876. {
  3877. *piNum = -1;
  3878. }
  3879. for (dw=0; dw<m_psiIndex->dwNumStrings; dw++)
  3880. {
  3881. if (m_psiIndex->StringEntry[dw].dwStringLen == (DWORD)iLen)
  3882. {
  3883. if ((fCaseSensitive && (!StrCmpW(GetString(dw), lpwstr))) ||
  3884. (!fCaseSensitive && (!StrCmpIW(GetString(dw), lpwstr))))
  3885. {
  3886. // Match!
  3887. if (piNum)
  3888. {
  3889. *piNum = (int) dw;
  3890. }
  3891. return S_OK;
  3892. }
  3893. }
  3894. }
  3895. return E_FAIL; // Couldn't find it
  3896. }
  3897. // CStringList is not optimized for deleting
  3898. HRESULT CStringList::DeleteString(int iIndex)
  3899. {
  3900. TraceMsg(TF_IFORMS, "+DeleteString");
  3901. if (!m_psiIndex)
  3902. {
  3903. return E_FAIL;
  3904. }
  3905. if ((iIndex<0) || ((DWORD)iIndex >= m_psiIndex->dwNumStrings))
  3906. {
  3907. return E_INVALIDARG;
  3908. }
  3909. if ((DWORD)iIndex == (m_psiIndex->dwNumStrings-1))
  3910. {
  3911. // Simple case - deleting last string
  3912. m_dwBufEnd -= (sizeof(WCHAR) * (GetStringLen(iIndex) + 1));
  3913. m_psiIndex->dwNumStrings --;
  3914. return S_OK;
  3915. }
  3916. DWORD cbSizeDeleted;
  3917. LPCWSTR pwszString1, pwszString2;
  3918. pwszString1 = GetString(iIndex);
  3919. pwszString2 = GetString(iIndex+1);
  3920. // Size in bytes of string to be deleted including null terminator
  3921. cbSizeDeleted = (DWORD)((DWORD_PTR)pwszString2 - (DWORD_PTR)pwszString1);
  3922. ASSERT(cbSizeDeleted == (sizeof(WCHAR) * (lstrlenW(GetString(iIndex))+1)));
  3923. // Delete entry in index
  3924. memcpy(&(m_psiIndex->StringEntry[iIndex]), &(m_psiIndex->StringEntry[iIndex+1]),
  3925. STRINGENTRY_SIZE*(m_psiIndex->dwNumStrings - iIndex - 1));
  3926. m_psiIndex->dwNumStrings --;
  3927. // Delete string in buffer
  3928. memcpy((LPWSTR)pwszString1, pwszString2, m_dwBufEnd-(int)PtrDiff(pwszString2, m_pBuffer));
  3929. m_dwBufEnd -= cbSizeDeleted;
  3930. // Fix up pointers in index
  3931. for (int i=iIndex; (DWORD)i < m_psiIndex->dwNumStrings; i++)
  3932. {
  3933. m_psiIndex->StringEntry[i].dwStringPtr -= cbSizeDeleted;
  3934. }
  3935. TraceMsg(TF_IFORMS, "-DeleteString");
  3936. return S_OK;
  3937. }
  3938. HRESULT CStringList::InsertString(int iIndex, LPCWSTR lpwstr)
  3939. {
  3940. TraceMsg(TF_IFORMS, "+InsertString");
  3941. if (!m_psiIndex)
  3942. {
  3943. return E_FAIL;
  3944. }
  3945. if ((iIndex<0) || ((DWORD)iIndex > m_psiIndex->dwNumStrings))
  3946. {
  3947. return E_INVALIDARG;
  3948. }
  3949. if ((DWORD)iIndex == m_psiIndex->dwNumStrings)
  3950. {
  3951. // Simple case - inserting to end
  3952. return _AddString(lpwstr, FALSE, NULL);
  3953. }
  3954. DWORD dwLen = (DWORD)lstrlenW(lpwstr);
  3955. DWORD dwSizeInserted = sizeof(WCHAR) * (dwLen + 1);
  3956. if (FAILED(EnsureBuffer(m_dwBufEnd + dwSizeInserted)) ||
  3957. FAILED(EnsureIndex(m_psiIndex->dwNumStrings + 1)))
  3958. {
  3959. return E_OUTOFMEMORY;
  3960. }
  3961. // Insert into buffer
  3962. LPWSTR pwszBufLoc = GetStringPtr(iIndex);
  3963. memcpy((LPBYTE)pwszBufLoc + dwSizeInserted, pwszBufLoc, m_dwBufEnd - (int) PtrDiff(pwszBufLoc, m_pBuffer));
  3964. memcpy(pwszBufLoc, lpwstr, dwSizeInserted);
  3965. m_dwBufEnd += dwSizeInserted;
  3966. // Insert into index
  3967. memcpy(&(m_psiIndex->StringEntry[iIndex+1]), &(m_psiIndex->StringEntry[iIndex]),
  3968. STRINGENTRY_SIZE*(m_psiIndex->dwNumStrings - iIndex));
  3969. struct StringIndex::tagStringEntry *pse=&(m_psiIndex->StringEntry[iIndex]);
  3970. pse->dwStringPtr = (DWORD)PtrDiff(pwszBufLoc, m_pBuffer);
  3971. pse->ftLastSubmitted.dwLowDateTime = pse->ftLastSubmitted.dwHighDateTime = 0;
  3972. pse->dwStringLen = dwLen;
  3973. m_psiIndex->dwNumStrings ++;
  3974. // Fix up pointers after inserted string
  3975. for (int i=iIndex+1; (DWORD)i<m_psiIndex->dwNumStrings; i++)
  3976. {
  3977. m_psiIndex->StringEntry[i].dwStringPtr += dwSizeInserted;
  3978. }
  3979. TraceMsg(TF_IFORMS, "-InsertString");
  3980. return S_OK;
  3981. }
  3982. HRESULT CStringList::ReplaceString(int iIndex, LPCWSTR lpwstr)
  3983. {
  3984. TraceMsg(TF_IFORMS, "+ReplaceString");
  3985. if (!m_psiIndex)
  3986. {
  3987. return E_FAIL;
  3988. }
  3989. if ((iIndex<0) || ((DWORD)iIndex >= m_psiIndex->dwNumStrings))
  3990. {
  3991. return E_INVALIDARG;
  3992. }
  3993. if ((DWORD)lstrlenW(lpwstr) == m_psiIndex->StringEntry[iIndex].dwStringLen)
  3994. {
  3995. // Simple case - strings equal length
  3996. memcpy( GetStringPtr(iIndex),
  3997. lpwstr,
  3998. (m_psiIndex->StringEntry[iIndex].dwStringLen)*sizeof(WCHAR));
  3999. return S_OK;
  4000. }
  4001. // Delete old string, then insert new one
  4002. DeleteString(iIndex);
  4003. HRESULT hr = InsertString(iIndex, lpwstr);
  4004. TraceMsg(TF_IFORMS, "-ReplaceString");
  4005. return hr;
  4006. }
  4007. HRESULT CStringList::AddString(LPCWSTR lpwstr, FILETIME ft, int *piNum /*=NULL*/)
  4008. {
  4009. int iNum;
  4010. HRESULT hr;
  4011. TraceMsg(TF_IFORMS, "+AddString");
  4012. hr = _AddString(lpwstr, TRUE, &iNum);
  4013. if (piNum)
  4014. {
  4015. *piNum = iNum;
  4016. }
  4017. if (SUCCEEDED(hr))
  4018. {
  4019. UpdateStringTime(iNum, ft);
  4020. }
  4021. TraceMsg(TF_IFORMS, "-AddString");
  4022. return hr;
  4023. }
  4024. HRESULT CStringList::AddString(LPCWSTR lpwstr, int *piNum /*=NULL*/)
  4025. {
  4026. return _AddString(lpwstr, TRUE, piNum);
  4027. }
  4028. HRESULT CStringList::AppendString(LPCWSTR lpwstr, int *piNum /*=NULL*/)
  4029. {
  4030. return _AddString(lpwstr, FALSE, piNum);
  4031. }
  4032. HRESULT CStringList::AppendString(LPCWSTR lpwstr, FILETIME ft, int *piNum /*=NULL*/)
  4033. {
  4034. int iNum;
  4035. HRESULT hr;
  4036. hr = _AddString(lpwstr, FALSE, &iNum);
  4037. if (piNum)
  4038. {
  4039. *piNum = iNum;
  4040. }
  4041. if (SUCCEEDED(hr))
  4042. {
  4043. SetStringTime(iNum, ft);
  4044. }
  4045. return hr;
  4046. }
  4047. HRESULT CStringList::_AddString(LPCWSTR lpwstr, BOOL fCheckDuplicates, int *piNum)
  4048. {
  4049. DWORD dwSize, dwLen;
  4050. int iNum = -1;
  4051. WCHAR wchBufTruncated[MAX_URL_STRING];
  4052. LPCWSTR lpwstrTruncated=lpwstr;
  4053. TraceMsg(TF_IFORMS, "+_AddString");
  4054. if (piNum)
  4055. {
  4056. *piNum = -1;
  4057. }
  4058. if (!lpwstr)
  4059. {
  4060. return E_INVALIDARG;
  4061. }
  4062. if (!m_psiIndex)
  4063. {
  4064. if (FAILED(Init()))
  4065. {
  4066. return E_FAIL;
  4067. }
  4068. }
  4069. dwLen = (DWORD) lstrlenW(lpwstr);
  4070. // Explicitly truncate strings to MAX_URL characters. If we don't do this, browseui
  4071. // autocomplete code truncates it anyway and then we have problems removing
  4072. // duplicates and deleting these long strings. All IntelliForms code can handle
  4073. // arbitrary length strings.
  4074. if (dwLen >= ARRAYSIZE(wchBufTruncated))
  4075. {
  4076. StrCpyNW(wchBufTruncated, lpwstr, ARRAYSIZE(wchBufTruncated));
  4077. lpwstrTruncated = wchBufTruncated;
  4078. dwLen = lstrlenW(wchBufTruncated);
  4079. }
  4080. dwSize = (dwLen+1)*sizeof(WCHAR);
  4081. if (fCheckDuplicates && SUCCEEDED(FindString(lpwstrTruncated, (int)dwLen, &iNum, FALSE)))
  4082. {
  4083. if (piNum)
  4084. {
  4085. *piNum = iNum;
  4086. }
  4087. if (!StrCmpW(lpwstrTruncated, GetString(iNum)))
  4088. {
  4089. return S_FALSE; // String is an exact duplicate
  4090. }
  4091. // String is a duplicate but has different case. Replace.
  4092. ASSERT(m_psiIndex->StringEntry[iNum].dwStringLen == dwLen);
  4093. memcpy(GetStringPtr(iNum), lpwstrTruncated, dwSize);
  4094. return S_OK; // String was different in case
  4095. }
  4096. if (m_psiIndex->dwNumStrings >= m_dwMaxStrings)
  4097. {
  4098. if (m_fAutoScavenge)
  4099. {
  4100. // Remove the oldest string from our list.
  4101. DWORD dwIndex;
  4102. int iOldest=-1;
  4103. FILETIME ftOldest = { 0xFFFFFFFF, 0x7FFFFFFF };
  4104. for (dwIndex=0; dwIndex<m_psiIndex->dwNumStrings; dwIndex++)
  4105. {
  4106. if ((FILETIME_TO_INT(m_psiIndex->StringEntry[dwIndex].ftLastSubmitted) != 0) &&
  4107. (1 == CompareFileTime(&ftOldest, &m_psiIndex->StringEntry[dwIndex].ftLastSubmitted)))
  4108. {
  4109. ftOldest = m_psiIndex->StringEntry[dwIndex].ftLastSubmitted;
  4110. iOldest = (int)dwIndex;
  4111. }
  4112. }
  4113. if (iOldest != -1)
  4114. {
  4115. DeleteString(iOldest);
  4116. }
  4117. else
  4118. {
  4119. // User must not be setting string times.
  4120. return E_OUTOFMEMORY;
  4121. }
  4122. }
  4123. else
  4124. {
  4125. // Auto-scavenge is disabled.
  4126. return E_OUTOFMEMORY;
  4127. }
  4128. }
  4129. if (FAILED(EnsureBuffer(m_dwBufEnd + dwSize)) ||
  4130. FAILED(EnsureIndex(m_psiIndex->dwNumStrings + 1)))
  4131. {
  4132. return E_OUTOFMEMORY;
  4133. }
  4134. // Our buffers are large enough. Do it.
  4135. if (piNum)
  4136. {
  4137. *piNum = (int) m_psiIndex->dwNumStrings;
  4138. }
  4139. LPWSTR pwszNewString = (LPWSTR)(m_pBuffer + m_dwBufEnd);
  4140. memcpy(pwszNewString, lpwstrTruncated, dwSize);
  4141. m_dwBufEnd += dwSize;
  4142. struct StringIndex::tagStringEntry *pse=&(m_psiIndex->StringEntry[m_psiIndex->dwNumStrings]);
  4143. pse->dwStringPtr = (DWORD)PtrDiff(pwszNewString, m_pBuffer);
  4144. pse->ftLastSubmitted.dwLowDateTime = pse->ftLastSubmitted.dwHighDateTime = 0;
  4145. pse->dwStringLen = dwLen;
  4146. m_psiIndex->dwNumStrings ++;
  4147. TraceMsg(TF_IFORMS, "-_AddString");
  4148. return S_OK; // We added a new string
  4149. }
  4150. HRESULT CStringList::EnsureBuffer(DWORD dwSizeNeeded)
  4151. {
  4152. TraceMsg(TF_IFORMS, "+EnsureBuffer");
  4153. if (dwSizeNeeded <= m_dwBufSize)
  4154. {
  4155. return S_OK; // Already big enough
  4156. }
  4157. if (!m_pBuffer)
  4158. {
  4159. return E_FAIL;
  4160. }
  4161. DWORD dwNewBufSize = m_dwBufSize * 2;
  4162. // Grow buffer.
  4163. if (dwSizeNeeded > dwNewBufSize)
  4164. {
  4165. TraceMsg(TF_IFORMS, "IForms: StringList special growing size (big string)");
  4166. dwNewBufSize = dwSizeNeeded;
  4167. }
  4168. TraceMsg(TF_IFORMS, "IForms: CStringList growing");
  4169. LPBYTE pBuf = (LPBYTE)LocalReAlloc(m_pBuffer, dwNewBufSize, LMEM_MOVEABLE);
  4170. if (!pBuf)
  4171. {
  4172. TraceMsg(TF_IFORMS, "IForms: CStringList: ReAlloc failure");
  4173. // Realloc failure: our old memory is still present
  4174. return E_FAIL;
  4175. }
  4176. m_dwBufSize = dwNewBufSize;
  4177. m_pBuffer = pBuf;
  4178. TraceMsg(TF_IFORMS, "-EnsureBuffer");
  4179. // Successfully realloced to bigger buffer
  4180. return S_OK;
  4181. }
  4182. // grow psiIndex if needed
  4183. HRESULT CStringList::EnsureIndex(DWORD dwNumStringsNeeded)
  4184. {
  4185. TraceMsg(TF_IFORMS, "+EnsureIndex");
  4186. if (!m_psiIndex)
  4187. {
  4188. return E_FAIL;
  4189. }
  4190. if (INDEX_SIZE(dwNumStringsNeeded) > m_dwIndexSize)
  4191. {
  4192. DWORD dwNewMaxStrings = (m_psiIndex->dwNumStrings) * 2;
  4193. DWORD dwNewIndexSize = INDEX_SIZE(dwNewMaxStrings);
  4194. TraceMsg(TF_IFORMS, "IForms: CStringList growing max strings");
  4195. StringIndex *psiBuf =
  4196. (StringIndex *)LocalReAlloc(m_psiIndex, dwNewIndexSize, LMEM_MOVEABLE);
  4197. if (!psiBuf)
  4198. {
  4199. // Realloc failure: Old memory still present
  4200. TraceMsg(TF_IFORMS, "IForms: CStringList ReAlloc failure");
  4201. return E_OUTOFMEMORY;
  4202. }
  4203. // Success. Don't need to fix any pointers in index (buffer is unchanged)
  4204. m_psiIndex = psiBuf;
  4205. m_dwIndexSize = dwNewIndexSize;
  4206. }
  4207. TraceMsg(TF_IFORMS, "-EnsureIndex");
  4208. return S_OK;
  4209. }
  4210. // This dlg proc is used for password save, change, delete dialogs
  4211. INT_PTR AutoSuggestDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
  4212. {
  4213. switch (msg)
  4214. {
  4215. case WM_INITDIALOG:
  4216. CenterWindow(hDlg, GetParent(hDlg));
  4217. SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR) lParam);
  4218. if (lParam == IDD_AUTOSUGGEST_SAVEPASSWORD)
  4219. {
  4220. // For "Save" password we default to no. For "Change" and "Delete" we default to yes.
  4221. SetFocus(GetDlgItem(hDlg, IDNO));
  4222. return FALSE;
  4223. }
  4224. return TRUE;
  4225. case WM_COMMAND:
  4226. switch (GET_WM_COMMAND_ID(wParam, lParam))
  4227. {
  4228. case IDCANCEL: // close box
  4229. case IDYES: // yes button
  4230. case IDNO: // no button
  4231. if (IDD_AUTOSUGGEST_SAVEPASSWORD == GetWindowLongPtr(hDlg, DWLP_USER))
  4232. {
  4233. // Check the "don't ask me again" checkbox for the save password dlg
  4234. if (IsDlgButtonChecked(hDlg, IDC_AUTOSUGGEST_NEVER))
  4235. {
  4236. SHSetValue(HKEY_CURRENT_USER, c_szRegKeySMIEM, c_szRegValAskPasswords,
  4237. REG_SZ, c_szNo, sizeof(c_szNo));
  4238. }
  4239. }
  4240. EndDialog(hDlg, LOWORD(wParam));
  4241. return TRUE;
  4242. }
  4243. break;
  4244. #ifdef CHECKBOX_HELP
  4245. case WM_HELP:
  4246. // Only process WM_HELP for save password dlg
  4247. if (IDD_AUTOSUGGEST_SAVEPASSWORD == GetWindowLong(hDlg, DWL_USER))
  4248. {
  4249. SHWinHelpOnDemandWrap((HWND) ((LPHELPINFO) lParam)->hItemHandle, c_szHelpFile,
  4250. HELP_WM_HELP, (DWORD_PTR)(LPTSTR) c_aIFormsHelpIds);
  4251. }
  4252. break;
  4253. case WM_CONTEXTMENU: // right mouse click
  4254. // Only process WM_HELP for save password dlg
  4255. if (IDD_AUTOSUGGEST_SAVEPASSWORD == GetWindowLong(hDlg, DWL_USER))
  4256. {
  4257. SHWinHelpOnDemandWrap((HWND) wParam, c_szHelpFile, HELP_CONTEXTMENU,
  4258. (DWORD_PTR)(LPTSTR) c_aIFormsHelpIds);
  4259. }
  4260. break;
  4261. #endif
  4262. }
  4263. return FALSE;
  4264. }
  4265. //================================================================================
  4266. INT_PTR CALLBACK AskUserDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  4267. {
  4268. switch (uMsg)
  4269. {
  4270. case WM_INITDIALOG:
  4271. CenterWindow(hDlg, GetParent(hDlg));
  4272. Animate_OpenEx(GetDlgItem(hDlg, IDD_ANIMATE), HINST_THISDLL, MAKEINTRESOURCE(IDA_AUTOSUGGEST));
  4273. return TRUE;
  4274. case WM_COMMAND:
  4275. {
  4276. switch (LOWORD(wParam))
  4277. {
  4278. case IDC_AUTOSUGGEST_HELP:
  4279. SHHtmlHelpOnDemandWrap(GetParent(hDlg), TEXT("iexplore.chm > iedefault"),
  4280. HH_DISPLAY_TOPIC, (DWORD_PTR) TEXT("autocomp.htm"), ML_CROSSCODEPAGE);
  4281. break;
  4282. case IDYES:
  4283. case IDNO:
  4284. {
  4285. LPCTSTR pszData;
  4286. DWORD cbData;
  4287. DWORD dwData=0;
  4288. if (LOWORD(wParam) == IDYES)
  4289. {
  4290. pszData = c_szYes;
  4291. cbData = sizeof(c_szYes);
  4292. }
  4293. else
  4294. {
  4295. pszData = c_szNo;
  4296. cbData = sizeof(c_szNo);
  4297. }
  4298. // Write the enabled state into our CPL regkey
  4299. SHSetValue(HKEY_CURRENT_USER, c_szRegKeySMIEM, c_szRegValUseFormSuggest,
  4300. REG_SZ, pszData, cbData);
  4301. // Flag it as "asked user" so we don't ask them again
  4302. SHSetValue(HKEY_CURRENT_USER, c_szRegKeyIntelliForms, c_szRegValAskUser,
  4303. REG_DWORD, &dwData, sizeof(dwData));
  4304. }
  4305. // Fall through
  4306. case IDCANCEL:
  4307. {
  4308. EndDialog(hDlg, LOWORD(wParam));
  4309. }
  4310. break;
  4311. }
  4312. }
  4313. return TRUE;
  4314. case WM_DESTROY:
  4315. break;
  4316. }
  4317. return FALSE;
  4318. }