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_DESCRIPTIVAE_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