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.

2781 lines
82 KiB

  1. /*****************************************************************************\
  2. FILE: util.cpp
  3. DESCRIPTION:
  4. Shared stuff that operates on all classes.
  5. \*****************************************************************************/
  6. #include "priv.h"
  7. #include "util.h"
  8. #include "ftpurl.h"
  9. #include "view.h"
  10. #include "statusbr.h"
  11. #include <commctrl.h>
  12. #include <shdocvw.h>
  13. #define CPP_FUNCTIONS
  14. #include <crtfree.h>
  15. HINSTANCE g_hinst; /* My instance handle */
  16. CHAR g_szShell32[MAX_PATH]; /* Full path to shell32.dll (must be ANSI) */
  17. #ifdef DEBUG
  18. DWORD g_TLSliStopWatchStartHi = 0;
  19. DWORD g_TLSliStopWatchStartLo = 0;
  20. LARGE_INTEGER g_liStopWatchFreq = {0};
  21. #endif // DEBUG
  22. // Shell32.dll v3 (original Win95/WinNT) has so many bugs when it receives
  23. // an IDataObject with FILEGROUPDESCRIPTOR that it doesn't make sense to allow
  24. // users to drag from FTP with FILEGROUPDESCRIPTOR on these early shell machines.
  25. // This #define turns this on off.
  26. //#define BROWSERONLY_DRAGGING 1
  27. const VARIANT c_vaEmpty = {0};
  28. #define PVAREMPTY ((VARIANT*)&c_vaEmpty)
  29. //////////////////////////// IE 5 vs IE 4 /////////////////////////////////
  30. // These are functions that IE5 exposes (normally in shlwapi), but
  31. // if we want to be compatible with IE4, we need to have our own copy.s
  32. // If we turn on USE_IE5_UTILS, we won't work with IE4's DLLs (like shlwapi).
  33. //
  34. #ifndef USE_IE5_UTILS
  35. void UnicWrapper_IUnknown_Set(IUnknown ** ppunk, IUnknown * punk)
  36. {
  37. ENTERCRITICAL;
  38. if (*ppunk)
  39. (*ppunk)->Release();
  40. *ppunk = punk;
  41. if (punk)
  42. punk->AddRef();
  43. LEAVECRITICAL;
  44. }
  45. void UnicWrapper_IUnknown_AtomicRelease(void ** ppunk)
  46. {
  47. if (ppunk && *ppunk) {
  48. IUnknown* punk = *(IUnknown**)ppunk;
  49. *ppunk = NULL;
  50. punk->Release();
  51. }
  52. }
  53. DWORD UnicWrapper_SHWaitForSendMessageThread(HANDLE hThread, DWORD dwTimeout)
  54. {
  55. MSG msg;
  56. DWORD dwRet;
  57. DWORD dwEnd = GetTickCount() + dwTimeout;
  58. // We will attempt to wait up to dwTimeout for the thread to
  59. // terminate
  60. do
  61. {
  62. dwRet = MsgWaitForMultipleObjects(1, &hThread, FALSE,
  63. dwTimeout, QS_SENDMESSAGE);
  64. if (dwRet == WAIT_OBJECT_0 ||
  65. dwRet == WAIT_FAILED)
  66. {
  67. // The thread must have exited, so we are happy
  68. break;
  69. }
  70. if (dwRet == WAIT_TIMEOUT)
  71. {
  72. // The thread is taking too long to finish, so just
  73. // return and let the caller kill it
  74. break;
  75. }
  76. // There must be a pending SendMessage from either the
  77. // thread we are killing or some other thread/process besides
  78. // this one. Do a PeekMessage to process the pending
  79. // SendMessage and try waiting again
  80. PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
  81. if (dwTimeout != INFINITE)
  82. dwTimeout = dwEnd - GetTickCount();
  83. }
  84. while((dwTimeout == INFINITE) || ((long)dwTimeout > 0));
  85. return(dwRet);
  86. }
  87. /****************************************************\
  88. FUNCTION: UnicWrapper_AutoCompleteFileSysInEditbox
  89. DESCRIPTION:
  90. This function will have AutoComplete take over
  91. an editbox to help autocomplete DOS paths.
  92. \****************************************************/
  93. HRESULT UnicWrapper_AutoCompleteFileSysInEditbox(HWND hwndEdit)
  94. {
  95. HRESULT hr;
  96. IUnknown * punkACLISF;
  97. hr = CoCreateInstance(CLSID_ACListISF, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&punkACLISF);
  98. if (SUCCEEDED(hr))
  99. {
  100. IAutoComplete * pac;
  101. // Create the AutoComplete Object
  102. hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, IID_IAutoComplete, (void **)&pac);
  103. if (SUCCEEDED(hr))
  104. {
  105. hr = pac->Init(hwndEdit, punkACLISF, NULL, NULL);
  106. pac->Release();
  107. }
  108. punkACLISF->Release();
  109. }
  110. return hr;
  111. }
  112. #endif // USE_IE5_UTILS
  113. //////////////////////////// IE 5 vs IE 4 /////////////////////////////////
  114. void IUnknown_Set(IMalloc ** ppm, IMalloc * pm)
  115. {
  116. ENTERCRITICAL;
  117. if (*ppm)
  118. (*ppm)->Release();
  119. *ppm = pm;
  120. if (pm)
  121. pm->AddRef();
  122. LEAVECRITICAL;
  123. }
  124. // TODO: This is a remnent of using C++ in stead of real COM
  125. void IUnknown_Set(CFtpFolder ** ppff, CFtpFolder * pff)
  126. {
  127. ENTERCRITICAL;
  128. if (*ppff)
  129. (*ppff)->Release();
  130. *ppff = pff;
  131. if (pff)
  132. pff->AddRef();
  133. LEAVECRITICAL;
  134. }
  135. void IUnknown_Set(CFtpDir ** ppfd, CFtpDir * pfd)
  136. {
  137. ENTERCRITICAL;
  138. if (*ppfd)
  139. (*ppfd)->Release();
  140. *ppfd = pfd;
  141. if (pfd)
  142. pfd->AddRef();
  143. LEAVECRITICAL;
  144. }
  145. void IUnknown_Set(CFtpSite ** ppfs, CFtpSite * pfs)
  146. {
  147. ENTERCRITICAL;
  148. if (*ppfs)
  149. (*ppfs)->Release();
  150. *ppfs = pfs;
  151. if (pfs)
  152. pfs->AddRef();
  153. LEAVECRITICAL;
  154. }
  155. void IUnknown_Set(CFtpList ** ppfl, CFtpList * pfl)
  156. {
  157. ENTERCRITICAL;
  158. if (*ppfl)
  159. (*ppfl)->Release();
  160. *ppfl = pfl;
  161. if (pfl)
  162. pfl->AddRef();
  163. LEAVECRITICAL;
  164. }
  165. void IUnknown_Set(CFtpPidlList ** ppflpidl, CFtpPidlList * pflpidl)
  166. {
  167. ENTERCRITICAL;
  168. if (*ppflpidl)
  169. (*ppflpidl)->Release();
  170. *ppflpidl = pflpidl;
  171. if (pflpidl)
  172. pflpidl->AddRef();
  173. LEAVECRITICAL;
  174. }
  175. void IUnknown_Set(CFtpEfe ** ppfefe, CFtpEfe * pfefe)
  176. {
  177. ENTERCRITICAL;
  178. if (*ppfefe)
  179. (*ppfefe)->Release();
  180. *ppfefe = pfefe;
  181. if (pfefe)
  182. pfefe->AddRef();
  183. LEAVECRITICAL;
  184. }
  185. void IUnknown_Set(CFtpGlob ** ppfg, CFtpGlob * pfg)
  186. {
  187. ENTERCRITICAL;
  188. if (*ppfg)
  189. (*ppfg)->Release();
  190. *ppfg = pfg;
  191. if (pfg)
  192. pfg->AddRef();
  193. LEAVECRITICAL;
  194. }
  195. void IUnknown_Set(CFtpMenu ** ppfcm, CFtpMenu * pfcm)
  196. {
  197. ENTERCRITICAL;
  198. if (*ppfcm)
  199. (*ppfcm)->Release();
  200. *ppfcm = pfcm;
  201. if (pfcm)
  202. pfcm->AddRef();
  203. LEAVECRITICAL;
  204. }
  205. void IUnknown_Set(CFtpStm ** ppfstm, CFtpStm * pfstm)
  206. {
  207. ENTERCRITICAL;
  208. if (*ppfstm)
  209. (*ppfstm)->Release();
  210. *ppfstm = pfstm;
  211. if (pfstm)
  212. pfstm->AddRef();
  213. LEAVECRITICAL;
  214. }
  215. #undef ILCombine
  216. // Fix Shell32 bug
  217. LPITEMIDLIST ILCombineWrapper(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  218. {
  219. if (!pidl1)
  220. return ILClone(pidl2);
  221. if (!pidl2)
  222. return ILClone(pidl1);
  223. return ILCombine(pidl1, pidl2);
  224. }
  225. #undef ILClone
  226. // Fix Shell32 bug
  227. LPITEMIDLIST ILCloneWrapper(LPCITEMIDLIST pidl)
  228. {
  229. if (!pidl)
  230. return NULL;
  231. return ILClone(pidl);
  232. }
  233. #undef ILFree
  234. // Fix Shell32 bug
  235. void ILFreeWrapper(LPITEMIDLIST pidl)
  236. {
  237. if (pidl)
  238. ILFree(pidl);
  239. }
  240. // Don't ship with this on.
  241. //#define DEBUG_LEGACY
  242. BOOL IsLegacyChangeNotifyNeeded(LONG wEventId)
  243. {
  244. #ifdef DEBUG_LEGACY
  245. return TRUE;
  246. #endif // DEBUG_LEGACY
  247. // The only version that doesn't support IDelegateFolder pidls is
  248. // shell32 v3 (w/o IE4 Shell Intergrated)
  249. BOOL fResult = (SHELL_VERSION_W95NT4 == GetShellVersion());
  250. return fResult;
  251. }
  252. /*****************************************************************************\
  253. FUNCTION: LegacyChangeNotify
  254. DESCRIPTION:
  255. Browser only can't read IDelegateFolder pidls (our Pidls), so we need
  256. to use this function instead of SHChangeNotify that will use hacks to
  257. get DefView's ListView to update by using
  258. SHShellFolderView_Message(HWND hwnd, UINT uMsg, LPARAM lParam).
  259. These are the messages to use.
  260. SFVM_ADDOBJECT (SHCNE_CREATE & SHCNE_MKDIR),
  261. SFVM_UPDATEOBJECT (SHCNE_RENAMEFOLDER, SHCNE_RENAMEITEM, SHCNE_ATTRIBUTES), or SFVM_REFRESHOBJECT(),
  262. SFVM_REMOVEOBJECT (SHCNE_RMDIR & SHCNE_DELETE).
  263. \*****************************************************************************/
  264. HRESULT LegacyChangeNotify(HWND hwnd, LONG wEventId, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  265. {
  266. if (EVAL(hwnd)) // We can't talk to the window w/o this.
  267. {
  268. switch(wEventId)
  269. {
  270. case SHCNE_CREATE:
  271. case SHCNE_MKDIR:
  272. {
  273. // NOTE: If the item alread exists, it will create a new duplicate name.
  274. // We need to skip this if it exists.
  275. LPCITEMIDLIST pidlRelative = ILGetLastID(pidl1);
  276. // SFVM_ADDOBJECT frees the pidl we give them.
  277. EVAL(SHShellFolderView_Message(hwnd, SFVM_ADDOBJECT, (LPARAM) ILClone(pidlRelative)));
  278. break;
  279. }
  280. case SHCNE_RMDIR:
  281. case SHCNE_DELETE:
  282. {
  283. LPCITEMIDLIST pidlRelative = ILGetLastID(pidl1);
  284. EVAL(SHShellFolderView_Message(hwnd, SFVM_REMOVEOBJECT, (LPARAM) pidlRelative));
  285. break;
  286. }
  287. case SHCNE_RENAMEFOLDER:
  288. case SHCNE_RENAMEITEM:
  289. case SHCNE_ATTRIBUTES:
  290. {
  291. LPCITEMIDLIST pidlArray[2];
  292. pidlArray[0] = ILGetLastID(pidl1);
  293. pidlArray[1] = ILClone(ILGetLastID(pidl2));
  294. EVAL(SHShellFolderView_Message(hwnd, SFVM_UPDATEOBJECT, (LPARAM) pidlArray));
  295. break;
  296. }
  297. }
  298. }
  299. return S_OK;
  300. }
  301. /*****************************************************************************\
  302. FUNCTION: FtpChangeNotify
  303. Convert the relative pidls into absolute pidls, then hand onwards
  304. to SHChangeNotify. If we can't do the notification, tough.
  305. Issuing a change notify also invalidates the name-cache, because
  306. we know that something happened to the directory.
  307. If we wanted to be clever, we could edit the name-cache on the
  308. fly, but that would entail allocating a new name-cache, initializing
  309. it with the edited directory contents, then setting it as the new
  310. cache. (We can't edit the name-cache in place because somebody
  311. might still be holding a reference to it.) And all this work needs
  312. to be done under the critical section, so that nobody else tries
  313. to do the same thing simultaneously. What's more, the only thing
  314. that this helps is the case where the user opens two views on
  315. the same folder from within the same process, which not a very
  316. common scenario. Summary: It's just not worth it.
  317. Note that this must be done at the CFtpFolder level and not at the
  318. CFtpDir level, because CFtpDir doesn't know where we are rooted.
  319. (We might have several instances, each rooted at different places.)
  320. _UNDOCUMENTED_: The pidl1 and pidl2 parameters to SHChangeNotify
  321. are not documented. It is also not mentioned (although it becomes
  322. obvious once you realize it) that the pidls passed to SHChangeNotify
  323. must be absolute.
  324. \*****************************************************************************/
  325. void FtpChangeNotify(HWND hwnd, LONG wEventId, CFtpFolder * pff, CFtpDir * pfd, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, BOOL fTopLevel)
  326. {
  327. ASSERT(pfd && IsValidPIDL(pidl1));
  328. ASSERT(!pidl2 || IsValidPIDL(pidl2));
  329. // Update our local cache because SHChangeNotify will come back in later and
  330. // want to create a pidl from a DisplayName and will then use that pidls
  331. // time/date. This is done because the shell is trying to create a 'full'
  332. // pidl.
  333. switch (wEventId)
  334. {
  335. case SHCNE_CREATE:
  336. case SHCNE_MKDIR:
  337. // TraceMsg(TF_CHANGENOTIFY, ((wEventId == SHCNE_CREATE) ? "FtpChangeNotify(SHCNE_CREATE), Name=%ls" : "FtpChangeNotify(SHCNE_MKDIR), Name=%s"), FtpPidl_GetFileDisplayName(pidl1));
  338. EVAL(SUCCEEDED(pfd->AddItem(pidl1)));
  339. break;
  340. case SHCNE_RMDIR:
  341. case SHCNE_DELETE:
  342. // TraceMsg(TF_CHANGENOTIFY, "FtpChangeNotify(SHCNE_DELETE), Name=%ls", FtpPidl_GetLastFileDisplayName(pidl1));
  343. pfd->DeletePidl(pidl1); // This may fail if we never populated that cache.
  344. break;
  345. case SHCNE_RENAMEFOLDER:
  346. {
  347. CFtpDir * pfdSubFolder = pfd->GetSubFtpDir(NULL, pidl1, TRUE);
  348. if (EVAL(pfdSubFolder))
  349. {
  350. LPITEMIDLIST pidlDest = pfd->GetSubPidl(NULL, pidl2, TRUE);
  351. if (EVAL(pidlDest))
  352. {
  353. EVAL(SUCCEEDED(pfdSubFolder->ChangeFolderName(pidlDest)));
  354. ILFree(pidlDest);
  355. }
  356. pfdSubFolder->Release();
  357. }
  358. }
  359. // break; Fall Thru so we change the pidl also.
  360. case SHCNE_RENAMEITEM:
  361. case SHCNE_ATTRIBUTES:
  362. // TraceMsg(TF_CHANGENOTIFY, "FtpChangeNotify(SHCNE_RENAMEITEM), Name1=%ls, Name2=%ls", FtpPidl_GetLastFileDisplayName(pidl1), FtpPidl_GetLastFileDisplayName(pidl2));
  363. EVAL(SUCCEEDED(pfd->ReplacePidl(pidl1, pidl2)));
  364. break;
  365. }
  366. pidl1 = pfd->GetSubPidl(pff, pidl1, TRUE);
  367. if (EVAL(pidl1))
  368. {
  369. if ((pidl2 == NULL) || (EVAL(pidl2 = pfd->GetSubPidl(pff, pidl2, TRUE))) != 0)
  370. {
  371. // LRESULT SHShellFolderView_Message(HWND hwnd, UINT uMsg, LPARAM lParam)
  372. // Are we on something (browser only) that can't read
  373. // IDelegateFolder pidls (our Pidls)?
  374. if (IsLegacyChangeNotifyNeeded(wEventId))
  375. {
  376. // Yes, so SHChangeNotify won't work. Use a work around.
  377. if (fTopLevel) // Only top level changes are appropriate.
  378. LegacyChangeNotify(hwnd, wEventId, pidl1, pidl2);
  379. }
  380. else
  381. SHChangeNotify(wEventId, (SHCNF_IDLIST | SHCNF_FLUSH), pidl1, pidl2);
  382. ILFree((LPITEMIDLIST)pidl2);
  383. }
  384. ILFree((LPITEMIDLIST)pidl1);
  385. }
  386. }
  387. /**************************************************************\
  388. FUNCTION: EscapeString
  389. DESCRIPTION:
  390. \**************************************************************/
  391. HRESULT EscapeString(LPCTSTR pszStrToEscape, LPTSTR pszEscapedStr, DWORD cchSize)
  392. {
  393. LPCTSTR pszCopy = NULL;
  394. if (!pszStrToEscape)
  395. {
  396. Str_SetPtr((LPTSTR *) &pszCopy, pszEscapedStr); // NULL pszStrToEscape means do pszEscapedStr in place.
  397. pszStrToEscape = pszCopy;
  398. }
  399. pszEscapedStr[0] = 0;
  400. if (pszStrToEscape && pszStrToEscape[0])
  401. UrlEscape(pszStrToEscape, pszEscapedStr, &cchSize, URL_ESCAPE_SEGMENT_ONLY);
  402. Str_SetPtr((LPTSTR *) &pszCopy, NULL); // NULL pszStrToEscape means do pszEscapedStr in place.
  403. return S_OK;
  404. }
  405. /**************************************************************\
  406. FUNCTION: UnEscapeString
  407. DESCRIPTION:
  408. \**************************************************************/
  409. HRESULT UnEscapeString(LPCTSTR pszStrToUnEscape, LPTSTR pszUnEscapedStr, DWORD cchSize)
  410. {
  411. LPCTSTR pszCopy = NULL;
  412. if (!pszStrToUnEscape)
  413. {
  414. Str_SetPtr((LPTSTR *) &pszCopy, pszUnEscapedStr); // NULL pszStrToEscape means do pszEscapedStr in place.
  415. pszStrToUnEscape = pszCopy;
  416. }
  417. pszUnEscapedStr[0] = 0;
  418. UrlUnescape((LPTSTR)pszStrToUnEscape, pszUnEscapedStr, &cchSize, URL_ESCAPE_SEGMENT_ONLY);
  419. Str_SetPtr((LPTSTR *) &pszCopy, NULL); // NULL pszStrToEscape means do pszEscapedStr in place.
  420. return S_OK;
  421. }
  422. /**************************************************************\
  423. Since wininet errors are often very generic, this function
  424. will generate error message of this format:
  425. "An error occurred while attempted to do x and it could not
  426. be completed.
  427. Details:
  428. <Wininet error that may be specific or generic>"
  429. \**************************************************************/
  430. int DisplayWininetErrorEx(HWND hwnd, BOOL fAssertOnNULLHWND, DWORD dwError, UINT idTitleStr, UINT idBaseErrorStr, UINT idDetailsStr, UINT nMsgBoxType, IProgressDialog * ppd, LPCWSTR pwzDetails)
  431. {
  432. TCHAR szErrMessage[MAX_PATH*3];
  433. TCHAR szTitle[MAX_PATH];
  434. BOOL fIsWininetError = ((dwError >= INTERNET_ERROR_BASE) && (dwError <= INTERNET_ERROR_LAST));
  435. HMODULE hmod = (fIsWininetError ? GetModuleHandle(TEXT("WININET")) : NULL);
  436. UINT uiType = (IDS_FTPERR_GETDIRLISTING == idBaseErrorStr) ? MB_ICONINFORMATION : MB_ICONERROR;
  437. if (ppd)
  438. {
  439. // If we have a progress dialog, we want to close it down
  440. // because we will display an error message and the progress
  441. // dialog in the background looks really dumb.
  442. ppd->StopProgressDialog();
  443. }
  444. // Default message if FormatMessage doesn't recognize hres
  445. LoadString(HINST_THISDLL, idBaseErrorStr, szErrMessage, ARRAYSIZE(szErrMessage));
  446. LoadString(HINST_THISDLL, idTitleStr, szTitle, ARRAYSIZE(szTitle));
  447. // Yes we did, so display the error.
  448. WCHAR szDetails[MAX_URL_STRING*2];
  449. TCHAR szPromptTemplate[MAX_PATH];
  450. TCHAR szBuffer[MAX_PATH*4];
  451. LoadString(HINST_THISDLL, idDetailsStr, szPromptTemplate, ARRAYSIZE(szPromptTemplate));
  452. // Can wininet give us extended error messages?
  453. // UNIX servers cancel the connection if the disk or quote is full
  454. // but the return a value that explains that to the user.
  455. if ((ERROR_INTERNET_EXTENDED_ERROR == dwError) ||
  456. (ERROR_INTERNET_CONNECTION_ABORTED == dwError))
  457. {
  458. if (!pwzDetails)
  459. {
  460. // We could remove the FTP cmd numbers from before the err strings except advanced users
  461. // can use them to know more information about the state of the server when this happened.
  462. // StripResponseHeaders(pszMOTD);
  463. if (FAILED(InternetGetLastResponseInfoDisplayWrap(TRUE, &dwError, szDetails, ARRAYSIZE(szDetails))))
  464. szDetails[0] = 0;
  465. pwzDetails = (LPCWSTR) szDetails;
  466. }
  467. }
  468. else
  469. {
  470. if (fIsWininetError)
  471. FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, (LPCVOID)hmod, dwError, 0, szDetails, ARRAYSIZE(szDetails), NULL);
  472. else
  473. FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, (LPCVOID)hmod, dwError, 0, szDetails, ARRAYSIZE(szDetails), NULL);
  474. pwzDetails = (LPCWSTR) szDetails;
  475. }
  476. wnsprintf(szBuffer, ARRAYSIZE(szBuffer), szPromptTemplate, pwzDetails);
  477. StrCatBuff(szErrMessage, szBuffer, ARRAYSIZE(szErrMessage));
  478. return MessageBox(hwnd, szErrMessage, szTitle, (uiType | nMsgBoxType));
  479. }
  480. int DisplayWininetError(HWND hwnd, BOOL fAssertOnNULLHWND, DWORD dwError, UINT idTitleStr, UINT idBaseErrorStr, UINT idDetailsStr, UINT nMsgBoxType, IProgressDialog * ppd)
  481. {
  482. if (hwnd) // Only display if HWND exists.
  483. return DisplayWininetErrorEx(hwnd, fAssertOnNULLHWND, dwError, idTitleStr, idBaseErrorStr, idDetailsStr, nMsgBoxType, ppd, NULL);
  484. else
  485. {
  486. if (fAssertOnNULLHWND)
  487. {
  488. // ASSERT(hwnd);
  489. }
  490. TraceMsg(TF_ALWAYS, "DisplayWininetError() no HWND so no Error.");
  491. }
  492. return IDCANCEL;
  493. }
  494. #define CCH_SIZE_ERROR_MESSAGE 6*1024
  495. HRESULT FtpSafeCreateDirectory(HWND hwnd, HINTERNET hint, CMultiLanguageCache * pmlc, CFtpFolder * pff, CFtpDir * pfd, IProgressDialog * ppd, LPCWSTR pwzFtpPath, BOOL fRoot)
  496. {
  497. FTP_FIND_DATA wfd;
  498. HRESULT hr = S_OK;
  499. WIRECHAR wFtpPath[MAX_PATH];
  500. CWireEncoding * pwe = pfd->GetFtpSite()->GetCWireEncoding();
  501. if (SUCCEEDED(pwe->UnicodeToWireBytes(NULL, pwzFtpPath, (pfd->IsUTF8Supported() ? WIREENC_USE_UTF8 : WIREENC_NONE), wFtpPath, ARRAYSIZE(wFtpPath))))
  502. {
  503. hr = FtpCreateDirectoryWrap(hint, TRUE, wFtpPath);
  504. // PERF NOTE: It is faster to just try to create the directory and then ignore
  505. // error return values that indicate that they failed to create because it
  506. // already exists. The problem I worry about is that there is some FTP server
  507. // impl somewhere that will return the same error as failed to create because
  508. // of access violation and we don't or can't return an error value.
  509. if (FAILED(hr)
  510. // NOTE: IE #30208: Currently broken in wininet. The dorks in wininet never fixed this because
  511. // they say it's no repro. It's no repro because I did this work around!$#!@@#%!!!
  512. //
  513. // I want to test the attribute flags but for some reason the FILE_ATTRIBUTE_DIRECTORY bit
  514. // is also set for files!!!! (!@(*#!!!)
  515. // || !(FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
  516. )
  517. {
  518. // Maybe if failed because it already exists, which is fine by me.
  519. // First save off the error msg in case we need it for the err dlg later.
  520. CHAR szErrorMsg[CCH_SIZE_ERROR_MESSAGE];
  521. WCHAR wzErrorMsg[CCH_SIZE_ERROR_MESSAGE];
  522. DWORD cchSize = ARRAYSIZE(szErrorMsg);
  523. InternetGetLastResponseInfoWrap(TRUE, NULL, szErrorMsg, &cchSize);
  524. HRESULT hrOrig = hr;
  525. pwe->WireBytesToUnicode(NULL, szErrorMsg, WIREENC_NONE, wzErrorMsg, ARRAYSIZE(wzErrorMsg));
  526. // Does it already exist?
  527. hr = FtpDoesFileExist(hint, TRUE, wFtpPath, &wfd, (INTERNET_NO_CALLBACK | INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RESYNCHRONIZE | INTERNET_FLAG_RELOAD));
  528. // It's okay if we failed to create the directory because a -DIRECTORY- already exists
  529. // because we'll just use that directory. However, it a file with that name exists,
  530. // then we need the err msg.
  531. if ((S_OK != hr) || !(FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes))
  532. {
  533. // No, so it was a real error, now display the error message with the original
  534. // server response.
  535. DisplayWininetErrorEx(hwnd, TRUE, HRESULT_CODE(hrOrig), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_DIRCOPY, IDS_FTPERR_WININET, MB_OK, ppd, wzErrorMsg);
  536. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  537. }
  538. }
  539. // Was it created successfully?
  540. if (SUCCEEDED(hr))
  541. {
  542. // Yes, so fire the change notify.
  543. LPITEMIDLIST pidlNewDir;
  544. FILETIME ftUTC;
  545. FTP_FIND_DATA wfd;
  546. GetSystemTimeAsFileTime(&ftUTC); // UTC
  547. FileTimeToLocalFileTime(&ftUTC, &wfd.ftCreationTime); // Need Local Time because FTP won't work in the cross time zones case.
  548. // For some reason, FtpFindFirstFile needs an '*' behind the name.
  549. StrCpyNA(wfd.cFileName, wFtpPath, ARRAYSIZE(wfd.cFileName));
  550. wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
  551. wfd.ftLastWriteTime = wfd.ftCreationTime;
  552. wfd.ftLastAccessTime = wfd.ftCreationTime;
  553. wfd.nFileSizeLow = 0;
  554. wfd.nFileSizeHigh = 0;
  555. wfd.dwReserved0 = 0;
  556. wfd.dwReserved1 = 0;
  557. wfd.cAlternateFileName[0] = 0;
  558. hr = FtpItemID_CreateReal(&wfd, pwzFtpPath, &pidlNewDir);
  559. if (SUCCEEDED(hr)) // May happen on weird character set problems.
  560. {
  561. // Notify the folder of the new item so the Shell Folder updates.
  562. // PERF: Note that we should give SHChangeNotify() the information (time/date)
  563. // from the local file system which may be different than on the server.
  564. // But I don't think it's worth the perf to hit the server for the info.
  565. FtpChangeNotify(hwnd, SHCNE_MKDIR, pff, pfd, pidlNewDir, NULL, fRoot);
  566. ILFree(pidlNewDir);
  567. }
  568. }
  569. }
  570. return hr;
  571. }
  572. HWND GetProgressHWnd(IProgressDialog * ppd, HWND hwndDefault)
  573. {
  574. if (ppd)
  575. {
  576. HWND hwndProgress = NULL;
  577. IUnknown_GetWindow(ppd, &hwndProgress);
  578. if (hwndProgress)
  579. hwndDefault = hwndProgress;
  580. }
  581. return hwndDefault;
  582. }
  583. // Returns FALSE if out of memory
  584. int SHMessageBox(HWND hwnd, LPCTSTR pszMessage, UINT uMessageID, UINT uTitleID, UINT uType)
  585. {
  586. int nResult = IDCANCEL;
  587. TCHAR szMessage[MAX_PATH];
  588. TCHAR szTitle[MAX_PATH];
  589. if (LoadString(HINST_THISDLL, uTitleID, szTitle, ARRAYSIZE(szTitle)) &&
  590. (pszMessage ||
  591. (uMessageID && LoadString(HINST_THISDLL, uMessageID, szMessage, ARRAYSIZE(szMessage)))))
  592. {
  593. nResult = MessageBox(hwnd, pszMessage ? pszMessage : szMessage, szTitle, uType);
  594. }
  595. return nResult;
  596. }
  597. BOOL IsOSNT(void)
  598. {
  599. OSVERSIONINFOA osVerInfoA;
  600. osVerInfoA.dwOSVersionInfoSize = sizeof(osVerInfoA);
  601. if (!GetVersionExA(&osVerInfoA))
  602. return VER_PLATFORM_WIN32_WINDOWS; // Default to this.
  603. return (VER_PLATFORM_WIN32_NT == osVerInfoA.dwPlatformId);
  604. }
  605. DWORD GetOSVer(void)
  606. {
  607. OSVERSIONINFOA osVerInfoA;
  608. osVerInfoA.dwOSVersionInfoSize = sizeof(osVerInfoA);
  609. if (!GetVersionExA(&osVerInfoA))
  610. return VER_PLATFORM_WIN32_WINDOWS; // Default to this.
  611. return osVerInfoA.dwMajorVersion;
  612. }
  613. LPITEMIDLIST SHILCreateFromPathWrapper(LPCTSTR pszPath)
  614. {
  615. LPITEMIDLIST pidl;
  616. if (IsOSNT())
  617. {
  618. WCHAR wzPath[MAX_PATH];
  619. SHTCharToUnicode(pszPath, wzPath, ARRAYSIZE(wzPath));
  620. SHILCreateFromPath((LPCTSTR)wzPath, &pidl, NULL);
  621. }
  622. else
  623. {
  624. CHAR szPath[MAX_PATH];
  625. SHTCharToAnsi(pszPath, szPath, ARRAYSIZE(szPath));
  626. SHILCreateFromPath((LPCTSTR)szPath, &pidl, NULL);
  627. }
  628. return pidl;
  629. }
  630. LPCITEMIDLIST ILGetLastID(LPCITEMIDLIST pidlIn)
  631. {
  632. LPITEMIDLIST pidl = (LPITEMIDLIST) pidlIn;
  633. while (!ILIsEmpty(_ILNext(pidl)))
  634. pidl = _ILNext(pidl);
  635. return pidl;
  636. }
  637. LPCITEMIDLIST ILGetLastNonFragID(LPCITEMIDLIST pidlIn)
  638. {
  639. LPITEMIDLIST pidl = (LPITEMIDLIST) pidlIn;
  640. while (!ILIsEmpty(_ILNext(pidl)) && !FtpItemID_IsFragment(_ILNext(pidl)))
  641. pidl = _ILNext(pidl);
  642. return pidl;
  643. }
  644. SAFEARRAY * MakeSafeArrayFromData(LPCBYTE pData,DWORD cbData)
  645. {
  646. SAFEARRAY * psa;
  647. if (!pData || 0 == cbData)
  648. return NULL; // nothing to do
  649. // create a one-dimensional safe array
  650. psa = SafeArrayCreateVector(VT_UI1,0,cbData);
  651. ASSERT(psa);
  652. if (psa) {
  653. // copy data into the area in safe array reserved for data
  654. // Note we party directly on the pointer instead of using locking/
  655. // unlocking functions. Since we just created this and no one
  656. // else could possibly know about it or be using it, this is OK.
  657. ASSERT(psa->pvData);
  658. memcpy(psa->pvData,pData,cbData);
  659. }
  660. return psa;
  661. }
  662. //
  663. // PARAMETER:
  664. // pvar - Allocated by caller and filled in by this function.
  665. // pidl - Allocated by caller and caller needs to free.
  666. //
  667. // This function will take the PIDL parameter and COPY it
  668. // into the Variant data structure. This allows the pidl
  669. // to be freed and the pvar to be used later, however, it
  670. // is necessary to call VariantClear(pvar) to free memory
  671. // that this function allocates.
  672. BOOL InitVariantFromIDList(VARIANT* pvar, LPCITEMIDLIST pidl)
  673. {
  674. UINT cb = ILGetSize(pidl);
  675. SAFEARRAY* psa = MakeSafeArrayFromData((LPCBYTE)pidl, cb);
  676. if (psa) {
  677. ASSERT(psa->cDims == 1);
  678. // ASSERT(psa->cbElements == cb);
  679. ASSERT(ILGetSize((LPCITEMIDLIST)psa->pvData)==cb);
  680. VariantInit(pvar);
  681. pvar->vt = VT_ARRAY|VT_UI1;
  682. pvar->parray = psa;
  683. return TRUE;
  684. }
  685. return FALSE;
  686. }
  687. BSTR BStrFromStr(LPCTSTR pszStr)
  688. {
  689. BSTR bStr = NULL;
  690. #ifdef UNICODE
  691. bStr = SysAllocString(pszStr);
  692. #else // UNICODE
  693. DWORD cchSize = (lstrlen(pszStr) + 2);
  694. bStr = SysAllocStringLen(NULL, cchSize);
  695. if (bStr)
  696. SHAnsiToUnicode(pszStr, bStr, cchSize);
  697. #endif // UNICODE
  698. return bStr;
  699. }
  700. HRESULT IUnknown_IWebBrowserNavigate2(IUnknown * punk, LPCITEMIDLIST pidl, BOOL fHistoryEntry)
  701. {
  702. HRESULT hr = E_FAIL;
  703. IWebBrowser2 * pwb2;
  704. // punk will be NULL on Browser Only installs because the old
  705. // shell32 doesn't do ::SetSite().
  706. IUnknown_QueryService(punk, SID_SWebBrowserApp, IID_IWebBrowser2, (LPVOID *) &pwb2);
  707. if (pwb2)
  708. {
  709. VARIANT varThePidl;
  710. if (InitVariantFromIDList(&varThePidl, pidl))
  711. {
  712. VARIANT varFlags;
  713. VARIANT * pvarFlags = PVAREMPTY;
  714. if (!fHistoryEntry)
  715. {
  716. varFlags.vt = VT_I4;
  717. varFlags.lVal = navNoHistory;
  718. pvarFlags = &varFlags;
  719. }
  720. hr = pwb2->Navigate2(&varThePidl, pvarFlags, PVAREMPTY, PVAREMPTY, PVAREMPTY);
  721. VariantClear(&varThePidl);
  722. }
  723. pwb2->Release();
  724. }
  725. else
  726. {
  727. IShellBrowser * psb;
  728. // Maybe we are in comdlg32.
  729. hr = IUnknown_QueryService(punk, SID_SCommDlgBrowser, IID_IShellBrowser, (LPVOID *) &psb);
  730. if (SUCCEEDED(hr))
  731. {
  732. CFtpView * pfv = GetCFtpViewFromDefViewSite(punk);
  733. AssertMsg((NULL != pfv), TEXT("IUnknown_IWebBrowserNavigate2() defview gave us our IShellFolderViewCB so it needs to support this interface."));
  734. if (pfv)
  735. {
  736. // Are we on the forground thread?
  737. if (pfv->IsForegroundThread())
  738. {
  739. // Yes, so this will be easy. This is the case
  740. // where "Login As..." was chosen from the background context menu item.
  741. hr = psb->BrowseObject(pidl, 0);
  742. }
  743. else
  744. {
  745. // No, so this is the case where we failed to login with the original
  746. // UserName/Password and we will try again with the corrected Username/Password.
  747. // Okay, we are talking to the ComDlg code but we don't want to use
  748. // IShellBrowse::BrowseObject() because we are on a background thread. (NT #297732)
  749. // Therefore, we want to have the IShellFolderViewCB (CFtpView) cause
  750. // the redirect on the forground thread. Let's inform
  751. // CFtpView now to do this.
  752. hr = pfv->SetRedirectPidl(pidl);
  753. }
  754. pfv->Release();
  755. }
  756. AssertMsg(SUCCEEDED(hr), TEXT("IUnknown_IWebBrowserNavigate2() defview needs to support QS(SID_ShellFolderViewCB) on all platforms that hit this point"));
  757. psb->Release();
  758. }
  759. }
  760. return hr;
  761. }
  762. HRESULT IUnknown_PidlNavigate(IUnknown * punk, LPCITEMIDLIST pidl, BOOL fHistoryEntry)
  763. {
  764. HRESULT hrOle = SHCoInitialize();
  765. HRESULT hr = IUnknown_IWebBrowserNavigate2(punk, pidl, fHistoryEntry);
  766. // Try a pre-NT5 work around.
  767. // punk will be NULL on Browser Only installs because the old
  768. // shell32 doesn't do ::SetSite().
  769. if (FAILED(hr))
  770. {
  771. IWebBrowserApp * pauto = NULL;
  772. hr = SHGetIDispatchForFolder(pidl, &pauto);
  773. if (pauto)
  774. {
  775. hr = IUnknown_IWebBrowserNavigate2(pauto, pidl, fHistoryEntry);
  776. ASSERT(SUCCEEDED(hr));
  777. pauto->Release();
  778. }
  779. }
  780. ASSERT(SUCCEEDED(hrOle));
  781. SHCoUninitialize(hrOle);
  782. return hr;
  783. }
  784. /*****************************************************************************\
  785. HIDACREATEINFO
  786. Structure that collects all information needed when building
  787. an ID List Array.
  788. \*****************************************************************************/
  789. typedef struct tagHIDACREATEINFO
  790. {
  791. HIDA hida; /* The HIDA being built */
  792. UINT ipidl; /* Who we are */
  793. UINT ib; /* Where we are */
  794. UINT cb; /* Where we're going */
  795. UINT cpidl; /* How many we're doing */
  796. LPCITEMIDLIST pidlFolder; /* The parent all these LPITEMIDLISTs live in */
  797. CFtpPidlList * pflHfpl; /* The pidl list holding all the kids */
  798. } HIDACREATEINFO, * LPHIDACREATEINFO;
  799. #define pidaPhci(phci) ((LPIDA)(phci)->hida) /* no need to lock */
  800. /*****************************************************************************\
  801. Misc_SfgaoFromFileAttributes
  802. AIGH!
  803. UNIX and Win32 semantics on file permissions are different.
  804. On UNIX, the ability to rename or delete a file depends on
  805. your permissions on the parent folder.
  806. On Win32, the ability to rename or delete a file depends on
  807. your permissions on the file itself.
  808. Note that there is no such thing as "deny-read" attributes
  809. on Win32... I wonder how WinINet handles that...
  810. I'm going to hope that WinINet does the proper handling of this,
  811. so I'll just proceed with Win32 semantics... I'm probably assuming too much...
  812. \*****************************************************************************/
  813. DWORD Misc_SfgaoFromFileAttributes(DWORD dwFAFLFlags)
  814. {
  815. DWORD sfgao = SFGAO_CANLINK; // You can always link
  816. sfgao |= SFGAO_HASPROPSHEET; // You can always view properties
  817. sfgao |= SFGAO_CANCOPY; // Deny-read? No such thing! (Yet)
  818. if (dwFAFLFlags & FILE_ATTRIBUTE_READONLY)
  819. { /* Can't delete it, sorry */
  820. #ifdef _SOMEDAY_ASK_FRANCISH_WHAT_THIS_IS
  821. if (SHELL_VERSION_NT5 == GetShellVersion())
  822. sfgao |= SFGAO_READONLY;
  823. #endif
  824. }
  825. else
  826. {
  827. sfgao |= (SFGAO_CANRENAME | SFGAO_CANDELETE);
  828. #ifdef FEATURE_CUT_MOVE
  829. sfgao |= SFGAO_CANMOVE;
  830. #endif // FEATURE_CUT_MOVE
  831. }
  832. if (dwFAFLFlags & FILE_ATTRIBUTE_DIRECTORY)
  833. {
  834. //Since FTP connections are expensive, assume SFGAO_HASSUBFOLDER
  835. sfgao |= SFGAO_DROPTARGET | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_STORAGEANCESTOR;
  836. }
  837. else
  838. {
  839. // We always return the
  840. // SFGAO_BROWSABLE because we always want to do the navigation
  841. // using our IShellFolder::CreateViewObject(). In the case of
  842. // files, the CreateViewObject() that we create is for URLMON
  843. // which will do the download. This is especially true for
  844. // Folder Shortcuts.
  845. sfgao |= SFGAO_BROWSABLE | SFGAO_STREAM;
  846. }
  847. return sfgao;
  848. }
  849. /*****************************************************************************\
  850. FUNCTION: Misc_StringFromFileTime
  851. DESCRIPTION:
  852. Get the date followed by the time. flType can be DATE_SHORTDATE
  853. (for defview's details list) or DATE_LONGDATE for the property sheet.
  854. PARAMETERS:
  855. pft: This needs to be stored in UTC (NOT Time Zone dependent form!!!!)
  856. \*****************************************************************************/
  857. HRESULT Misc_StringFromFileTime(LPTSTR pszDateTime, DWORD cchSize, LPFILETIME pftUTC, DWORD flType)
  858. {
  859. if (EVAL(pftUTC && pftUTC->dwHighDateTime))
  860. {
  861. // SHFormatDateTime() takes the date in UTC format.
  862. SHFormatDateTime(pftUTC, &flType, pszDateTime, cchSize);
  863. }
  864. else
  865. pszDateTime[0] = 0;
  866. return S_OK;
  867. }
  868. LPITEMIDLIST GetPidlFromFtpFolderAndPidlList(CFtpFolder * pff, CFtpPidlList * pflHfpl)
  869. {
  870. LPCITEMIDLIST pidlBase = pff->GetPrivatePidlReference();
  871. LPCITEMIDLIST pidlRelative = ((0 == pflHfpl->GetCount()) ? c_pidlNil : pflHfpl->GetPidl(0));
  872. return ILCombine(pidlBase, pidlRelative);
  873. }
  874. IProgressDialog * CProgressDialog_CreateInstance(UINT idTitle, UINT idAnimation)
  875. {
  876. IProgressDialog * ppd = NULL;
  877. if (SUCCEEDED(CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, IID_IProgressDialog, (void **)&ppd)))
  878. {
  879. WCHAR wzTitle[MAX_PATH];
  880. if (EVAL(LoadStringW(HINST_THISDLL, idTitle, wzTitle, ARRAYSIZE(wzTitle))))
  881. EVAL(SUCCEEDED(ppd->SetTitle(wzTitle)));
  882. EVAL(SUCCEEDED(ppd->SetAnimation(HINST_THISDLL, idAnimation)));
  883. }
  884. return ppd;
  885. }
  886. BOOL_PTR CALLBACK ProxyDlgWarningWndProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  887. {
  888. if (uMsg == WM_INITDIALOG)
  889. {
  890. LPCTSTR pszUrl = (LPCTSTR)lParam;
  891. TCHAR szMessage[MAX_PATH*3];
  892. TCHAR szTemplate[MAX_PATH*3];
  893. ASSERT(pszUrl);
  894. EVAL(LoadString(HINST_THISDLL, IDS_FTP_PROXY_WARNING, szTemplate, ARRAYSIZE(szTemplate)));
  895. wnsprintf(szMessage, ARRAYSIZE(szMessage), szTemplate, pszUrl);
  896. EVAL(SetWindowText(GetDlgItem(hDlg, IDC_PROXY_MESSAGE), szMessage));
  897. }
  898. return FALSE;
  899. }
  900. /*****************************************************************************\
  901. FUNCTION: DisplayBlockingProxyDialog
  902. DESCRIPTION:
  903. Inform user that their CERN style proxy is blocking real FTP access so
  904. they can do something about it.
  905. Inform the user so they can:
  906. A) Change proxies,
  907. B) Annoy their administrator to install real proxies,
  908. C) Install Remote WinSock themselves,
  909. D) or settle for their sorry situation in life and use the
  910. limited CERN proxy support and dream about the abilitity
  911. to rename, delete, and upload.
  912. This will be a no-op if the user clicks "Don't display this
  913. message again" check box.
  914. \*****************************************************************************/
  915. HRESULT DisplayBlockingProxyDialog(LPCITEMIDLIST pidl, HWND hwnd)
  916. {
  917. // Did the IBindCtx provide information to allow us to do UI?
  918. if (hwnd)
  919. {
  920. TCHAR szUrl[MAX_PATH];
  921. HWND hwndParent = hwnd;
  922. // NT #321103: If the user used Start->Run to open the dialog, then
  923. // it may not receive focus because our parent browser has not yet
  924. // appeared (it doesn't have the WS_VISIBLE style yet and it has the
  925. // WS_DISABLED style. So we need to force our dialog to become active.
  926. if (hwndParent && !IsWindowVisible(hwndParent))
  927. {
  928. hwndParent = NULL;
  929. }
  930. UrlCreateFromPidl(pidl, SHGDN_FORPARSING, szUrl, ARRAYSIZE(szUrl), 0, TRUE);
  931. // Make it modal while the dialog is being displayed.
  932. // IUnknown_EnableModless(punkSite, FALSE);
  933. SHMessageBoxCheckEx(hwndParent, HINST_THISDLL, MAKEINTRESOURCE(IDD_PROXYDIALOG), ProxyDlgWarningWndProc, (LPVOID) szUrl, IDOK, SZ_REGVALUE_WARN_ABOUT_PROXY);
  934. // IUnknown_EnableModless(punkSite, TRUE);
  935. }
  936. return S_OK;
  937. }
  938. HRESULT CreateFromToStr(LPWSTR pwzStrOut, DWORD cchSize, ...)
  939. {
  940. CHAR szStatusText[MAX_PATH];
  941. CHAR szTemplate[MAX_PATH];
  942. va_list vaParamList;
  943. va_start(vaParamList, cchSize);
  944. // Generate the string "From <SrcFtpUrlDir> to <DestFileDir>" status string
  945. EVAL(LoadStringA(HINST_THISDLL, IDS_DL_SRC_DEST, szTemplate, ARRAYSIZE(szTemplate)));
  946. if (EVAL(FormatMessageA(FORMAT_MESSAGE_FROM_STRING, szTemplate, 0, 0, szStatusText, ARRAYSIZE(szStatusText), &vaParamList)))
  947. SHAnsiToUnicode(szStatusText, pwzStrOut, cchSize);
  948. va_end(vaParamList);
  949. return S_OK;
  950. }
  951. /****************************************************\
  952. FUNCTION: FtpProgressInternetStatusCB
  953. DESCRIPTION:
  954. This function is exists to be called back during
  955. long FTP operations so we can update the progress
  956. dialog during FtpPutFile or FtpGetFile.
  957. A pointer to our PROGRESSINFO struct is passed in
  958. dwContext.
  959. \****************************************************/
  960. void FtpProgressInternetStatusCB(IN HINTERNET hInternet, IN DWORD_PTR pdwContext, IN DWORD dwInternetStatus, IN LPVOID lpwStatusInfo, IN DWORD dwStatusInfoLen)
  961. {
  962. LPPROGRESSINFO pProgInfo = (LPPROGRESSINFO) pdwContext;
  963. if (EVAL(pProgInfo))
  964. {
  965. switch (dwInternetStatus)
  966. {
  967. case INTERNET_STATUS_RESPONSE_RECEIVED:
  968. case INTERNET_STATUS_REQUEST_SENT:
  969. if (EVAL(lpwStatusInfo && (sizeof(DWORD) == dwStatusInfoLen)
  970. && pProgInfo))
  971. {
  972. if (pProgInfo->hint && pProgInfo->ppd->HasUserCancelled())
  973. {
  974. EVAL(InternetCloseHandle(pProgInfo->hint));
  975. pProgInfo->hint = NULL;
  976. }
  977. pProgInfo->dwCompletedInCurFile += *(LPDWORD)lpwStatusInfo;
  978. // Has a big enough chunck of the file completed that we need
  979. // to update the progress? We only want to update the progress
  980. // every SIZE_PROGRESS_AFTERBYTES (50k) chunck.
  981. if (pProgInfo->dwLastDisplayed < (pProgInfo->dwCompletedInCurFile / SIZE_PROGRESS_AFTERBYTES))
  982. {
  983. ULARGE_INTEGER uliBytesCompleted;
  984. pProgInfo->dwLastDisplayed = (pProgInfo->dwCompletedInCurFile / SIZE_PROGRESS_AFTERBYTES);
  985. uliBytesCompleted.HighPart = 0;
  986. uliBytesCompleted.LowPart = pProgInfo->dwCompletedInCurFile;
  987. uliBytesCompleted.QuadPart += pProgInfo->uliBytesCompleted.QuadPart;
  988. if (pProgInfo->ppd)
  989. EVAL(SUCCEEDED(pProgInfo->ppd->SetProgress64(uliBytesCompleted.QuadPart, pProgInfo->uliBytesTotal.QuadPart)));
  990. }
  991. }
  992. break;
  993. }
  994. }
  995. }
  996. /*****************************************************************************\
  997. Misc_CreateHglob
  998. Allocate an hglobal of the indicated size, initialized from the
  999. specified buffer.
  1000. \*****************************************************************************/
  1001. HRESULT Misc_CreateHglob(SIZE_T cb, LPVOID pv, HGLOBAL *phglob)
  1002. {
  1003. HRESULT hres = E_OUTOFMEMORY;
  1004. *phglob = 0; // Rules are rules
  1005. if (cb)
  1006. {
  1007. *phglob = (HGLOBAL) LocalAlloc(LPTR, cb);
  1008. if (*phglob)
  1009. {
  1010. hres = S_OK;
  1011. CopyMemory(*phglob, pv, cb);
  1012. }
  1013. }
  1014. else
  1015. hres = E_INVALIDARG; // Can't clone a discardable block
  1016. return hres;
  1017. }
  1018. /*****************************************************************************\
  1019. _HIDA_Create_Tally
  1020. Worker function for HIDA_Create which tallies up the total size.
  1021. \*****************************************************************************/
  1022. int _HIDA_Create_Tally(LPVOID pvPidl, LPVOID pv)
  1023. {
  1024. LPCITEMIDLIST pidl = (LPCITEMIDLIST) pvPidl;
  1025. UINT *pcb = (UINT *) pv;
  1026. int nContinue = (pv ? TRUE : FALSE);
  1027. if (pcb)
  1028. {
  1029. *pcb += ILGetSize(pidl);
  1030. }
  1031. return nContinue;
  1032. }
  1033. /*****************************************************************************\
  1034. _HIDA_Create_AddIdl
  1035. Worker function for HIDA_Create which appends another ID List
  1036. to the growing HIDA.
  1037. \*****************************************************************************/
  1038. int _HIDA_Create_AddIdl(LPVOID pvPidl, LPVOID pv)
  1039. {
  1040. LPCITEMIDLIST pidl = (LPCITEMIDLIST) pvPidl;
  1041. LPHIDACREATEINFO phci = (LPHIDACREATEINFO) pv;
  1042. UINT cb = ILGetSize(pidl);
  1043. pidaPhci(phci)->aoffset[phci->ipidl++] = phci->ib;
  1044. CopyMemory(pvByteIndexCb(pidaPhci(phci), phci->ib), pidl, cb);
  1045. phci->ib += cb;
  1046. return phci ? TRUE : FALSE;
  1047. }
  1048. /*****************************************************************************\
  1049. _Misc_HIDA_Init
  1050. Once we've allocated the memory for a HIDA, fill it with stuff.
  1051. \*****************************************************************************/
  1052. BOOL _Misc_HIDA_Init(LPVOID hida, LPVOID pv, LPCVOID pvParam2, BOOL fUnicode)
  1053. {
  1054. LPHIDACREATEINFO phci = (LPHIDACREATEINFO) pv;
  1055. phci->hida = hida;
  1056. pidaPhci(phci)->cidl = phci->cpidl;
  1057. phci->ipidl = 0;
  1058. phci->pflHfpl->TraceDump(_ILNext(phci->pidlFolder), TEXT("_Misc_HIDA_Init() TraceDump Before"));
  1059. _HIDA_Create_AddIdl((LPVOID) phci->pidlFolder, (LPVOID) phci);
  1060. phci->pflHfpl->Enum(_HIDA_Create_AddIdl, (LPVOID) phci);
  1061. phci->pflHfpl->TraceDump(_ILNext(phci->pidlFolder), TEXT("_Misc_HIDA_Init() TraceDump After"));
  1062. return 1;
  1063. }
  1064. /*****************************************************************************\
  1065. HIDA_Create
  1066. Swiped from idlist.c in the shell because they didn't ;Internal
  1067. export it. ;Internal
  1068. \*****************************************************************************/
  1069. HIDA Misc_HIDA_Create(LPCITEMIDLIST pidlFolder, CFtpPidlList * pflHfpl)
  1070. {
  1071. HIDACREATEINFO hci;
  1072. LPHIDACREATEINFO phci = &hci;
  1073. HIDA hida;
  1074. pflHfpl->TraceDump(_ILNext(pidlFolder), TEXT("Misc_HIDA_Create() TraceDump Before"));
  1075. phci->pidlFolder = pidlFolder;
  1076. phci->pflHfpl = pflHfpl;
  1077. phci->cpidl = pflHfpl->GetCount();
  1078. phci->ib = sizeof(CIDA) + sizeof(UINT) * phci->cpidl;
  1079. phci->cb = phci->ib + ILGetSize(pidlFolder);
  1080. pflHfpl->Enum(_HIDA_Create_Tally, (LPVOID) &phci->cb);
  1081. hida = AllocHGlob(phci->cb, _Misc_HIDA_Init, phci, NULL, FALSE);
  1082. pflHfpl->TraceDump(_ILNext(pidlFolder), TEXT("Misc_HIDA_Create() TraceDump Before"));
  1083. return hida;
  1084. }
  1085. typedef struct tagURL_FILEGROUP
  1086. {
  1087. LPFILEGROUPDESCRIPTORA pfgdA;
  1088. LPFILEGROUPDESCRIPTORW pfgdW;
  1089. LPCITEMIDLIST pidlParent;
  1090. } URL_FILEGROUP;
  1091. /*****************************************************************************\
  1092. Misc_HFGD_Create
  1093. Build a file group descriptor based on an pflHfpl.
  1094. CFtpObj::_DelayRender_FGD() did the recursive walk to expand the list
  1095. of pidls, so we don't have to.
  1096. \*****************************************************************************/
  1097. #define cbFgdCfdW(cfd) FIELD_OFFSET(FILEGROUPDESCRIPTORW, fgd[cfd])
  1098. #define cbFgdCfdA(cfd) FIELD_OFFSET(FILEGROUPDESCRIPTORA, fgd[cfd])
  1099. int _Misc_HFGD_Create(LPVOID pvPidl, LPVOID pv)
  1100. {
  1101. BOOL fSucceeded = TRUE;
  1102. URL_FILEGROUP * pUrlFileGroup = (URL_FILEGROUP *) pv;
  1103. LPCITEMIDLIST pidlFull = (LPCITEMIDLIST) pvPidl;
  1104. LPCITEMIDLIST pidl;
  1105. LPFILEGROUPDESCRIPTORA pfgdA = pUrlFileGroup->pfgdA;
  1106. LPFILEGROUPDESCRIPTORW pfgdW = pUrlFileGroup->pfgdW;
  1107. LPFILEDESCRIPTORA pfdA = (pfgdA ? &pfgdA->fgd[pfgdA->cItems++] : NULL);
  1108. LPFILEDESCRIPTORW pfdW = (pfgdW ? &pfgdW->fgd[pfgdW->cItems++] : NULL);
  1109. pidl = ILGetLastID(pidlFull);
  1110. if (pfdA)
  1111. {
  1112. #if !DEBUG_LEGACY_PROGRESS
  1113. pfdA->dwFlags = (FD_ATTRIBUTES | FD_FILESIZE | FD_CREATETIME | FD_ACCESSTIME | FD_WRITESTIME | FD_PROGRESSUI);
  1114. #else // !DEBUG_LEGACY_PROGRESS
  1115. pfdA->dwFlags = (FD_ATTRIBUTES | FD_FILESIZE | FD_CREATETIME | FD_ACCESSTIME | FD_WRITESTIME);
  1116. #endif // !DEBUG_LEGACY_PROGRESS
  1117. pfdA->dwFileAttributes = FtpItemID_GetAttributes(pidl);
  1118. pfdA->nFileSizeLow = FtpItemID_GetFileSizeLo(pidl);
  1119. pfdA->nFileSizeHigh = FtpItemID_GetFileSizeHi(pidl);
  1120. // all WIN32_FIND_DATA want to be stored in TimeZone independent
  1121. // ways, except for WININET's FTP. Also note that we only store Modified
  1122. // time and use if for everything because of another UNIX/Wininet issue.
  1123. // See priv.h on more FTP Time/Date issues.
  1124. pfdA->ftCreationTime = FtpPidl_GetFileTime(ILFindLastID(pidl));
  1125. pfdA->ftLastWriteTime = pfdA->ftCreationTime;
  1126. pfdA->ftLastAccessTime = pfdA->ftCreationTime;
  1127. }
  1128. else
  1129. {
  1130. #if !DEBUG_LEGACY_PROGRESS
  1131. pfdW->dwFlags = (FD_ATTRIBUTES | FD_FILESIZE | FD_CREATETIME | FD_ACCESSTIME | FD_WRITESTIME | FD_PROGRESSUI);
  1132. #else // !DEBUG_LEGACY_PROGRESS
  1133. pfdW->dwFlags = (FD_ATTRIBUTES | FD_FILESIZE | FD_CREATETIME | FD_ACCESSTIME | FD_WRITESTIME);
  1134. #endif // !DEBUG_LEGACY_PROGRESS
  1135. pfdW->dwFileAttributes = FtpItemID_GetAttributes(pidl);
  1136. pfdW->nFileSizeLow = FtpItemID_GetFileSizeLo(pidl);
  1137. pfdW->nFileSizeHigh = FtpItemID_GetFileSizeHi(pidl);
  1138. // all WIN32_FIND_DATA want to be stored in TimeZone independent
  1139. // ways, except for WININET's FTP. Also note that we only store Modified
  1140. // time and use if for everything because of another UNIX/Wininet issue.
  1141. // See priv.h on more FTP Time/Date issues.
  1142. pfdW->ftCreationTime = FtpPidl_GetFileTime(ILFindLastID(pidl));
  1143. pfdW->ftLastWriteTime = pfdW->ftCreationTime;
  1144. pfdW->ftLastAccessTime = pfdW->ftCreationTime;
  1145. }
  1146. LPCITEMIDLIST pidlDiff = FtpItemID_FindDifference(pUrlFileGroup->pidlParent, pidlFull);
  1147. if (pfdA)
  1148. {
  1149. GetWirePathFromPidl(pidlDiff, pfdA->cFileName, ARRAYSIZE(pfdA->cFileName), FALSE);
  1150. UrlPathRemoveSlashA(pfdA->cFileName);
  1151. UrlPathRemoveFrontSlashA(pfdA->cFileName);
  1152. UrlPathToFilePathA(pfdA->cFileName);
  1153. }
  1154. else
  1155. {
  1156. GetDisplayPathFromPidl(pidlDiff, pfdW->cFileName, ARRAYSIZE(pfdW->cFileName), FALSE);
  1157. UrlPathRemoveSlashW(pfdW->cFileName);
  1158. UrlPathRemoveFrontSlashW(pfdW->cFileName);
  1159. UrlPathToFilePathW(pfdW->cFileName);
  1160. }
  1161. TraceMsg(TF_FTPURL_UTILS, "_Misc_HFGD_Create() pfd(A/W)->dwFileAttributes=%#08lX", (pfdW ? pfdW->dwFileAttributes : pfdA->dwFileAttributes));
  1162. return fSucceeded;
  1163. }
  1164. BOOL _Misc_HFGD_Init(LPVOID pv, LPVOID pvHFPL, LPCVOID pvParam2, BOOL fUnicode)
  1165. {
  1166. CFtpPidlList * pflHfpl = (CFtpPidlList *) pvHFPL;
  1167. URL_FILEGROUP urlFG = {0};
  1168. urlFG.pidlParent = (LPCITEMIDLIST) pvParam2;
  1169. if (fUnicode)
  1170. urlFG.pfgdW = (LPFILEGROUPDESCRIPTORW) pv;
  1171. else
  1172. urlFG.pfgdA = (LPFILEGROUPDESCRIPTORA) pv;
  1173. TraceMsg(TF_PIDLLIST_DUMP, "_Misc_HFGD_Init() TraceDump Before");
  1174. pflHfpl->TraceDump(NULL, TEXT("_Misc_HFGD_Init() TraceDump before"));
  1175. pflHfpl->Enum(_Misc_HFGD_Create, (LPVOID) &urlFG);
  1176. pflHfpl->TraceDump(NULL, TEXT("_Misc_HFGD_Init() TraceDump after"));
  1177. return 1;
  1178. }
  1179. HGLOBAL Misc_HFGD_Create(CFtpPidlList * pflHfpl, LPCITEMIDLIST pidlItem, BOOL fUnicode)
  1180. {
  1181. DWORD dwCount = pflHfpl->GetCount();
  1182. DWORD cbAllocSize = (fUnicode ? cbFgdCfdW(dwCount) : cbFgdCfdA(dwCount));
  1183. return AllocHGlob(cbAllocSize, _Misc_HFGD_Init, pflHfpl, (LPCVOID) pidlItem, fUnicode);
  1184. }
  1185. // Returns the submenu of the given menu and ID. Returns NULL if there
  1186. // is no submenu
  1187. int _MergePopupMenus(HMENU hmDest, HMENU hmSource, int idCmdFirst, int idCmdLast)
  1188. {
  1189. int i, idFinal = idCmdFirst;
  1190. for (i = GetMenuItemCount(hmSource) - 1; i >= 0; --i)
  1191. {
  1192. MENUITEMINFO mii;
  1193. mii.cbSize = SIZEOF(mii);
  1194. mii.fMask = MIIM_ID|MIIM_SUBMENU;
  1195. mii.cch = 0; // just in case
  1196. if (EVAL(GetMenuItemInfo(hmSource, i, TRUE, &mii)))
  1197. {
  1198. HMENU hmDestSub = GetMenuFromID(hmDest, mii.wID);
  1199. if (hmDestSub)
  1200. {
  1201. int idTemp = Shell_MergeMenus(hmDestSub, mii.hSubMenu, (UINT)0, idCmdFirst, idCmdLast, MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);
  1202. if (idFinal < idTemp)
  1203. idFinal = idTemp;
  1204. }
  1205. }
  1206. }
  1207. return idFinal;
  1208. }
  1209. /*****************************************************************************\
  1210. FUNCTION: AddToPopupMenu
  1211. DESCRIPTION:
  1212. Swiped from utils.c in RNAUI, in turn swiped from the ;Internal
  1213. shell. ;Internal
  1214. ;Internal
  1215. Takes a destination menu and a (menu id, submenu index) pair,
  1216. and inserts the items from the (menu id, submenu index) at location
  1217. imi in the destination menu, with a separator, returning the number
  1218. of items added. (imi = index to menu item)
  1219. Returns the first the number of items added.
  1220. hmenuDst - destination menu
  1221. idMenuToAdd - menu resource identifier
  1222. idSubMenuIndex - submenu from menu resource to act as template
  1223. indexMenu - location at which menu items should be inserted
  1224. idCmdFirst - first available menu identifier
  1225. idCmdLast - first unavailable menu identifier
  1226. uFlags - flags for Shell_MergeMenus
  1227. \*****************************************************************************/
  1228. #define FLAGS_MENUMERGE (MM_SUBMENUSHAVEIDS | MM_DONTREMOVESEPS)
  1229. UINT AddToPopupMenu(HMENU hmenuDst, UINT idMenuToAdd, UINT idSubMenuIndex, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  1230. {
  1231. UINT nLastItem = 0;
  1232. HMENU hmenuSrc = LoadMenu(g_hinst, MAKEINTRESOURCE(idMenuToAdd));
  1233. if (hmenuSrc)
  1234. {
  1235. nLastItem = Shell_MergeMenus(hmenuDst, GetSubMenu(hmenuSrc, idSubMenuIndex), indexMenu, idCmdFirst, idCmdLast, (uFlags | FLAGS_MENUMERGE));
  1236. DestroyMenu(hmenuSrc);
  1237. }
  1238. return nLastItem;
  1239. }
  1240. UINT MergeInToPopupMenu(HMENU hmenuDst, UINT idMenuToMerge, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  1241. {
  1242. UINT nLastItem = 0;
  1243. HMENU hmenuSrc = LoadMenu(g_hinst, MAKEINTRESOURCE(idMenuToMerge));
  1244. if (hmenuSrc)
  1245. {
  1246. nLastItem = _MergePopupMenus(hmenuDst, hmenuSrc, idCmdFirst, idCmdLast);
  1247. DestroyMenu(hmenuSrc);
  1248. }
  1249. return nLastItem;
  1250. }
  1251. /*****************************************************************************\
  1252. GetMenuFromID
  1253. Swiped from defviewx.c in the shell. ;Internal
  1254. ;Internal
  1255. Given an actual menu and a menu identifier which corresponds
  1256. to a submenu, return the submenu handle.
  1257. hmenu - source menu
  1258. idm - menu identifier
  1259. \*****************************************************************************/
  1260. HMENU GetMenuFromID(HMENU hmenu, UINT idm)
  1261. {
  1262. HMENU hmenuRet = NULL;
  1263. if (!hmenu)
  1264. return NULL;
  1265. MENUITEMINFO mii;
  1266. mii.cbSize = sizeof(mii);
  1267. mii.fMask = MIIM_SUBMENU;
  1268. mii.cch = 0; // just in case
  1269. mii.hSubMenu = 0; // in case GetMenuItemInfo fails
  1270. if (GetMenuItemInfo(hmenu, idm, 0, &mii))
  1271. hmenuRet = mii.hSubMenu;
  1272. return hmenuRet;
  1273. }
  1274. /*****************************************************************************\
  1275. MergeMenuHierarchy
  1276. Swiped from defcm.c in the shell. ;Internal
  1277. ;Internal
  1278. Given an actual menu (hmenuDst), iterate over its submenus
  1279. and merge corresponding submenus whose IDs match the IDs of
  1280. actuals.
  1281. hmenuDst - menu being adjusted
  1282. hmenuSrc - template menu
  1283. idcMin - first available index
  1284. idcMax - first unavailable index
  1285. \*****************************************************************************/
  1286. UINT MergeMenuHierarchy(HMENU hmenuDst, HMENU hmenuSrc, UINT idcMin, UINT idcMax)
  1287. {
  1288. int imi;
  1289. UINT idcMaxUsed = idcMin;
  1290. imi = GetMenuItemCount(hmenuSrc);
  1291. while (--imi >= 0)
  1292. {
  1293. UINT idcT;
  1294. MENUITEMINFO mii;
  1295. mii.cbSize = sizeof(mii);
  1296. mii.fMask = MIIM_ID|MIIM_SUBMENU;
  1297. mii.cch = 0; /* just in case */
  1298. if (GetMenuItemInfo(hmenuSrc, imi, 1, &mii))
  1299. {
  1300. idcT = Shell_MergeMenus(GetMenuFromID(hmenuDst, mii.wID),
  1301. mii.hSubMenu, (UINT)0, idcMin, idcMax,
  1302. MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);
  1303. idcMaxUsed = max(idcMaxUsed, idcT);
  1304. }
  1305. }
  1306. return idcMaxUsed;
  1307. }
  1308. HRESULT _SetStatusBarZone(CStatusBar * psb, CFtpSite * pfs)
  1309. {
  1310. if (EVAL(psb && pfs))
  1311. {
  1312. LPITEMIDLIST pidl = pfs->GetPidl();
  1313. if (pidl)
  1314. {
  1315. TCHAR szUrl[MAX_URL_STRING];
  1316. UrlCreateFromPidl(pidl, SHGDN_FORPARSING, szUrl, ARRAYSIZE(szUrl), 0, TRUE);
  1317. psb->UpdateZonesPane(szUrl);
  1318. ILFree(pidl);
  1319. }
  1320. }
  1321. return S_OK;
  1322. }
  1323. /*****************************************************************************\
  1324. Misc_CopyPidl
  1325. I wrote this on my own, and discovered months later ;Internal
  1326. that this is the same as SHILClone... ;Internal
  1327. ;Internal
  1328. \*****************************************************************************/
  1329. HRESULT Misc_CopyPidl(LPCITEMIDLIST pidl, LPITEMIDLIST * ppidlOut)
  1330. {
  1331. *ppidlOut = ILClone(pidl);
  1332. return *ppidlOut ? S_OK : E_OUTOFMEMORY;
  1333. }
  1334. /*****************************************************************************\
  1335. Misc_CloneHglobal
  1336. \*****************************************************************************/
  1337. HRESULT Misc_CloneHglobal(HGLOBAL hglob, HGLOBAL *phglob)
  1338. {
  1339. LPVOID pv;
  1340. HRESULT hres;
  1341. ASSERT(hglob);
  1342. *phglob = 0; /* Rules are rules */
  1343. pv = GlobalLock(hglob);
  1344. if (EVAL(pv))
  1345. {
  1346. hres = Misc_CreateHglob(GlobalSize(hglob), pv, phglob);
  1347. GlobalUnlock(hglob);
  1348. }
  1349. else
  1350. { /* Not a valid global handle */
  1351. hres = E_INVALIDARG;
  1352. }
  1353. return hres;
  1354. }
  1355. #define FTP_PROPPAGES_FROM_INETCPL (INET_PAGE_SECURITY | INET_PAGE_CONTENT | INET_PAGE_CONNECTION)
  1356. HRESULT AddFTPPropertyPages(LPFNADDPROPSHEETPAGE pfnAddPropSheetPage, LPARAM lParam, HINSTANCE * phinstInetCpl, IUnknown * punkSite)
  1357. {
  1358. HRESULT hr = E_FAIL;
  1359. if (NULL == *phinstInetCpl)
  1360. *phinstInetCpl = LoadLibrary(TEXT("inetcpl.cpl"));
  1361. // First add the pages from the Internet Control Panel.
  1362. if (*phinstInetCpl)
  1363. {
  1364. PFNADDINTERNETPROPERTYSHEETSEX pfnAddSheet = (PFNADDINTERNETPROPERTYSHEETSEX)GetProcAddress(*phinstInetCpl, STR_ADDINTERNETPROPSHEETSEX);
  1365. if (EVAL(pfnAddSheet))
  1366. {
  1367. IEPROPPAGEINFO iepi = {0};
  1368. iepi.cbSize = sizeof(iepi);
  1369. iepi.dwFlags = (DWORD)-1; // all pages
  1370. hr = pfnAddSheet(pfnAddPropSheetPage, lParam, 0, 0, &iepi);
  1371. }
  1372. // Don't FreeLibrary here, otherwise PropertyPage will GP-fault!
  1373. }
  1374. ASSERT(SUCCEEDED(hr));
  1375. if (((LPPROPSHEETHEADER)lParam)->nPages > 0)
  1376. return hr;
  1377. else
  1378. return S_FALSE;
  1379. }
  1380. #if 0
  1381. /*****************************************************************************\
  1382. Misc_SetDataDword
  1383. \*****************************************************************************/
  1384. HRESULT Misc_SetDataDword(IDataObject *pdto, FORMATETC *pfe, DWORD dw)
  1385. {
  1386. HRESULT hres;
  1387. HGLOBAL hglob;
  1388. hres = Misc_CreateHglob(sizeof(dw), &dw, &hglob);
  1389. if (SUCCEEDED(hres))
  1390. {
  1391. STGMEDIUM stg = { TYMED_HGLOBAL, hglob, 0 };
  1392. hres = pdto->SetData(&fe, &stg, 1);
  1393. if (!(EVAL(SUCCEEDED(hres))))
  1394. GlobalFree(hglob);
  1395. }
  1396. else
  1397. hres = E_OUTOFMEMORY;
  1398. return hres;
  1399. }
  1400. #endif
  1401. CFtpPidlList * CreateRelativePidlList(CFtpFolder * pff, CFtpPidlList * pPidlListFull)
  1402. {
  1403. int nSize = pPidlListFull->GetCount();
  1404. CFtpPidlList * pPidlListNew = NULL;
  1405. if (nSize > 0)
  1406. {
  1407. LPCITEMIDLIST pidlFirst = pff->GetPrivatePidlReference();
  1408. int nCount = 0;
  1409. while (!ILIsEmpty(pidlFirst))
  1410. {
  1411. pidlFirst = _ILNext(pidlFirst);
  1412. nCount++;
  1413. }
  1414. if (nSize > 0)
  1415. {
  1416. for (int nIndex = 0; nIndex < nSize; nIndex++)
  1417. {
  1418. int nLeft = nCount;
  1419. LPITEMIDLIST pidl = pPidlListFull->GetPidl(nIndex);
  1420. while (nLeft--)
  1421. pidl = _ILNext(pidl);
  1422. AssertMsg((pidl ? TRUE : FALSE), TEXT("CreateRelativePidlList() pPidlListFull->GetPidl() should never fail because we got the size and no mem allocation is needed."));
  1423. if (0 == nIndex)
  1424. {
  1425. CFtpPidlList_Create(1, (LPCITEMIDLIST *)&pidl, &pPidlListNew);
  1426. if (!pPidlListNew)
  1427. break;
  1428. }
  1429. else
  1430. {
  1431. // We only want to add top level nodes.
  1432. // ftp://s/d1/d2/ <- Root of copy.
  1433. // ftp://s/d1/d2/d3a/ <- First Top Level Item
  1434. // ftp://s/d1/d2/d3a/f1 <- Skip non-top level items
  1435. // ftp://s/d1/d2/d3b/ <- Second Top Level Item
  1436. if (pidl && !ILIsEmpty(pidl) && ILIsEmpty(_ILNext(pidl)))
  1437. pPidlListNew->InsertSorted(pidl);
  1438. }
  1439. }
  1440. }
  1441. }
  1442. return pPidlListNew;
  1443. }
  1444. #define SZ_VERB_DELETEA "delete"
  1445. /*****************************************************************************\
  1446. FUNCTION: Misc_DeleteHfpl
  1447. DESCRIPTION:
  1448. Delete the objects described by a pflHfpl.
  1449. \*****************************************************************************/
  1450. HRESULT Misc_DeleteHfpl(CFtpFolder * pff, HWND hwnd, CFtpPidlList * pflHfpl)
  1451. {
  1452. IContextMenu * pcm;
  1453. HRESULT hr = pff->GetUIObjectOfHfpl(hwnd, pflHfpl, IID_IContextMenu, (LPVOID *)&pcm, FALSE);
  1454. if (SUCCEEDED(hr))
  1455. {
  1456. CMINVOKECOMMANDINFO ici = {
  1457. sizeof(ici), // cbSize
  1458. CMIC_MASK_FLAG_NO_UI, // fMask
  1459. hwnd, // hwnd
  1460. SZ_VERB_DELETEA, // lpVerb
  1461. 0, // lpParameters
  1462. 0, // lpDirectory
  1463. 0, // nShow
  1464. 0, // dwHotKey
  1465. 0, // hIcon
  1466. };
  1467. hr = pcm->InvokeCommand(&ici);
  1468. pcm->Release();
  1469. }
  1470. else
  1471. {
  1472. // Couldn't delete source; oh well. Don't need UI because
  1473. // this should only happen in out of memory.
  1474. }
  1475. return hr;
  1476. }
  1477. /*****************************************************************************\
  1478. Misc_FindStatusBar
  1479. Get the status bar from a browser window.
  1480. _UNDOCUMENTED_: The following quirks are not documented.
  1481. Note that we need to be very paranoid about the way GetControlWindow
  1482. works. Some people (Desktop) properly return error if the window
  1483. does not exist. Others (Explorer) return S_OK when the window
  1484. does not exist, but they kindly set *lphwndOut = 0. Still others
  1485. (Find File) return S_OK but leave *lphwndOut unchanged!
  1486. In order to work with all these, we must manually set hwnd = 0
  1487. before calling, and continue only if GetControlWindow returns success
  1488. *and* the outgoing hwnd is nonzero.
  1489. Furthermore, the documentation for GetControlWindow says that we
  1490. have to check the window class before trusting the hwnd.
  1491. \*****************************************************************************/
  1492. #pragma BEGIN_CONST_DATA
  1493. TCHAR c_tszStatusBarClass[] = STATUSCLASSNAME;
  1494. #pragma END_CONST_DATA
  1495. HWND Misc_FindStatusBar(HWND hwndOwner)
  1496. {
  1497. HWND hwnd = 0; // Must preinit in case GetControlWindow fails
  1498. if (EVAL(hwndOwner))
  1499. {
  1500. IShellBrowser * psb = FileCabinet_GetIShellBrowser(hwndOwner);
  1501. if (psb)
  1502. {
  1503. if (SUCCEEDED(psb->GetControlWindow(FCW_STATUS, &hwnd)) && hwnd) // This won't work when hosted in an IFRAME
  1504. {
  1505. // Make sure it really is a status bar...
  1506. TCHAR tszClass[ARRAYSIZE(c_tszStatusBarClass)+1];
  1507. if (GetClassName(hwnd, tszClass, ARRAYSIZE(tszClass)) &&
  1508. !StrCmpI(tszClass, c_tszStatusBarClass))
  1509. {
  1510. // We have a winner
  1511. }
  1512. else
  1513. hwnd = 0; // False positive
  1514. }
  1515. }
  1516. }
  1517. return hwnd;
  1518. }
  1519. #ifdef DEBUG
  1520. void TraceMsgWithCurrentDir(DWORD dwTFOperation, LPCSTR pszMessage, HINTERNET hint)
  1521. {
  1522. // For debugging...
  1523. TCHAR szCurrentDir[MAX_PATH];
  1524. DWORD cchDebugSize = ARRAYSIZE(szCurrentDir);
  1525. DEBUG_CODE(DebugStartWatch());
  1526. // PERF: Status FtpGetCurrentDirectory/FtpSetCurrentDirectory() takes
  1527. // 180-280ms on ftp.microsoft.com on average.
  1528. // 500-2000ms on ftp://ftp.tu-clausthal.de/ on average
  1529. // 0-10ms on ftp://shapitst/ on average
  1530. EVAL(FtpGetCurrentDirectory(hint, szCurrentDir, &cchDebugSize));
  1531. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "TraceMsgWithCurrentDir() FtpGetCurrentDirectory() returned %ls and took %lu milliseconds", szCurrentDir, DebugStopWatch()));
  1532. TraceMsg(dwTFOperation, pszMessage, szCurrentDir);
  1533. }
  1534. void DebugStartWatch(void)
  1535. {
  1536. LARGE_INTEGER liStopWatchStart;
  1537. liStopWatchStart.HighPart = PtrToUlong(TlsGetValue(g_TLSliStopWatchStartHi));
  1538. liStopWatchStart.LowPart = PtrToUlong(TlsGetValue(g_TLSliStopWatchStartLo));
  1539. ASSERT(!liStopWatchStart.QuadPart); // If you hit this, then the stopwatch is nested.
  1540. QueryPerformanceFrequency(&g_liStopWatchFreq);
  1541. QueryPerformanceCounter(&liStopWatchStart);
  1542. TlsSetValue(g_TLSliStopWatchStartHi, UlongToPtr(liStopWatchStart.HighPart));
  1543. TlsSetValue(g_TLSliStopWatchStartLo, UlongToPtr(liStopWatchStart.LowPart));
  1544. }
  1545. DWORD DebugStopWatch(void)
  1546. {
  1547. LARGE_INTEGER liDiff;
  1548. LARGE_INTEGER liStopWatchStart;
  1549. QueryPerformanceCounter(&liDiff);
  1550. liStopWatchStart.HighPart = PtrToUlong(TlsGetValue(g_TLSliStopWatchStartHi));
  1551. liStopWatchStart.LowPart = PtrToUlong(TlsGetValue(g_TLSliStopWatchStartLo));
  1552. liDiff.QuadPart -= liStopWatchStart.QuadPart;
  1553. ASSERT(0 != g_liStopWatchFreq.QuadPart); // I don't like to fault with div 0.
  1554. DWORD dwTime = (DWORD)((liDiff.QuadPart * 1000) / g_liStopWatchFreq.QuadPart);
  1555. TlsSetValue(g_TLSliStopWatchStartHi, (LPVOID) 0);
  1556. TlsSetValue(g_TLSliStopWatchStartLo, (LPVOID) 0);
  1557. return dwTime;
  1558. }
  1559. #endif // DEBUG
  1560. /*****************************************************************************\
  1561. GetCfBuf
  1562. Convert a clipboard format name to something stringable.
  1563. \*****************************************************************************/
  1564. void GetCfBufA(UINT cf, LPSTR pszOut, int cchOut)
  1565. {
  1566. if (!GetClipboardFormatNameA(cf, pszOut, cchOut))
  1567. wnsprintfA(pszOut, cchOut, "[%04x]", cf);
  1568. }
  1569. /*****************************************************************************\
  1570. AllocHGlob
  1571. Allocate a moveable HGLOBAL of the requested size, lock it, then call
  1572. the callback. On return, unlock it and get out.
  1573. Returns the allocated HGLOBAL, or 0.
  1574. \*****************************************************************************/
  1575. HGLOBAL AllocHGlob(UINT cb, HGLOBWITHPROC pfn, LPVOID pvRef, LPCVOID pvParam2, BOOL fUnicode)
  1576. {
  1577. HGLOBAL hglob = GlobalAlloc(GHND, cb);
  1578. if (hglob)
  1579. {
  1580. LPVOID pv = GlobalLock(hglob);
  1581. if (pv)
  1582. {
  1583. BOOL fRc = pfn(pv, pvRef, pvParam2, fUnicode);
  1584. GlobalUnlock(hglob);
  1585. if (!fRc)
  1586. {
  1587. GlobalFree(hglob);
  1588. hglob = 0;
  1589. }
  1590. }
  1591. else
  1592. {
  1593. GlobalFree(hglob);
  1594. hglob = 0;
  1595. }
  1596. }
  1597. return hglob;
  1598. }
  1599. SHELL_VERSION g_ShellVersion = SHELL_VERSION_UNKNOWN;
  1600. #define SHELL_VERSION_FOR_WIN95_AND_NT4 4
  1601. SHELL_VERSION GetShellVersion(void)
  1602. {
  1603. if (SHELL_VERSION_UNKNOWN == g_ShellVersion)
  1604. {
  1605. g_ShellVersion = SHELL_VERSION_W95NT4;
  1606. HINSTANCE hInst = LoadLibrary(TEXT("shell32.dll"));
  1607. if (hInst)
  1608. {
  1609. DLLGETVERSIONPROC pfnDllGetVersion = (DLLGETVERSIONPROC) GetProcAddress(hInst, "DllGetVersion");
  1610. if (pfnDllGetVersion)
  1611. {
  1612. DLLVERSIONINFO dllVersionInfo;
  1613. g_ShellVersion = SHELL_VERSION_IE4; // Assume this.
  1614. dllVersionInfo.cbSize = sizeof(dllVersionInfo);
  1615. if (SUCCEEDED(pfnDllGetVersion(&dllVersionInfo)))
  1616. {
  1617. if (SHELL_VERSION_FOR_WIN95_AND_NT4 < dllVersionInfo.dwMajorVersion)
  1618. g_ShellVersion = SHELL_VERSION_NT5; // Assume this.
  1619. }
  1620. }
  1621. FreeLibrary(hInst);
  1622. }
  1623. }
  1624. return g_ShellVersion;
  1625. }
  1626. DWORD GetShdocvwVersion(void)
  1627. {
  1628. static DWORD majorVersion=0; // cache for perf
  1629. if (majorVersion)
  1630. return majorVersion;
  1631. HINSTANCE hInst = LoadLibrary(TEXT("shdocvw.dll"));
  1632. if (hInst)
  1633. {
  1634. DLLGETVERSIONPROC pfnDllGetVersion = (DLLGETVERSIONPROC) GetProcAddress(hInst, "DllGetVersion");
  1635. if (pfnDllGetVersion)
  1636. {
  1637. DLLVERSIONINFO dllVersionInfo;
  1638. dllVersionInfo.cbSize = sizeof(dllVersionInfo);
  1639. if (SUCCEEDED(pfnDllGetVersion(&dllVersionInfo)))
  1640. {
  1641. majorVersion = dllVersionInfo.dwMajorVersion;
  1642. }
  1643. }
  1644. FreeLibrary(hInst);
  1645. }
  1646. return majorVersion;
  1647. }
  1648. BOOL ShouldSkipDropFormat(int nIndex)
  1649. {
  1650. // Allow DROP_IDList or repositioning items withing
  1651. // ftp windows won't work.
  1652. /*
  1653. // We want to skip DROP_IDList on Win95 and WinNT4's shell
  1654. // because it will cause the old shell to only offer DROPEFFECT_LINK
  1655. // so download isn't available.
  1656. if (((DROP_IDList == nIndex)) &&
  1657. (SHELL_VERSION_W95NT4 == GetShellVersion()))
  1658. {
  1659. return TRUE;
  1660. }
  1661. */
  1662. #ifndef BROWSERONLY_DRAGGING
  1663. if (((DROP_FGDW == nIndex) || (DROP_FGDA == nIndex)) &&
  1664. (SHELL_VERSION_NT5 != GetShellVersion()))
  1665. {
  1666. return TRUE;
  1667. }
  1668. #endif // BROWSERONLY_DRAGGING
  1669. return FALSE;
  1670. }
  1671. void SetWindowBits(HWND hWnd, int iWhich, DWORD dwBits, DWORD dwValue)
  1672. {
  1673. DWORD dwStyle;
  1674. DWORD dwNewStyle;
  1675. dwStyle = GetWindowLong(hWnd, iWhich);
  1676. dwNewStyle = ( dwStyle & ~dwBits ) | (dwValue & dwBits);
  1677. if (dwStyle != dwNewStyle) {
  1678. SetWindowLong(hWnd, iWhich, dwNewStyle);
  1679. }
  1680. }
  1681. void InitComctlForNaviteFonts(void)
  1682. {
  1683. // hinst is ignored because we set it at our LibMain()
  1684. INITCOMMONCONTROLSEX icex = {0};
  1685. icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
  1686. icex.dwICC = ICC_USEREX_CLASSES|ICC_NATIVEFNTCTL_CLASS;
  1687. InitCommonControlsEx(&icex);
  1688. }
  1689. BOOL DoesUrlContainNTDomainName(LPCTSTR pszUrl)
  1690. {
  1691. BOOL fResult = FALSE;
  1692. LPCTSTR pszPointer = pszUrl;
  1693. if (lstrlen(pszPointer) > ARRAYSIZE(SZ_FTPURL))
  1694. {
  1695. pszPointer += ARRAYSIZE(SZ_FTPURL); // Skip past the scheme.
  1696. pszPointer = StrChr(pszPointer, CH_URL_SLASH);
  1697. if (pszPointer)
  1698. {
  1699. pszPointer = StrChr(CharNext(pszPointer), CH_URL_PASSWORD_SEPARATOR);
  1700. if (pszPointer)
  1701. {
  1702. pszPointer = StrChr(CharNext(pszPointer), CH_URL_LOGON_SEPARATOR);
  1703. if (pszPointer)
  1704. fResult = TRUE;
  1705. }
  1706. }
  1707. }
  1708. return fResult;
  1709. }
  1710. HRESULT CharReplaceWithStrW(LPWSTR pszLocToInsert, DWORD cchSize, DWORD cchChars, LPWSTR pszStrToInsert)
  1711. {
  1712. WCHAR szTemp[MAX_URL_STRING];
  1713. StrCpyNW(szTemp, pszLocToInsert, ARRAYSIZE(szTemp));
  1714. pszLocToInsert[0] = 0; // Terminate String here to kill char.
  1715. StrCatBuffW(pszLocToInsert, pszStrToInsert, cchSize);
  1716. StrCatBuffW(pszLocToInsert, &szTemp[cchChars], cchSize);
  1717. return S_OK;
  1718. }
  1719. HRESULT CharReplaceWithStrA(LPSTR pszLocToInsert, DWORD cchSize, DWORD cchChars, LPSTR pszStrToInsert)
  1720. {
  1721. CHAR szTemp[MAX_URL_STRING];
  1722. StrCpyNA(szTemp, pszLocToInsert, ARRAYSIZE(szTemp));
  1723. pszLocToInsert[0] = 0; // Terminate String here to kill char.
  1724. StrCatBuffA(pszLocToInsert, pszStrToInsert, cchSize);
  1725. StrCatBuffA(pszLocToInsert, &szTemp[cchChars], cchSize);
  1726. return S_OK;
  1727. }
  1728. HRESULT RemoveCharsFromString(LPTSTR pszLocToRemove, DWORD cchSizeToRemove)
  1729. {
  1730. LPTSTR pszRest = &pszLocToRemove[cchSizeToRemove];
  1731. MoveMemory((LPVOID) pszLocToRemove, (LPVOID) pszRest, (lstrlen(pszRest) + 1) * sizeof(TCHAR));
  1732. return S_OK;
  1733. }
  1734. HRESULT RemoveCharsFromStringA(LPSTR pszLocToRemove, DWORD cchSizeToRemove)
  1735. {
  1736. LPSTR pszRest = &pszLocToRemove[cchSizeToRemove];
  1737. MoveMemory((LPVOID) pszLocToRemove, (LPVOID) pszRest, (lstrlenA(pszRest) + 1) * sizeof(CHAR));
  1738. return S_OK;
  1739. }
  1740. // Helper function to convert Ansi string to allocated BSTR
  1741. #ifndef UNICODE
  1742. BSTR AllocBStrFromString(LPCTSTR psz)
  1743. {
  1744. OLECHAR wsz[INFOTIPSIZE]; // assumes INFOTIPSIZE number of chars max
  1745. SHAnsiToUnicode(psz, wsz, ARRAYSIZE(wsz));
  1746. return SysAllocString(wsz);
  1747. }
  1748. #endif // UNICODE
  1749. /****************************************************\
  1750. FUNCTION: StrListLength
  1751. DESCRIPTION:
  1752. \****************************************************/
  1753. DWORD StrListLength(LPCTSTR ppszStrList)
  1754. {
  1755. LPTSTR pszStr = (LPTSTR) ppszStrList;
  1756. DWORD cchLength = 0;
  1757. while (pszStr[0])
  1758. {
  1759. pszStr += (lstrlen(pszStr) + 1);
  1760. cchLength++;
  1761. }
  1762. return cchLength;
  1763. }
  1764. /****************************************************\
  1765. FUNCTION: CalcStrListSizeA
  1766. DESCRIPTION:
  1767. \****************************************************/
  1768. DWORD CalcStrListSizeA(LPCSTR ppszStrList)
  1769. {
  1770. LPSTR pszStr = (LPSTR) ppszStrList;
  1771. DWORD cchSize = 1;
  1772. while (pszStr[0])
  1773. {
  1774. DWORD cchSizeCurr = lstrlenA(pszStr) + 1;
  1775. cchSize += cchSizeCurr;
  1776. pszStr += cchSizeCurr;
  1777. }
  1778. return cchSize;
  1779. }
  1780. /****************************************************\
  1781. FUNCTION: CalcStrListSizeW
  1782. DESCRIPTION:
  1783. \****************************************************/
  1784. DWORD CalcStrListSizeW(LPCWSTR ppwzStrList)
  1785. {
  1786. LPWSTR pwzStr = (LPWSTR) ppwzStrList;
  1787. DWORD cchSize = 1;
  1788. while (pwzStr[0])
  1789. {
  1790. DWORD cchSizeCurr = lstrlenW(pwzStr) + 1;
  1791. cchSize += cchSizeCurr;
  1792. pwzStr += cchSizeCurr;
  1793. }
  1794. return cchSize;
  1795. }
  1796. /****************************************************\
  1797. FUNCTION: AnsiToUnicodeStrList
  1798. DESCRIPTION:
  1799. \****************************************************/
  1800. void AnsiToUnicodeStrList(LPCSTR ppszStrListIn, LPCWSTR ppwzStrListOut, DWORD cchSize)
  1801. {
  1802. LPWSTR pwzStrOut = (LPWSTR) ppwzStrListOut;
  1803. LPSTR pszStrIn = (LPSTR) ppszStrListIn;
  1804. while (pszStrIn[0])
  1805. {
  1806. SHAnsiToUnicode(pszStrIn, pwzStrOut, lstrlenA(pszStrIn) + 2);
  1807. pszStrIn += lstrlenA(pszStrIn) + 1;
  1808. pwzStrOut += lstrlenW(pwzStrOut) + 1;
  1809. }
  1810. pwzStrOut[0] = L'\0';
  1811. }
  1812. /****************************************************\
  1813. FUNCTION: UnicodeToAnsiStrList
  1814. DESCRIPTION:
  1815. \****************************************************/
  1816. void UnicodeToAnsiStrList(LPCWSTR ppwzStrListIn, LPCSTR ppszStrListOut, DWORD cchSize)
  1817. {
  1818. LPSTR pszStrOut = (LPSTR) ppszStrListOut;
  1819. LPWSTR pwzStrIn = (LPWSTR) ppwzStrListIn;
  1820. while (pwzStrIn[0])
  1821. {
  1822. SHUnicodeToAnsi(pwzStrIn, pszStrOut, lstrlenW(pwzStrIn) + 2);
  1823. pwzStrIn += lstrlenW(pwzStrIn) + 1;
  1824. pszStrOut += lstrlenA(pszStrOut) + 1;
  1825. }
  1826. pszStrOut[0] = '\0';
  1827. }
  1828. /****************************************************\
  1829. FUNCTION: Str_StrAndThunkA
  1830. DESCRIPTION:
  1831. \****************************************************/
  1832. HRESULT Str_StrAndThunkA(LPTSTR * ppszOut, LPCSTR pszIn, BOOL fStringList)
  1833. {
  1834. #ifdef UNICODE
  1835. if (!fStringList)
  1836. {
  1837. DWORD cchSize = (lstrlenA(pszIn) + 2);
  1838. LPWSTR pwzBuffer = (LPWSTR) LocalAlloc(LPTR, cchSize * SIZEOF(WCHAR));
  1839. if (!pwzBuffer)
  1840. return E_OUTOFMEMORY;
  1841. SHAnsiToUnicode(pszIn, pwzBuffer, cchSize);
  1842. Str_SetPtrW(ppszOut, pwzBuffer);
  1843. }
  1844. else
  1845. {
  1846. DWORD cchSize = CalcStrListSizeA(pszIn);
  1847. Str_SetPtrW(ppszOut, NULL); // Free
  1848. *ppszOut = (LPTSTR) LocalAlloc(LPTR, cchSize * sizeof(WCHAR));
  1849. if (*ppszOut)
  1850. AnsiToUnicodeStrList(pszIn, *ppszOut, cchSize);
  1851. }
  1852. #else // UNICODE
  1853. if (!fStringList)
  1854. {
  1855. // No thunking needed.
  1856. Str_SetPtrA(ppszOut, pszIn);
  1857. }
  1858. else
  1859. {
  1860. DWORD cchSize = CalcStrListSizeA(pszIn);
  1861. Str_SetPtrA(ppszOut, NULL); // Free
  1862. *ppszOut = (LPTSTR) LocalAlloc(LPTR, cchSize * sizeof(CHAR));
  1863. if (*ppszOut)
  1864. CopyMemory(*ppszOut, pszIn, cchSize * sizeof(CHAR));
  1865. }
  1866. #endif // UNICODE
  1867. return S_OK;
  1868. }
  1869. BOOL IsValidFtpAnsiFileName(LPCTSTR pszString)
  1870. {
  1871. #ifdef UNICODE
  1872. // TODO:
  1873. #endif // UNICODE
  1874. return TRUE;
  1875. }
  1876. /****************************************************\
  1877. FUNCTION: Str_StrAndThunkW
  1878. DESCRIPTION:
  1879. \****************************************************/
  1880. HRESULT Str_StrAndThunkW(LPTSTR * ppszOut, LPCWSTR pwzIn, BOOL fStringList)
  1881. {
  1882. #ifdef UNICODE
  1883. if (!fStringList)
  1884. {
  1885. // No thunking needed.
  1886. Str_SetPtrW(ppszOut, pwzIn);
  1887. }
  1888. else
  1889. {
  1890. DWORD cchSize = CalcStrListSizeW(pwzIn);
  1891. Str_SetPtrW(ppszOut, NULL); // Free
  1892. *ppszOut = (LPTSTR) LocalAlloc(LPTR, cchSize * sizeof(WCHAR));
  1893. if (*ppszOut)
  1894. CopyMemory(*ppszOut, pwzIn, cchSize * sizeof(WCHAR));
  1895. }
  1896. #else // UNICODE
  1897. if (!fStringList)
  1898. {
  1899. DWORD cchSize = (lstrlenW(pwzIn) + 2);
  1900. LPSTR pszBuffer = (LPSTR) LocalAlloc(LPTR, cchSize * SIZEOF(CHAR));
  1901. if (!pszBuffer)
  1902. return E_OUTOFMEMORY;
  1903. SHUnicodeToAnsi(pwzIn, pszBuffer, cchSize);
  1904. Str_SetPtrA(ppszOut, pszBuffer);
  1905. }
  1906. else
  1907. {
  1908. DWORD cchSize = CalcStrListSizeW(pwzIn);
  1909. Str_SetPtrA(ppszOut, NULL); // Free
  1910. *ppszOut = (LPTSTR) LocalAlloc(LPTR, cchSize * sizeof(CHAR));
  1911. if (*ppszOut)
  1912. UnicodeToAnsiStrList(pwzIn, *ppszOut, cchSize * sizeof(CHAR));
  1913. }
  1914. #endif // UNICODE
  1915. return S_OK;
  1916. }
  1917. #ifndef UNICODE
  1918. // TruncateString
  1919. //
  1920. // purpose: cut a string at the given length in dbcs safe manner.
  1921. // the string may be truncated at cch-2 if the sz[cch] points
  1922. // to a lead byte that would result in cutting in the middle
  1923. // of double byte character.
  1924. //
  1925. // update: made it faster for sbcs environment (5/26/97)
  1926. // now returns adjusted cch (6/20/97)
  1927. //
  1928. void TruncateString(char *sz, int cchBufferSize)
  1929. {
  1930. if (!sz || cchBufferSize <= 0) return;
  1931. int cch = cchBufferSize - 1; // get index position to NULL out
  1932. LPSTR psz = &sz[cch];
  1933. while (psz >sz)
  1934. {
  1935. psz--;
  1936. if (!IsDBCSLeadByte(*psz))
  1937. {
  1938. // Found non-leadbyte for the first time.
  1939. // This is either a trail byte of double byte char
  1940. // or a single byte character we've first seen.
  1941. // Thus, the next pointer must be at either of a leadbyte
  1942. // or &sz[cch]
  1943. psz++;
  1944. break;
  1945. }
  1946. }
  1947. if (((&sz[cch] - psz) & 1) && cch > 0)
  1948. {
  1949. // we're truncating the string in the middle of dbcs
  1950. cch--;
  1951. }
  1952. sz[cch] = '\0';
  1953. return;
  1954. }
  1955. #endif // UNICODE
  1956. HRESULT CopyStgMediumWrap(const STGMEDIUM * pcstgmedSrc, STGMEDIUM * pstgmedDest)
  1957. {
  1958. HRESULT hr = CopyStgMedium(pcstgmedSrc, pstgmedDest);
  1959. // if pstgmedDest->pUnkForElease is NULL,
  1960. // then we need to free hglobal because we own freeing the memory.
  1961. // else someone else owns the lifetime of the memory and releasing
  1962. // pUnkForElease is the way to indicate that we won't use it anymore.
  1963. //
  1964. // The problem is that urlmon's CopyStgMedium() ERRouniously copies the
  1965. // pUnkForElease param in addition to cloning the memory. This means
  1966. // that we own freeing the memory but the pointer being non-NULL would
  1967. // indicate that we don't own freeing the memory.
  1968. // ASSERT(NULL == pstgmedDest->pUnkForElease);
  1969. pstgmedDest->pUnkForRelease = NULL;
  1970. return hr;
  1971. }
  1972. HRESULT SHBindToIDList(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  1973. {
  1974. IShellFolder * psf;
  1975. HRESULT hr = SHGetDesktopFolder(&psf);
  1976. if (SUCCEEDED(hr))
  1977. {
  1978. hr = psf->BindToObject(pidl, pbc, riid, ppv);
  1979. psf->Release();
  1980. }
  1981. return hr;
  1982. }
  1983. STDAPI DataObj_GetDropTarget(IDataObject *pdtobj, CLSID *pclsid)
  1984. {
  1985. STGMEDIUM medium;
  1986. FORMATETC fmte = {(CLIPFORMAT) g_cfTargetCLSID, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  1987. HRESULT hr = pdtobj->GetData(&fmte, &medium);
  1988. if (SUCCEEDED(hr))
  1989. {
  1990. CLSID *pdw = (CLSID *)GlobalLock(medium.hGlobal);
  1991. if (pdw)
  1992. {
  1993. *pclsid = *pdw;
  1994. GlobalUnlock(medium.hGlobal);
  1995. }
  1996. else
  1997. {
  1998. hr = E_UNEXPECTED;
  1999. }
  2000. ReleaseStgMedium(&medium);
  2001. }
  2002. return hr;
  2003. }
  2004. STDAPI DataObj_SetPreferredEffect(IDataObject *pdtobj, DWORD dwEffect)
  2005. {
  2006. return DataObj_SetDWORD(pdtobj, g_dropTypes[DROP_PrefDe].cfFormat, dwEffect);
  2007. }
  2008. STDAPI DataObj_SetPasteSucceeded(IDataObject *pdtobj, DWORD dwEffect)
  2009. {
  2010. return DataObj_SetDWORD(pdtobj, g_formatPasteSucceeded.cfFormat, dwEffect);
  2011. }
  2012. /****************************************************\
  2013. FUNCTION: ShowEnableWindow
  2014. DESCRIPTION:
  2015. If you don't want a window to be visible or
  2016. usable by the user, you need to call both
  2017. ShowWindow(SW_HIDE) and EnableWindow(FALSE) or
  2018. the window may be hidden but still accessible via
  2019. the keyboard.
  2020. \****************************************************/
  2021. void ShowEnableWindow(HWND hwnd, BOOL fShow)
  2022. {
  2023. ShowWindow(hwnd, (fShow ? SW_SHOW : SW_HIDE));
  2024. EnableWindow(hwnd, fShow);
  2025. }
  2026. STDAPI StringToStrRetW(LPCWSTR pwzString, STRRET *pstrret)
  2027. {
  2028. HRESULT hr = SHStrDupW(pwzString, &pstrret->pOleStr);
  2029. if (SUCCEEDED(hr))
  2030. {
  2031. pstrret->uType = STRRET_WSTR;
  2032. }
  2033. return hr;
  2034. }
  2035. #define BIT_8_SET 0x80
  2036. BOOL Is7BitAnsi(LPCWIRESTR pwByteStr)
  2037. {
  2038. BOOL fIs7BitAnsi = TRUE;
  2039. if (pwByteStr)
  2040. {
  2041. while (pwByteStr[0])
  2042. {
  2043. if (BIT_8_SET & pwByteStr[0])
  2044. {
  2045. fIs7BitAnsi = FALSE;
  2046. break;
  2047. }
  2048. pwByteStr++;
  2049. }
  2050. }
  2051. return fIs7BitAnsi;
  2052. }
  2053. HRESULT LoginAs(HWND hwnd, CFtpFolder * pff, CFtpDir * pfd, IUnknown * punkSite)
  2054. {
  2055. HRESULT hr = E_FAIL;
  2056. CFtpSite * pfs = pfd->GetFtpSite();
  2057. ASSERT(hwnd && pff);
  2058. if (pfs)
  2059. {
  2060. CAccounts cAccounts;
  2061. TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH];
  2062. TCHAR szUser[INTERNET_MAX_USER_NAME_LENGTH];
  2063. TCHAR szPassword[INTERNET_MAX_PASSWORD_LENGTH];
  2064. LPCITEMIDLIST pidlPrevious = pfd->GetPidlReference();
  2065. pfs->GetServer(szServer, ARRAYSIZE(szServer));
  2066. pfs->GetUser(szUser, ARRAYSIZE(szUser));
  2067. pfs->GetPassword(szPassword, ARRAYSIZE(szPassword));
  2068. hr = cAccounts.DisplayLoginDialog(hwnd, LOGINFLAGS_DEFAULT, szServer, szUser, ARRAYSIZE(szUser), szPassword, ARRAYSIZE(szPassword));
  2069. if (S_OK == hr)
  2070. {
  2071. LPITEMIDLIST pidlNew;
  2072. ASSERT(pff->GetItemAllocatorDirect());
  2073. hr = PidlReplaceUserPassword(pidlPrevious, &pidlNew, pff->GetItemAllocatorDirect(), szUser, szPassword);
  2074. if (SUCCEEDED(hr))
  2075. {
  2076. CFtpSite * pfs;
  2077. LPITEMIDLIST pidlRedirect;
  2078. // We need to update the password in the site to redirect to the correct or new one.
  2079. if (EVAL(SUCCEEDED(PidlReplaceUserPassword(pidlNew, &pidlRedirect, pff->GetItemAllocatorDirect(), szUser, TEXT(""))) &&
  2080. SUCCEEDED(SiteCache_PidlLookup(pidlRedirect, TRUE, pff->GetItemAllocatorDirect(), &pfs))))
  2081. {
  2082. EVAL(SUCCEEDED(pfs->SetRedirPassword(szPassword)));
  2083. pfs->Release();
  2084. ILFree(pidlRedirect);
  2085. }
  2086. // pidl is a full private pidl. pidlFull will be a full public pidl because
  2087. // that's what the browser needs to get back from the root of THE public
  2088. // name space back to and into us.
  2089. LPITEMIDLIST pidlFull = pff->CreateFullPublicPidl(pidlNew);
  2090. if (pidlFull)
  2091. {
  2092. hr = IUnknown_PidlNavigate(punkSite, pidlFull, TRUE);
  2093. ILFree(pidlFull);
  2094. }
  2095. else
  2096. hr = E_FAIL;
  2097. ILFree(pidlNew);
  2098. }
  2099. }
  2100. }
  2101. return hr;
  2102. }
  2103. HRESULT LoginAsViaFolder(HWND hwnd, CFtpFolder * pff, IUnknown * punkSite)
  2104. {
  2105. HRESULT hr = E_FAIL;
  2106. CFtpDir * pfd = pff->GetFtpDir();
  2107. if (pfd)
  2108. {
  2109. hr = LoginAs(hwnd, pff, pfd, punkSite);
  2110. pfd->Release();
  2111. }
  2112. return hr;
  2113. }
  2114. #define PATH_IS_DRIVE(wzPath) (-1 != PathGetDriveNumberW(wzPath))
  2115. HRESULT SHPathPrepareForWriteWrapW(HWND hwnd, IUnknown *punkEnableModless, LPCWSTR pwzPath, UINT wFunc, DWORD dwFlags)
  2116. {
  2117. HRESULT hr = S_OK;
  2118. if (SHELL_VERSION_NT5 == GetShellVersion())
  2119. {
  2120. // NT5's version of the API is better.
  2121. hr = _SHPathPrepareForWriteW(hwnd, punkEnableModless, pwzPath, dwFlags);
  2122. }
  2123. else
  2124. {
  2125. if (PATH_IS_DRIVE(pwzPath))
  2126. {
  2127. hr = (SHCheckDiskForMediaW(hwnd, punkEnableModless, pwzPath, wFunc) ? S_OK : E_FAIL);
  2128. }
  2129. else
  2130. {
  2131. if (PathIsUNCW(pwzPath))
  2132. {
  2133. hr = (PathFileExistsW(pwzPath) ? S_OK : E_FAIL);
  2134. }
  2135. }
  2136. }
  2137. return hr;
  2138. }
  2139. // Helper function
  2140. int _LoadStringW(HINSTANCE hinst, UINT id, LPWSTR wsz, UINT cchMax)
  2141. {
  2142. char szT[512];
  2143. if (LoadStringA(hinst, id, szT, ARRAYSIZE(szT)))
  2144. {
  2145. TraceMsg(0, "LoadStringW just loaded (%s)", szT);
  2146. return SHAnsiToUnicode(szT, wsz, cchMax) - 1; // -1 for terminator
  2147. }
  2148. else
  2149. {
  2150. TraceMsg(DM_TRACE, "sdv TR LoadStringW(%x) failed", id);
  2151. wsz[0] = L'\0';
  2152. }
  2153. return 0;
  2154. }
  2155. HRESULT HrShellExecute(HWND hwnd, LPCTSTR lpVerb, LPCTSTR lpFile, LPCTSTR lpParameters, LPCTSTR lpDirectory, INT nShowCmd)
  2156. {
  2157. HRESULT hr = S_OK;
  2158. HINSTANCE hReturn = ShellExecute(hwnd, lpVerb, lpFile, lpParameters, lpDirectory, nShowCmd);
  2159. if ((HINSTANCE)32 > hReturn)
  2160. {
  2161. hr = ResultFromLastError();
  2162. }
  2163. return hr;
  2164. }