1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_shell.hxx"
26 #include "internal/config.hxx"
27 #include "internal/global.hxx"
28 #include "internal/shlxthdl.hxx"
29 #include "classfactory.hxx"
30 #include "internal/registry.hxx"
31 #include "internal/fileextensions.hxx"
32 #include "internal/utilities.hxx"
33 
34 #include <tchar.h>
35 #include <string>
36 #include <shlobj.h>
37 
38 //---------------------------
39 // Module global
40 //---------------------------
41 long g_DllRefCnt = 0;
42 HINSTANCE g_hModule = NULL;
43 
44 namespace /* private */
45 {
46 	const char* GUID_PLACEHOLDER		= "{GUID}";
47 	const char* EXTENSION_PLACEHOLDER	= "{EXT}";
48 	const char* FORWARDKEY_PLACEHOLDER	= "{FWDKEY}";
49 
50 	const char* CLSID_ENTRY							= "CLSID\\{GUID}\\InProcServer32";
51 	const char* SHELLEX_IID_ENTRY					= "{EXT}\\shellex\\{GUID}";
52 	const char* SHELLEX_ENTRY						= "{EXT}\\shellex";
53 	const char* PROPSHEET_ENTRY						= "{EXT}\\CLSID\\{GUID}\\InProcServer32";
54 	const char* EXTENSION_CLSID						= "{EXT}\\CLSID";
55 	const char* EXTENSION_CLSID_GUID				= "{EXT}\\CLSID\\{GUID}";
56 	const char* FORWARD_PROPSHEET_MYPROPSHEET_ENTRY = "{FWDKEY}\\shellex\\PropertySheetHandlers\\MyPropSheet1";
57 	const char* FORWARD_PROPSHEET_ENTRY				= "{FWDKEY}\\shellex\\PropertySheetHandlers";
58 	const char* FORWARD_SHELLEX_ENTRY				= "{FWDKEY}\\shellex";
59 
60 	const char* SHELL_EXTENSION_APPROVED_KEY_NAME	= "Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved";
61 
62 	//---------------------------
63 	// "String Placeholder" ->
64 	// "String Replacement"
65 	//---------------------------
SubstitutePlaceholder(std::string & String,const std::string & Placeholder,const std::string & Replacement)66 	void SubstitutePlaceholder(std::string& String, const std::string& Placeholder, const std::string& Replacement)
67 	{
68 		std::string::size_type idx = String.find(Placeholder);
69 		std::string::size_type len = Placeholder.length();
70 
71 		while (std::string::npos != idx)
72 		{
73 			String.replace(idx, len, Replacement);
74 			idx = String.find(Placeholder);
75 		}
76 	}
77 
78 	/* Make the registry entry
79 		HKCR\CLSID\{GUID}
80 			InProcServer32 = Path\shlxthdl.dll
81 				ThreadingModel = Apartment
82 	*/
RegisterComComponent(const char * FilePath,const CLSID & Guid)83 	HRESULT RegisterComComponent(const char* FilePath, const CLSID& Guid)
84 	{
85 		std::string ClsidEntry = CLSID_ENTRY;
86 		SubstitutePlaceholder(ClsidEntry, GUID_PLACEHOLDER, ClsidToString(Guid));
87 
88 		if (!SetRegistryKey(HKEY_CLASSES_ROOT, ClsidEntry.c_str(), "", FilePath))
89 			return E_FAIL;
90 
91 		if (!SetRegistryKey(HKEY_CLASSES_ROOT, ClsidEntry.c_str(), "ThreadingModel", "Apartment"))
92 			return E_FAIL;
93 
94 		return S_OK;
95 	}
96 
UnregisterComComponent(const CLSID & Guid)97 	HRESULT UnregisterComComponent(const CLSID& Guid)
98 	{
99 		std::string tmp = "CLSID\\";
100 		tmp += ClsidToString(Guid);
101 		return DeleteRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str()) ? S_OK : E_FAIL;
102 	}
103 
RegisterColumnHandler(const char * ModuleFileName)104 	HRESULT RegisterColumnHandler(const char* ModuleFileName)
105 	{
106 		if (FAILED(RegisterComComponent(ModuleFileName, CLSID_COLUMN_HANDLER)))
107 			return E_FAIL;
108 
109 		std::string tmp = "Folder\\shellex\\ColumnHandlers\\";
110 		tmp += ClsidToString(CLSID_COLUMN_HANDLER);
111 
112 		return SetRegistryKey(
113 			HKEY_CLASSES_ROOT,
114 			tmp.c_str(),
115 			"",
116 			WStringToString(COLUMN_HANDLER_DESCRIPTIVE_NAME).c_str()) ? S_OK : E_FAIL;
117 	}
118 
UnregisterColumnHandler()119 	HRESULT UnregisterColumnHandler()
120 	{
121 		std::string tmp = "Folder\\shellex\\ColumnHandlers\\";
122 		tmp += ClsidToString(CLSID_COLUMN_HANDLER);
123 
124 		if (!DeleteRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str()))
125 			return E_FAIL;
126 
127 		return UnregisterComComponent(CLSID_COLUMN_HANDLER);
128 	}
129 
RegisterInfotipHandler(const char * ModuleFileName)130 	HRESULT RegisterInfotipHandler(const char* ModuleFileName)
131 	{
132 		if (FAILED(RegisterComComponent(ModuleFileName, CLSID_INFOTIP_HANDLER)))
133 			return E_FAIL;
134 
135 		std::string iid = ClsidToString(IID_IQueryInfo);
136 		std::string tmp;
137 
138 		for(size_t i = 0; i < OOFileExtensionTableSize; i++)
139 		{
140 			tmp = SHELLEX_IID_ENTRY;
141 			SubstitutePlaceholder(tmp, EXTENSION_PLACEHOLDER, OOFileExtensionTable[i].ExtensionAnsi);
142 			SubstitutePlaceholder(tmp, GUID_PLACEHOLDER, iid);
143 
144 			if (!SetRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str(), "", ClsidToString(CLSID_INFOTIP_HANDLER).c_str()))
145 				return E_FAIL;
146 		}
147 		return S_OK;
148 	}
149 
UnregisterInfotipHandler()150 	HRESULT UnregisterInfotipHandler()
151 	{
152 		std::string iid = ClsidToString(IID_IQueryInfo);
153 		std::string tmp;
154 
155 		for (size_t i = 0; i < OOFileExtensionTableSize; i++)
156 		{
157 			tmp = SHELLEX_IID_ENTRY;
158 
159 			SubstitutePlaceholder(tmp, EXTENSION_PLACEHOLDER, OOFileExtensionTable[i].ExtensionAnsi);
160 			SubstitutePlaceholder(tmp, GUID_PLACEHOLDER, iid);
161 
162 			DeleteRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str());
163 
164 			// if there are no further subkey below .ext\\shellex
165 			// delete the whole subkey
166 			tmp = SHELLEX_ENTRY;
167 			SubstitutePlaceholder(tmp, EXTENSION_PLACEHOLDER, OOFileExtensionTable[i].ExtensionAnsi);
168 
169 			bool HasSubKeys = true;
170 			if (HasSubkeysRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str(), HasSubKeys) && !HasSubKeys)
171 				DeleteRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str());
172 		}
173 		return UnregisterComComponent(CLSID_INFOTIP_HANDLER);
174 	}
175 
RegisterPropSheetHandler(const char * ModuleFileName)176 	HRESULT RegisterPropSheetHandler(const char* ModuleFileName)
177 	{
178 		std::string ExtEntry;
179 		std::string FwdKeyEntry;
180 
181 		if (FAILED(RegisterComComponent(ModuleFileName, CLSID_PROPERTYSHEET_HANDLER)))
182 			return E_FAIL;
183 
184 		for (size_t i = 0; i < OOFileExtensionTableSize; i++)
185 		{
186 			FwdKeyEntry = FORWARD_PROPSHEET_MYPROPSHEET_ENTRY;
187 			SubstitutePlaceholder(FwdKeyEntry, FORWARDKEY_PLACEHOLDER, OOFileExtensionTable[i].RegistryForwardKey);
188 
189 			if (!SetRegistryKey(HKEY_CLASSES_ROOT, FwdKeyEntry.c_str(), "", ClsidToString(CLSID_PROPERTYSHEET_HANDLER).c_str()))
190 				return E_FAIL;
191 		}
192 		return S_OK;
193 	}
194 
UnregisterPropSheetHandler()195 	HRESULT UnregisterPropSheetHandler()
196 	{
197 		std::string ExtEntry;
198 		std::string FwdKeyEntry;
199 
200 		for (size_t i = 0; i < OOFileExtensionTableSize; i++)
201 		{
202 			FwdKeyEntry = FORWARD_PROPSHEET_MYPROPSHEET_ENTRY;
203 			SubstitutePlaceholder(FwdKeyEntry, FORWARDKEY_PLACEHOLDER, OOFileExtensionTable[i].RegistryForwardKey);
204 
205 			DeleteRegistryKey(HKEY_CLASSES_ROOT, FwdKeyEntry.c_str());
206 
207 			FwdKeyEntry = FORWARD_PROPSHEET_ENTRY;
208 			SubstitutePlaceholder(FwdKeyEntry, FORWARDKEY_PLACEHOLDER, OOFileExtensionTable[i].RegistryForwardKey);
209 
210 			bool HasSubKeys = true;
211 			if (HasSubkeysRegistryKey(HKEY_CLASSES_ROOT, FwdKeyEntry.c_str(), HasSubKeys) && !HasSubKeys)
212 				DeleteRegistryKey(HKEY_CLASSES_ROOT, FwdKeyEntry.c_str());
213 
214 			FwdKeyEntry = FORWARD_SHELLEX_ENTRY;
215 			SubstitutePlaceholder(FwdKeyEntry, FORWARDKEY_PLACEHOLDER, OOFileExtensionTable[i].RegistryForwardKey);
216 
217 			HasSubKeys = true;
218 			if (HasSubkeysRegistryKey(HKEY_CLASSES_ROOT, FwdKeyEntry.c_str(), HasSubKeys) && !HasSubKeys)
219 				DeleteRegistryKey(HKEY_CLASSES_ROOT, FwdKeyEntry.c_str());
220 		}
221 
222 		return UnregisterComComponent(CLSID_PROPERTYSHEET_HANDLER);
223 	}
224 
RegisterThumbviewerHandler(const char * ModuleFileName)225 	HRESULT RegisterThumbviewerHandler(const char* ModuleFileName)
226 	{
227 		if (FAILED(RegisterComComponent(ModuleFileName, CLSID_THUMBVIEWER_HANDLER)))
228 			return E_FAIL;
229 
230 		std::string iid = ClsidToString(IID_IExtractImage);
231 		std::string tmp;
232 
233 		for(size_t i = 0; i < OOFileExtensionTableSize; i++)
234 		{
235 			tmp = SHELLEX_IID_ENTRY;
236 
237 			SubstitutePlaceholder(tmp, EXTENSION_PLACEHOLDER, OOFileExtensionTable[i].ExtensionAnsi);
238 			SubstitutePlaceholder(tmp, GUID_PLACEHOLDER, iid);
239 
240 			if (!SetRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str(), "", ClsidToString(CLSID_THUMBVIEWER_HANDLER).c_str()))
241 				return E_FAIL;
242 		}
243 		return S_OK;
244 	}
245 
UnregisterThumbviewerHandler()246 	HRESULT UnregisterThumbviewerHandler()
247 	{
248 		std::string iid = ClsidToString(IID_IExtractImage);
249 		std::string tmp;
250 
251 		for (size_t i = 0; i < OOFileExtensionTableSize; i++)
252 		{
253 			tmp = SHELLEX_IID_ENTRY;
254 
255 			SubstitutePlaceholder(tmp, EXTENSION_PLACEHOLDER, OOFileExtensionTable[i].ExtensionAnsi);
256 			SubstitutePlaceholder(tmp, GUID_PLACEHOLDER, iid);
257 
258 			DeleteRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str());
259 
260 			// if there are no further subkey below .ext\\shellex
261 			// delete the whole subkey
262 			tmp = SHELLEX_ENTRY;
263 			SubstitutePlaceholder(tmp, EXTENSION_PLACEHOLDER, OOFileExtensionTable[i].ExtensionAnsi);
264 
265 			bool HasSubKeys = true;
266 			if (HasSubkeysRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str(), HasSubKeys) && !HasSubKeys)
267 				DeleteRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str());
268 		}
269 		return UnregisterComComponent(CLSID_THUMBVIEWER_HANDLER);
270 	}
271 
272 	/** Approving/Unapproving the Shell Extension, it's important under Windows
273 	    NT/2000/XP, see MSDN: Creating Shell Extension Handlers */
ApproveShellExtension(CLSID clsid,const std::wstring & Description)274 	HRESULT ApproveShellExtension(CLSID clsid, const std::wstring& Description)
275 	{
276 		bool bRet = SetRegistryKey(
277 			HKEY_LOCAL_MACHINE,
278 			SHELL_EXTENSION_APPROVED_KEY_NAME,
279 			ClsidToString(clsid).c_str(),
280 			WStringToString(Description).c_str());
281 
282 		return bRet ? S_OK : E_FAIL;
283 	}
284 
UnapproveShellExtension(CLSID Clsid)285 	HRESULT UnapproveShellExtension(CLSID Clsid)
286 	{
287 		HKEY hkey;
288 
289 		LONG rc = RegOpenKeyA(
290 			HKEY_LOCAL_MACHINE,
291 			SHELL_EXTENSION_APPROVED_KEY_NAME,
292 			&hkey);
293 
294 		if (ERROR_SUCCESS == rc)
295 		{
296 			rc = RegDeleteValueA(
297 				hkey,
298 				ClsidToString(Clsid).c_str());
299 
300 			rc = RegCloseKey(hkey);
301 		}
302 
303 		return rc == ERROR_SUCCESS ? S_OK : E_FAIL;
304 	}
305 
306 } // namespace /* private */
307 
308 
309 //---------------------
310 // COM exports
311 //---------------------
312 
DllRegisterServer()313 extern "C" STDAPI DllRegisterServer()
314 {
315 	TCHAR ModuleFileName[MAX_PATH];
316 
317 	GetModuleFileName(
318 		GetModuleHandle(MODULE_NAME),
319 		ModuleFileName,
320 		sizeof(ModuleFileName));
321 
322 	std::string module_path = WStringToString(ModuleFileName);
323 	HRESULT hr = S_OK;
324 
325 	if (SUCCEEDED(RegisterColumnHandler(module_path.c_str())))
326 		ApproveShellExtension(CLSID_COLUMN_HANDLER, COLUMN_HANDLER_DESCRIPTIVE_NAME);
327 	else
328 		hr = E_FAIL;
329 
330 	if (SUCCEEDED(RegisterInfotipHandler(module_path.c_str())))
331 		ApproveShellExtension(CLSID_INFOTIP_HANDLER, INFOTIP_HANDLER_DESCRIPTIVE_NAME);
332 	else
333 		hr = E_FAIL;
334 
335 	if (SUCCEEDED(RegisterPropSheetHandler(module_path.c_str())))
336 		ApproveShellExtension(CLSID_PROPERTYSHEET_HANDLER, PROPSHEET_HANDLER_DESCRIPTIVE_NAME);
337 	else
338 		hr = E_FAIL;
339 
340 	if (SUCCEEDED(RegisterThumbviewerHandler(module_path.c_str())))
341 		ApproveShellExtension(CLSID_THUMBVIEWER_HANDLER, THUMBVIEWER_HANDLER_DESCRIPTIVE_NAME);
342 	else
343 		hr = E_FAIL;
344 
345 	// notify the Shell that something has changed
346 	SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0);
347 
348 	return hr;
349 }
350 
DllUnregisterServer()351 extern "C" STDAPI DllUnregisterServer()
352 {
353 	HRESULT hr = S_OK;
354 
355 	if (FAILED(UnregisterColumnHandler()))
356 		hr = E_FAIL;
357 
358 	UnapproveShellExtension(CLSID_COLUMN_HANDLER);
359 
360 	if (FAILED(UnregisterInfotipHandler()))
361 		hr = E_FAIL;
362 
363 	UnapproveShellExtension(CLSID_INFOTIP_HANDLER);
364 
365 	if (FAILED(UnregisterPropSheetHandler()))
366 		hr = E_FAIL;
367 
368 	UnapproveShellExtension(CLSID_PROPERTYSHEET_HANDLER);
369 
370 	if (FAILED(UnregisterThumbviewerHandler()))
371 		hr = E_FAIL;
372 
373 	UnapproveShellExtension(CLSID_THUMBVIEWER_HANDLER);
374 
375 	// notify the Shell that something has changed
376 	SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0);
377 
378 	return hr;
379 }
380 
DllGetClassObject(REFCLSID rclsid,REFIID riid,void ** ppv)381 extern "C" STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv)
382 {
383 	*ppv = 0;
384 
385 	if ((rclsid != CLSID_INFOTIP_HANDLER) &&
386 		(rclsid != CLSID_COLUMN_HANDLER) &&
387 		(rclsid != CLSID_PROPERTYSHEET_HANDLER) &&
388 		(rclsid != CLSID_THUMBVIEWER_HANDLER))
389 		return CLASS_E_CLASSNOTAVAILABLE;
390 
391 	if ((riid != IID_IUnknown) && (riid != IID_IClassFactory))
392 		return E_NOINTERFACE;
393 
394 	if ( rclsid == CLSID_INFOTIP_HANDLER )
395 		OutputDebugStringFormat( "DllGetClassObject: Create CLSID_INFOTIP_HANDLER\n" );
396 	else if ( rclsid == CLSID_COLUMN_HANDLER )
397 		OutputDebugStringFormat( "DllGetClassObject: Create CLSID_COLUMN_HANDLER\n" );
398 	else if ( rclsid == CLSID_PROPERTYSHEET_HANDLER )
399 		OutputDebugStringFormat( "DllGetClassObject: Create CLSID_PROPERTYSHEET_HANDLER\n" );
400 	else if ( rclsid == CLSID_THUMBVIEWER_HANDLER )
401 		OutputDebugStringFormat( "DllGetClassObject: Create CLSID_THUMBVIEWER_HANDLER\n" );
402 
403 	IUnknown* pUnk = new CClassFactory(rclsid);
404 	if (0 == pUnk)
405 		return E_OUTOFMEMORY;
406 
407 	*ppv = pUnk;
408 	return S_OK;
409 }
410 
DllCanUnloadNow(void)411 extern "C" STDAPI DllCanUnloadNow(void)
412 {
413 	if (CClassFactory::IsLocked() || g_DllRefCnt > 0)
414 		return S_FALSE;
415 
416 	return S_OK;
417 }
418 
DllMain(HINSTANCE hInst,ULONG,LPVOID)419 BOOL WINAPI DllMain(HINSTANCE hInst, ULONG /*ul_reason_for_call*/, LPVOID /*lpReserved*/)
420 {
421 	g_hModule = hInst;
422 	return TRUE;
423 }
424 
425 /* vim: set noet sw=4 ts=4: */
426