1 /*
2  * Copyright 2003, 2004, 2005, 2006 Martin Fuchs
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 
19 
20  //
21  // Explorer clone
22  //
23  // shellfs.cpp
24  //
25  // Martin Fuchs, 23.07.2003
26  //
27 
28 
29 #include <precomp.h>
30 
fill_w32fdata_shell(LPCITEMIDLIST pidl,SFGAOF attribs,WIN32_FIND_DATA * pw32fdata,BY_HANDLE_FILE_INFORMATION * pbhfi,bool do_access)31 bool ShellDirectory::fill_w32fdata_shell(LPCITEMIDLIST pidl, SFGAOF attribs, WIN32_FIND_DATA* pw32fdata, BY_HANDLE_FILE_INFORMATION* pbhfi, bool do_access)
32 {
33 	CONTEXT("ShellDirectory::fill_w32fdata_shell()");
34 
35 	bool bhfi_valid = false;
36 
37 	if (do_access && !( (attribs&SFGAO_FILESYSTEM) && SUCCEEDED(
38 				SHGetDataFromIDList(_folder, pidl, SHGDFIL_FINDDATA, pw32fdata, sizeof(WIN32_FIND_DATA))) )) {
39 		WIN32_FILE_ATTRIBUTE_DATA fad;
40 		IDataObject* pDataObj;
41 
42 		STGMEDIUM medium = {0, {0}, 0};
43 		FORMATETC fmt = {(USHORT)g_Globals._cfStrFName, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
44 
45 		HRESULT hr = _folder->GetUIObjectOf(0, 1, &pidl, IID_IDataObject, 0, (LPVOID*)&pDataObj);
46 
47 		if (SUCCEEDED(hr)) {
48 			hr = pDataObj->GetData(&fmt, &medium);
49 
50 			pDataObj->Release();
51 
52 			if (SUCCEEDED(hr)) {
53 				LPCTSTR path = (LPCTSTR)GlobalLock(medium.UNION_MEMBER(hGlobal));
54 
55 				if (path) {
56 					 // fill with drive names "C:", ...
57 					assert(_tcslen(path) < GlobalSize(medium.UNION_MEMBER(hGlobal)));
58 					_tcscpy(pw32fdata->cFileName, path);
59 
60 					UINT sem_org = SetErrorMode(SEM_FAILCRITICALERRORS);
61 
62 					if (GetFileAttributesEx(path, GetFileExInfoStandard, &fad)) {
63 						pw32fdata->dwFileAttributes = fad.dwFileAttributes;
64 						pw32fdata->ftCreationTime = fad.ftCreationTime;
65 						pw32fdata->ftLastAccessTime = fad.ftLastAccessTime;
66 						pw32fdata->ftLastWriteTime = fad.ftLastWriteTime;
67 
68 						if (!(fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
69 							 // copy file size
70 							pw32fdata->nFileSizeLow = fad.nFileSizeLow;
71 							pw32fdata->nFileSizeHigh = fad.nFileSizeHigh;
72 						} else {
73 							 // ignore FILE_ATTRIBUTE_HIDDEN attribute of NTFS drives - this would hide those drives in ShellBrowser
74 							if (pw32fdata->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
75 								if (path[1]==':' && path[2]=='\\' && !path[3])	// Is it a drive path?
76 									pw32fdata->dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
77 							}
78 						}
79 					}
80 
81 					HANDLE hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
82 												0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
83 
84 					if (hFile != INVALID_HANDLE_VALUE) {
85 						if (GetFileInformationByHandle(hFile, pbhfi))
86 							bhfi_valid = true;
87 
88 						CloseHandle(hFile);
89 					}
90 
91 					SetErrorMode(sem_org);
92 
93 					GlobalUnlock(medium.UNION_MEMBER(hGlobal));
94 					GlobalFree(medium.UNION_MEMBER(hGlobal));
95 				}
96 			}
97 		}
98 	}
99 
100 	if (!do_access || !(attribs&SFGAO_FILESYSTEM))	// Archiv files should not be displayed as folders in explorer view.
101 		if (attribs & (SFGAO_FOLDER|SFGAO_HASSUBFOLDER))
102 			pw32fdata->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
103 
104 	if (attribs & SFGAO_READONLY)
105 		pw32fdata->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
106 
107 	if (attribs & SFGAO_COMPRESSED)
108 		pw32fdata->dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED;
109 
110 	return bhfi_valid;
111 }
112 
113 
create_absolute_pidl() const114 ShellPath ShellEntry::create_absolute_pidl() const
115 {
116 	CONTEXT("ShellEntry::create_absolute_pidl()");
117 
118 	if (_up)
119 	{
120 		if (_up->_etype == ET_SHELL) {
121 			ShellDirectory* dir = static_cast<ShellDirectory*>(_up);
122 
123 			if (dir->_pidl->mkid.cb)	// Caching of absolute PIDLs could enhance performance.
124 				return _pidl.create_absolute_pidl(dir->create_absolute_pidl());
125 		} else
126 			return _pidl.create_absolute_pidl(_up->create_absolute_pidl());
127 	}
128 	return _pidl;
129 }
130 
131 
132  // get full path of a shell entry
get_path(PTSTR path,size_t path_count) const133 bool ShellEntry::get_path(PTSTR path, size_t path_count) const
134 {
135 	if (!path || path_count==0)
136 		return false;
137 /*
138 	path[0] = TEXT('\0');
139 
140 	if (FAILED(path_from_pidl(get_parent_folder(), &*_pidl, path, path_count)))
141 		return false;
142 */
143 	FileSysShellPath fs_path(create_absolute_pidl());
144 	LPCTSTR ret = fs_path;
145 
146 	if (ret) {
147 		lstrcpyn(path, ret, path_count);
148 		return true;
149 	} else
150 		return false;
151 }
152 
153 
154  // get full path of a shell folder
get_path(PTSTR path,size_t path_count) const155 bool ShellDirectory::get_path(PTSTR path, size_t path_count) const
156 {
157 	CONTEXT("ShellDirectory::get_path()");
158 
159 	if (!path || path_count==0)
160 		return false;
161 
162 	path[0] = TEXT('\0');
163 
164 	if (_folder.empty())
165 		return false;
166 
167 	SFGAOF attribs = SFGAO_FILESYSTEM;
168 
169 	// Split pidl into current and parent folder PIDLs
170 	ShellPath pidlParent, pidlFolder;
171 	_pidl.split(pidlParent, pidlFolder);
172 
173 	if (FAILED(const_cast<ShellFolder&>(_folder)->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlFolder, &attribs)))
174 		return false;
175 
176 	if (!(attribs & SFGAO_FILESYSTEM))
177 		return false;
178 
179 	if (FAILED(path_from_pidl(get_parent_folder(), &*_pidl, path, path_count)))
180 		return false;
181 
182 	return true;
183 }
184 
185 
launch_entry(HWND hwnd,UINT nCmdShow)186 BOOL ShellEntry::launch_entry(HWND hwnd, UINT nCmdShow)
187 {
188 	CONTEXT("ShellEntry::launch_entry()");
189 
190 	SHELLEXECUTEINFO shexinfo;
191 
192 	shexinfo.cbSize = sizeof(SHELLEXECUTEINFO);
193 	shexinfo.fMask = SEE_MASK_INVOKEIDLIST;	// SEE_MASK_IDLIST is also possible.
194 	shexinfo.hwnd = hwnd;
195 	shexinfo.lpVerb = NULL;
196 	shexinfo.lpFile = NULL;
197 	shexinfo.lpParameters = NULL;
198 	shexinfo.lpDirectory = NULL;
199 	shexinfo.nShow = nCmdShow;
200 
201 	ShellPath shell_path = create_absolute_pidl();
202 	shexinfo.lpIDList = &*shell_path;
203 
204 	 // add PIDL to the recent file list
205 	SHAddToRecentDocs(SHARD_PIDL, shexinfo.lpIDList);
206 
207 	BOOL ret = TRUE;
208 
209 	if (!ShellExecuteEx(&shexinfo)) {
210 		display_error(hwnd, GetLastError());
211 		ret = FALSE;
212 	}
213 
214 	return ret;
215 }
216 
217 
do_context_menu(HWND hwnd,const POINT & pptScreen,CtxMenuInterfaces & cm_ifs)218 HRESULT ShellEntry::do_context_menu(HWND hwnd, const POINT& pptScreen, CtxMenuInterfaces& cm_ifs)
219 {
220 	ShellDirectory* dir = static_cast<ShellDirectory*>(_up);
221 
222 	ShellFolder folder = dir? dir->_folder: GetDesktopFolder();
223 	LPCITEMIDLIST pidl = _pidl;
224 
225 	return ShellFolderContextMenu(folder, hwnd, 1, &pidl, pptScreen.x, pptScreen.y, cm_ifs);
226 }
227 
228 
GetUIObjectOf(HWND hWnd,REFIID riid,LPVOID * ppvOut)229 HRESULT ShellEntry::GetUIObjectOf(HWND hWnd, REFIID riid, LPVOID* ppvOut)
230 {
231 	LPCITEMIDLIST pidl = _pidl;
232 
233 	return get_parent_folder()->GetUIObjectOf(hWnd, 1, &pidl, riid, NULL, ppvOut);
234 }
235 
236 
get_shell_folder() const237 ShellFolder Entry::get_shell_folder() const
238 {
239 	return ShellFolder(create_absolute_pidl());
240 }
241 
get_shell_folder() const242 ShellFolder ShellEntry::get_shell_folder() const
243 {
244 	return get_parent_folder();
245 }
246 
get_shell_folder() const247 ShellFolder ShellDirectory::get_shell_folder() const
248 {
249 	return _folder;
250 }
251 
252 
read_directory(int scan_flags)253 void ShellDirectory::read_directory(int scan_flags)
254 {
255 	CONTEXT("ShellDirectory::read_directory()");
256 
257 	int level = _level + 1;
258 
259 	Entry* first_entry = NULL;
260 	Entry* last = NULL;
261 
262 	/*if (_folder.empty())
263 		return;*/
264 
265 #ifndef _NO_WIN_FS
266 	TCHAR buffer[_MAX_PATH+_MAX_FNAME];
267 
268 	if (!(scan_flags&SCAN_NO_FILESYSTEM) && get_path(buffer, COUNTOF(buffer)) && _tcsncmp(buffer,TEXT("::{"),3)) {
269 		Entry* entry = NULL;	// eliminate useless GCC warning by initializing entry
270 
271 		LPTSTR p = buffer + _tcslen(buffer);
272 
273 		lstrcpy(p, TEXT("\\*"));
274 
275 		WIN32_FIND_DATA w32fd;
276 		HANDLE hFind = FindFirstFile(buffer, &w32fd);
277 
278 		if (hFind != INVALID_HANDLE_VALUE) {
279 			do {
280 				 // ignore hidden files (usefull in the start menu)
281 				if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
282 					continue;
283 
284 				 // ignore directory entries "." and ".."
285 				if ((w32fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) &&
286 					w32fd.cFileName[0]==TEXT('.') &&
287 					(w32fd.cFileName[1]==TEXT('\0') ||
288 					(w32fd.cFileName[1]==TEXT('.') && w32fd.cFileName[2]==TEXT('\0'))))
289 					continue;
290 
291 				lstrcpy(p+1, w32fd.cFileName);
292 
293 				if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
294 					entry = new WinDirectory(this, buffer);
295 				else
296 					entry = new WinEntry(this);
297 
298 				if (!first_entry)
299 					first_entry = entry;
300 
301 				if (last)
302 					last->_next = entry;
303 
304 				memcpy(&entry->_data, &w32fd, sizeof(WIN32_FIND_DATA));
305 
306 				entry->_level = level;
307 
308 				if (!(scan_flags & SCAN_DONT_ACCESS)) {
309 					HANDLE hFile = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
310 												0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
311 
312 					if (hFile != INVALID_HANDLE_VALUE) {
313 						if (GetFileInformationByHandle(hFile, &entry->_bhfi))
314 							entry->_bhfi_valid = true;
315 
316 #ifdef BACKUP_READ_IMPLEMENTED
317 						if (ScanNTFSStreams(entry, hFile))
318 							entry->_scanned = true;	// There exist named NTFS sub-streams in this file.
319 #endif
320 
321 						CloseHandle(hFile);
322 					}
323 				}
324 
325 				 // set file type name
326 				LPCTSTR ext = g_Globals._ftype_mgr.set_type(entry);
327 
328 				DWORD attribs = SFGAO_FILESYSTEM;
329 
330 				if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
331 					attribs |= SFGAO_FOLDER|SFGAO_HASSUBFOLDER;
332 
333 				if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
334 					attribs |= SFGAO_READONLY;
335 
336 				//if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
337 				//	attribs |= SFGAO_HIDDEN;
338 
339 				if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED)
340 					attribs |= SFGAO_COMPRESSED;
341 
342 				if (ext && !_tcsicmp(ext, _T(".lnk"))) {
343 					attribs |= SFGAO_LINK;
344 					w32fd.dwFileAttributes |= ATTRIBUTE_SYMBOLIC_LINK;
345 				}
346 
347 				entry->_shell_attribs = attribs;
348 
349 				if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
350 					entry->_icon_id = ICID_FOLDER;
351 				else if (!(scan_flags & SCAN_DONT_EXTRACT_ICONS))
352 					entry->_icon_id = entry->safe_extract_icon();	// Assume small icon, we can extract the large icon later on demand.
353 
354 				last = entry;
355 			} while(FindNextFile(hFind, &w32fd));
356 
357 			FindClose(hFind);
358 		}
359 	}
360 	else // SCAN_NO_FILESYSTEM
361 #endif
362 	{
363 		ShellItemEnumerator enumerator(_folder, SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN|SHCONTF_SHAREABLE|SHCONTF_STORAGE);
364 
365 		TCHAR name[MAX_PATH];
366 		TCHAR path[MAX_PATH];
367 		HRESULT hr_next = S_OK;
368 
369 		do {
370 #define FETCH_ITEM_COUNT	32
371 			LPITEMIDLIST pidls[FETCH_ITEM_COUNT];
372 			ULONG cnt = 0;
373 
374 			memset(pidls, 0, sizeof(pidls));
375 
376 			hr_next = enumerator->Next(FETCH_ITEM_COUNT, pidls, &cnt);
377 
378 			/* don't break yet now: Registry Explorer Plugin returns E_FAIL!
379 			if (!SUCCEEDED(hr_next))
380 				break; */
381 
382 			if (hr_next == S_FALSE)
383 				break;
384 
385 			for(ULONG n=0; n<cnt; ++n) {
386 				WIN32_FIND_DATA w32fd;
387 				BY_HANDLE_FILE_INFORMATION bhfi;
388 				bool bhfi_valid = false;
389 
390 				memset(&w32fd, 0, sizeof(WIN32_FIND_DATA));
391 
392 				SFGAOF attribs_before = ~SFGAO_READONLY & ~SFGAO_VALIDATE;
393 				SFGAOF attribs = attribs_before;
394 				HRESULT hr = _folder->GetAttributesOf(1, (LPCITEMIDLIST*)&pidls[n], &attribs);
395 				bool removeable = false;
396 
397 				if (SUCCEEDED(hr) && attribs!=attribs_before) {
398 					 // avoid accessing floppy drives when browsing "My Computer"
399 					if (attribs & SFGAO_REMOVABLE) {
400 						attribs |= SFGAO_HASSUBFOLDER;
401 						removeable = true;
402 					} else if (!(scan_flags & SCAN_DONT_ACCESS)) {
403 						SFGAOF attribs2 = SFGAO_READONLY;
404 
405 						HRESULT hr = _folder->GetAttributesOf(1, (LPCITEMIDLIST*)&pidls[n], &attribs2);
406 
407 						if (SUCCEEDED(hr))
408 							attribs |= attribs2;
409 					}
410 				} else
411 					attribs = 0;
412 
413 				bhfi_valid = fill_w32fdata_shell(pidls[n], attribs, &w32fd, &bhfi,
414 												 !(scan_flags&SCAN_DONT_ACCESS) && !removeable);
415 
416 				try {
417 					Entry* entry = NULL;	// eliminate useless GCC warning by initializing entry
418 
419 					if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
420 						entry = new ShellDirectory(this, pidls[n], _hwnd);
421 					else
422 						entry = new ShellEntry(this, pidls[n]);
423 
424 					if (!first_entry)
425 						first_entry = entry;
426 
427 					if (last)
428 						last->_next = entry;
429 
430 					memcpy(&entry->_data, &w32fd, sizeof(WIN32_FIND_DATA));
431 
432 					if (bhfi_valid)
433 						memcpy(&entry->_bhfi, &bhfi, sizeof(BY_HANDLE_FILE_INFORMATION));
434 
435 					 // store path in entry->_data.cFileName in case fill_w32fdata_shell() didn't already fill it
436 					if (!entry->_data.cFileName[0])
437 						if (SUCCEEDED(path_from_pidl(_folder, pidls[n], path, COUNTOF(path))))
438 							_tcscpy(entry->_data.cFileName, path);
439 
440 					if (SUCCEEDED(name_from_pidl(_folder, pidls[n], name, COUNTOF(name), SHGDN_INFOLDER|0x2000/*0x2000=SHGDN_INCLUDE_NONFILESYS*/))) {
441 						if (!entry->_data.cFileName[0])
442 							_tcscpy(entry->_data.cFileName, name);
443 						else if (_tcscmp(entry->_display_name, name))
444 							entry->_display_name = _tcsdup(name);	// store display name separate from file name; sort display by file name
445 					}
446 
447 					if (attribs & SFGAO_LINK)
448 						w32fd.dwFileAttributes |= ATTRIBUTE_SYMBOLIC_LINK;
449 
450 					entry->_level = level;
451 					entry->_shell_attribs = attribs;
452 					entry->_bhfi_valid = bhfi_valid;
453 
454 					 // set file type name
455 					g_Globals._ftype_mgr.set_type(entry);
456 
457 					 // get icons for files and virtual objects
458 					if (!(entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
459 						!(attribs & SFGAO_FILESYSTEM)) {
460 						if (!(scan_flags & SCAN_DONT_EXTRACT_ICONS))
461 							entry->_icon_id = entry->safe_extract_icon();	// Assume small icon, we can extract the large icon later on demand.
462 					} else if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
463 						entry->_icon_id = ICID_FOLDER;
464 					else
465 						entry->_icon_id = ICID_NONE;	// don't try again later
466 
467 					last = entry;
468 				} catch(COMException& e) {
469 					HandleException(e, _hwnd);
470 				}
471 			}
472 		} while(SUCCEEDED(hr_next));
473 	}
474 
475 	if (last)
476 		last->_next = NULL;
477 
478 	_down = first_entry;
479 	_scanned = true;
480 }
481 
get_next_path_component(const void * p) const482 const void* ShellDirectory::get_next_path_component(const void* p) const
483 {
484 	LPITEMIDLIST pidl = (LPITEMIDLIST)p;
485 
486 	if (!pidl || !pidl->mkid.cb)
487 		return NULL;
488 
489 	 // go to next element
490 	pidl = (LPITEMIDLIST)((LPBYTE)pidl+pidl->mkid.cb);
491 
492 	return pidl;
493 }
494 
find_entry(const void * p)495 Entry* ShellDirectory::find_entry(const void* p)
496 {
497 	LPITEMIDLIST pidl = (LPITEMIDLIST) p;
498 
499 	 // handle special case of empty trailing id list entry
500 	if (!pidl->mkid.cb)
501 		return this;
502 
503 	for(Entry*entry=_down; entry; entry=entry->_next)
504 		if (entry->_etype == ET_SHELL) {
505 			ShellEntry* se = static_cast<ShellEntry*>(entry);
506 
507 			if (se->_pidl && se->_pidl->mkid.cb==pidl->mkid.cb && !memcmp(se->_pidl, pidl, se->_pidl->mkid.cb))
508 				return entry;
509 		} else {
510 			const ShellPath& sp = entry->create_absolute_pidl();
511 			static DynamicFct<LPITEMIDLIST(WINAPI*)(LPCITEMIDLIST)> ILFindLastID(TEXT("SHELL32"), "ILFindLastID");
512 
513 			if (ILFindLastID) {
514 				LPCITEMIDLIST entry_pidl = (*ILFindLastID)(sp);
515 
516 				if (entry_pidl && entry_pidl->mkid.cb==pidl->mkid.cb && !memcmp(entry_pidl, pidl, entry_pidl->mkid.cb))
517 					return entry;
518 			}
519 		}
520 
521 	return NULL;
522 }
523 
extract_icons(ICONCACHE_FLAGS flags)524 int ShellDirectory::extract_icons(ICONCACHE_FLAGS flags)
525 {
526 	int cnt = 0;
527 
528 	for(Entry*entry=_down; entry; entry=entry->_next)
529 		if (entry->_icon_id == ICID_UNKNOWN) {
530 			entry->_icon_id = entry->extract_icon(flags);
531 
532 			if (entry->_icon_id != ICID_NONE)
533 				++cnt;
534 		}
535 
536 	return cnt;
537 }
538