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 #ifdef _MSC_VER
25 #pragma warning(push, 1) /* disable warnings within system headers */
26 #endif
27 #define WIN32_LEAN_AND_MEAN
28 #include <windows.h>
29 #include <msiquery.h>
30 #ifdef _MSC_VER
31 #pragma warning(pop)
32 #endif
33 
34 #include <malloc.h>
35 #include <string>
36 #include <strsafe.h>
37 
38 //----------------------------------------------------------
39 static const CHAR* g_Extensions[] =
40 {
41     ".doc",     // Microsoft Word Text [0]
42     ".dot",     // Microsoft Word Template
43     ".rtf",     // rtf text
44     ".docx",    // Office Word 2007 XML document
45     ".docm",    // Office Word 2007 XML macro-enabled document
46     ".dotx",    // Office Word 2007 XML template
47     ".dotm",    // Office Word 2007 XML macro-enabled template
48     ".xlw",     // Microsoft Excel
49     ".xls",     // Microsoft Excel
50     ".xlt",     // Microsoft Excel Template
51     ".xlsx",    // Office Excel 2007 XML workbook
52     ".xlsm",    // Office Excel 2007 XML macro-enabled workbook
53     ".xltx",    // Office Excel 2007 XML template
54     ".xltm",    // Office Excel 2007 XML macro-enabled template
55     ".xlsb",    // Office Excel 2007 binary workbook (BIFF12)
56     ".ppt",     // Microsoft Powerpoint
57     ".pps",     // Microsoft Powerpoint
58     ".pot",     // Microsoft Powerpoint Template
59     ".pptx",    // Office PowerPoint 2007 XML presentation
60     ".pptm",    // Office PowerPoint 2007 macro-enabled XML presentation
61     ".potx",    // Office PowerPoint 2007 XML template
62     ".potm",    // Office PowerPoint 2007 macro-enabled XML template
63     ".ppsx",    // Office PowerPoint 2007 XML show
64     0
65 };
66 
67 static const int WORD_START = 0;
68 static const int EXCEL_START = 7;
69 static const int POWERPOINT_START = 15;
70 static const int POWERPOINT_END = 23;
71 
72 //    ".xlam",    // Office Excel 2007 XML macro-enabled add-in
73 //    ".ppam",    // Office PowerPoint 2007 macro-enabled XML add-in
74 //    ".ppsm",    // Office PowerPoint 2007 macro-enabled XML show
75 
76 //----------------------------------------------------------
77 #ifdef DEBUG
78 inline void OutputDebugStringFormat( LPCSTR pFormat, ... )
79 {
80 	CHAR    buffer[1024];
81 	va_list args;
82 
83 	va_start( args, pFormat );
84 	StringCchVPrintfA( buffer, sizeof(buffer), pFormat, args );
85 	OutputDebugStringA( buffer );
86 }
87 #else
88 static inline void OutputDebugStringFormat( LPCSTR, ... )
89 {
90 }
91 #endif
92 
93 //----------------------------------------------------------
94 static BOOL CheckExtensionInRegistry( LPCSTR lpSubKey )
95 {
96     BOOL    bRet = false;
97     HKEY    hKey = NULL;
98     LONG    lResult = RegOpenKeyExA( HKEY_CLASSES_ROOT, lpSubKey, 0, KEY_QUERY_VALUE, &hKey );
99 
100 	if ( ERROR_SUCCESS == lResult )
101 	{
102         CHAR    szBuffer[1024];
103         DWORD   nSize = sizeof( szBuffer );
104 
105         lResult = RegQueryValueExA( hKey, "", NULL, NULL, (LPBYTE)szBuffer, &nSize );
106         if ( ERROR_SUCCESS == lResult )
107         {
108             szBuffer[nSize] = '\0';
109             OutputDebugStringFormat( "Found value [%s] for key [%s].\n", szBuffer, lpSubKey );
110 
111             if ( strncmp( szBuffer, "WordPad.Document.1", 18 ) == 0 )
112             {   // We will replace registration for word pad
113                 bRet = true;
114             }
115             else if ( strncmp( szBuffer, "OpenOffice.org.", 15 ) == 0 )
116             {   // We will replace registration for our (former) own types, too
117                 bRet = true;
118             }
119             else if ( strncmp( szBuffer, "Apache OpenOffice.", 18 ) == 0 )
120             {   // We will replace registration for our own types, too
121                 bRet = true;
122             }
123             else if ( strncmp( szBuffer, "ooostub.", 8 ) == 0 )
124             {   // We will replace registration for ooostub, too
125                 bRet = true;
126             }
127             else
128             {
129                 OutputDebugStringFormat( "  Checking OpenWithList of [%s].\n", lpSubKey );
130                 HKEY hSubKey;
131                 lResult = RegOpenKeyExA( hKey, "OpenWithList", 0, KEY_ENUMERATE_SUB_KEYS, &hSubKey );
132                 if ( ERROR_SUCCESS == lResult )
133                 {
134                     DWORD nIndex = 0;
135                     while ( ERROR_SUCCESS == lResult )
136                     {
137                         nSize = sizeof( szBuffer );
138                         lResult = RegEnumKeyExA( hSubKey, nIndex++, szBuffer, &nSize, NULL, NULL, NULL, NULL );
139                         if ( ERROR_SUCCESS == lResult )
140                         {
141                             OutputDebugStringFormat( "    Found value [%s] in OpenWithList of [%s].\n", szBuffer, lpSubKey );
142                             if ( strncmp( szBuffer, "WordPad.exe", 11 ) == 0 )
143                             {   // We will replace registration for word pad
144                                 bRet = true;
145                             }
146                             else if ( nSize > 0 )
147                                 bRet = false;
148                         }
149                     }
150                 }
151                 else
152                 {
153                     OutputDebugStringFormat( "  No OpenWithList found!\n" );
154                 }
155             }
156         }
157         else    // no default value found -> return TRUE to register for that key
158             bRet = true;
159 
160 		RegCloseKey( hKey );
161 	}
162     else // no key found -> return TRUE to register for that key
163         bRet = true;
164 
165     return bRet;
166 }
167 
168 //----------------------------------------------------------
169 static LONG DeleteSubKeyTree( HKEY RootKey, LPCSTR lpKey )
170 {
171 	HKEY hKey;
172 	LONG rc = RegOpenKeyExA( RootKey, lpKey, 0, KEY_READ | DELETE, &hKey );
173 
174 	if (ERROR_SUCCESS == rc)
175 	{
176 		LPCSTR    lpSubKey;
177 		DWORD     nMaxSubKeyLen;
178 
179 		rc = RegQueryInfoKeyA( hKey, 0, 0, 0, 0, &nMaxSubKeyLen, 0, 0, 0, 0, 0, 0 );
180 		nMaxSubKeyLen++; // space for trailing '\0'
181 		lpSubKey = reinterpret_cast<CHAR*>( _alloca( nMaxSubKeyLen*sizeof(CHAR) ) );
182 
183 		while (ERROR_SUCCESS == rc)
184         {
185 			DWORD nLen = nMaxSubKeyLen;
186 			rc = RegEnumKeyExA( hKey, 0, (LPSTR)lpSubKey, &nLen, 0, 0, 0, 0);    // always index zero
187 
188             if ( ERROR_NO_MORE_ITEMS == rc )
189             {
190 				rc = RegDeleteKeyA( RootKey, lpKey );
191                 if ( rc == ERROR_SUCCESS )
192                     OutputDebugStringFormat( "deleted key [%s] from registry.\n", lpKey );
193                 else
194                     OutputDebugStringFormat( "RegDeleteKeyA %s returned %ld.\n", lpKey, rc );
195                 break;
196             }
197             else if ( rc == ERROR_SUCCESS )
198 			{
199 				rc = DeleteSubKeyTree( hKey, lpSubKey );
200                 if ( ERROR_SUCCESS != rc )
201                     OutputDebugStringFormat( "RegDeleteKeyA %s returned %ld.\n", lpSubKey, rc );
202 			}
203 
204 		}
205         RegCloseKey(hKey);
206 	}
207     else
208     {
209         OutputDebugStringFormat( "RegOpenKeyExA %s returned %ld.\n", lpKey, rc );
210     }
211 
212 	return rc;
213 }
214 
215 //----------------------------------------------------------
216 static BOOL RemoveExtensionInRegistry( LPCSTR lpSubKey )
217 {
218     CHAR    szBuffer[4096];
219     DWORD   nSize = sizeof( szBuffer );
220     HKEY    hKey = NULL;
221     HKEY    hSubKey = NULL;
222     LONG    lResult = RegOpenKeyExA( HKEY_LOCAL_MACHINE, "SOFTWARE\\Classes", 0, KEY_QUERY_VALUE, &hKey );
223 
224 	if ( ERROR_SUCCESS == lResult )
225     {
226 		lResult = RegOpenKeyExA( hKey, lpSubKey, 0, KEY_QUERY_VALUE, &hSubKey );
227 
228         if ( ERROR_SUCCESS == lResult )
229         {
230             DWORD nSubKeys = 1;
231             szBuffer[0] = '\0';
232 
233             // we get the value of the default key fist and while we are on querying,
234             // we ask for the subkey count, too
235             lResult = RegQueryValueExA( hSubKey, "", NULL, NULL, (LPBYTE)szBuffer, &nSize );
236             if ( ERROR_SUCCESS == lResult )
237                 RegQueryInfoKeyA( hSubKey, 0, 0, 0, &nSubKeys, 0, 0, 0, 0, 0, 0, 0 );
238             RegCloseKey( hSubKey );
239 
240             // we will remove all key with an default value starting with ooostub but
241             // we have to be careful about MSO keys
242             if ( strncmp( szBuffer, "opendocument.", 13 ) == 0 )
243             {
244                 if ( nSubKeys == 0 )
245                 {
246                     DeleteSubKeyTree( hKey, lpSubKey );
247                 }
248                 else
249                 {
250                     lResult = RegOpenKeyExA( hKey, lpSubKey, 0, KEY_SET_VALUE, &hSubKey );
251                     if ( ERROR_SUCCESS == lResult )
252                         RegDeleteValueA( hSubKey, "" );
253                     else
254                         OutputDebugStringFormat( "Could not open key %s for deleting: RegOpenKeyEx returned %ld.\n", lpSubKey, lResult );
255                 }
256             }
257         }
258 
259         RegCloseKey( hKey );
260     }
261 
262     return ( ERROR_SUCCESS == lResult );
263 }
264 
265 //----------------------------------------------------------
266 bool GetMsiProp( MSIHANDLE handle, LPCSTR name, /*out*/std::string& value )
267 {
268     DWORD sz = 0;
269     LPSTR dummy = "";
270     if (MsiGetPropertyA(handle, name, dummy, &sz) == ERROR_MORE_DATA)
271     {
272         sz++;
273         DWORD nbytes = sz * sizeof(TCHAR);
274         LPSTR buff = reinterpret_cast<LPSTR>(_alloca(nbytes));
275         ZeroMemory(buff, nbytes);
276         MsiGetPropertyA(handle, name, buff, &sz);
277         value = buff;
278         return true;
279     }
280     return false;
281 }
282 
283 //----------------------------------------------------------
284 bool IsSetMsiProp( MSIHANDLE handle, LPCSTR name )
285 {
286     std::string val;
287     GetMsiProp( handle, name, val );
288     return (val == "1");
289 }
290 
291 //----------------------------------------------------------
292 static void registerForExtension( MSIHANDLE handle, const int nIndex, bool bRegister )
293 {
294     CHAR sPropName[256];
295     StringCchCopyA( sPropName, 256, "REGISTER_" );
296     StringCchCatA( sPropName, 256, (g_Extensions[nIndex])+1 );
297     CharUpperBuffA( sPropName+9, 4 );
298 
299     if ( bRegister ) {
300         MsiSetPropertyA( handle, sPropName, "1" );
301         OutputDebugStringFormat( "Set MSI property %s.\n", sPropName );
302     } else {
303         MsiSetPropertyA( handle, sPropName, "0" );
304         OutputDebugStringFormat( "Unset MSI property %s.\n", sPropName );
305     }
306 }
307 
308 //----------------------------------------------------------
309 static void registerForExtensions( MSIHANDLE handle, BOOL bRegisterAll )
310 { // Check all file extensions
311     int nIndex = 0;
312     while ( g_Extensions[nIndex] != 0 )
313     {
314         BOOL bRegister = bRegisterAll || CheckExtensionInRegistry( g_Extensions[nIndex] );
315         if ( bRegister )
316             registerForExtension( handle, nIndex, true );
317         ++nIndex;
318     }
319 }
320 
321 //----------------------------------------------------------
322 static bool checkSomeExtensionInRegistry( const int nStart, const int nEnd )
323 { // Check all file extensions
324     int nIndex = nStart;
325     bool bFound = false;
326 
327     while ( !bFound && ( g_Extensions[nIndex] != 0 ) && ( nIndex < nEnd ) )
328     {
329         bFound = ! CheckExtensionInRegistry( g_Extensions[nIndex] );
330 
331         if ( bFound )
332             OutputDebugStringFormat( "Found registration for [%s].\n", g_Extensions[nIndex] );
333 
334         ++nIndex;
335     }
336     return bFound;
337 }
338 
339 //----------------------------------------------------------
340 static void registerSomeExtensions( MSIHANDLE handle, const int nStart, const int nEnd, bool bRegister )
341 { // Check all file extensions
342     int nIndex = nStart;
343 
344     while ( ( g_Extensions[nIndex] != 0 ) && ( nIndex < nEnd ) )
345     {
346         registerForExtension( handle, nIndex++, bRegister );
347     }
348 }
349 
350 //----------------------------------------------------------
351 //----------------------------------------------------------
352 //----------------------------------------------------------
353 extern "C" UINT __stdcall LookForRegisteredExtensions( MSIHANDLE handle )
354 {
355     OutputDebugStringFormat( "LookForRegisteredExtensions: " );
356 
357     INSTALLSTATE current_state;
358     INSTALLSTATE future_state;
359 
360     bool bWriterEnabled = false;
361     bool bCalcEnabled = false;
362     bool bImpressEnabled = false;
363     bool bRegisterNone = IsSetMsiProp( handle, "REGISTER_NO_MSO_TYPES" );
364 
365     if ( ( ERROR_SUCCESS == MsiGetFeatureState( handle, L"gm_p_Wrt", &current_state, &future_state ) ) &&
366          ( (future_state == INSTALLSTATE_LOCAL) || ((current_state == INSTALLSTATE_LOCAL) && (future_state == INSTALLSTATE_UNKNOWN) ) ) )
367         bWriterEnabled = true;
368 
369     OutputDebugStringFormat( "LookForRegisteredExtensions: Install state Writer is [%d], will be [%d]", current_state, future_state );
370     if ( bWriterEnabled )
371         OutputDebugStringFormat( "LookForRegisteredExtensions: Writer is enabled" );
372     else
373         OutputDebugStringFormat( "LookForRegisteredExtensions: Writer is NOT enabled" );
374 
375     if ( ( ERROR_SUCCESS == MsiGetFeatureState( handle, L"gm_p_Calc", &current_state, &future_state ) ) &&
376          ( (future_state == INSTALLSTATE_LOCAL) || ((current_state == INSTALLSTATE_LOCAL) && (future_state == INSTALLSTATE_UNKNOWN) ) ) )
377         bCalcEnabled = true;
378 
379     OutputDebugStringFormat( "LookForRegisteredExtensions: Install state Calc is [%d], will be [%d]", current_state, future_state );
380     if ( bCalcEnabled )
381         OutputDebugStringFormat( "LookForRegisteredExtensions: Calc is enabled" );
382     else
383         OutputDebugStringFormat( "LookForRegisteredExtensions: Calc is NOT enabled" );
384 
385     if ( ( ERROR_SUCCESS == MsiGetFeatureState( handle, L"gm_p_Impress", &current_state, &future_state ) ) &&
386          ( (future_state == INSTALLSTATE_LOCAL) || ((current_state == INSTALLSTATE_LOCAL) && (future_state == INSTALLSTATE_UNKNOWN) ) ) )
387         bImpressEnabled = true;
388 
389     OutputDebugStringFormat( "LookForRegisteredExtensions: Install state Impress is [%d], will be [%d]", current_state, future_state );
390     if ( bImpressEnabled )
391         OutputDebugStringFormat( "LookForRegisteredExtensions: Impress is enabled" );
392     else
393         OutputDebugStringFormat( "LookForRegisteredExtensions: Impress is NOT enabled" );
394 
395     MsiSetPropertyA( handle, "SELECT_WORD", "" );
396     MsiSetPropertyA( handle, "SELECT_EXCEL", "" );
397     MsiSetPropertyA( handle, "SELECT_POWERPOINT", "" );
398 
399     if ( ! bRegisterNone )
400     {
401         if ( IsSetMsiProp( handle, "REGISTER_ALL_MSO_TYPES" ) )
402         {
403             if ( bWriterEnabled )
404                 MsiSetPropertyA( handle, "SELECT_WORD", "1" );
405             if ( bCalcEnabled )
406                 MsiSetPropertyA( handle, "SELECT_EXCEL", "1" );
407             if ( bImpressEnabled )
408                 MsiSetPropertyA( handle, "SELECT_POWERPOINT", "1" );
409         }
410         else
411         {
412             if ( bWriterEnabled && ! checkSomeExtensionInRegistry( WORD_START, EXCEL_START ) )
413             {
414                 MsiSetPropertyA( handle, "SELECT_WORD", "1" );
415                 OutputDebugStringFormat( "LookForRegisteredExtensions: Register for MicroSoft Word" );
416             }
417             if ( bCalcEnabled && ! checkSomeExtensionInRegistry( EXCEL_START, POWERPOINT_START ) )
418             {
419                 MsiSetPropertyA( handle, "SELECT_EXCEL", "1" );
420                 OutputDebugStringFormat( "LookForRegisteredExtensions: Register for MicroSoft Excel" );
421             }
422             if ( bImpressEnabled && ! checkSomeExtensionInRegistry( POWERPOINT_START, POWERPOINT_END ) )
423             {
424                 MsiSetPropertyA( handle, "SELECT_POWERPOINT", "1" );
425                 OutputDebugStringFormat( "LookForRegisteredExtensions: Register for MicroSoft PowerPoint" );
426             }
427         }
428     }
429 
430     MsiSetPropertyA( handle, "FILETYPEDIALOGUSED", "1" );
431 
432     return ERROR_SUCCESS;
433 }
434 
435 //----------------------------------------------------------
436 extern "C" UINT __stdcall RegisterSomeExtensions( MSIHANDLE handle )
437 {
438     OutputDebugStringFormat( "RegisterSomeExtensions: " );
439 
440     if ( IsSetMsiProp( handle, "SELECT_WORD" ) )
441     {
442         registerSomeExtensions( handle, WORD_START, EXCEL_START, true );
443         MsiSetFeatureState( handle, L"gm_p_Wrt_MSO_Reg", INSTALLSTATE_LOCAL );
444         OutputDebugStringFormat( "RegisterSomeExtensions: Register for MicroSoft Word" );
445     }
446     else
447     {
448         registerSomeExtensions( handle, WORD_START, EXCEL_START, false );
449         MsiSetFeatureState( handle, L"gm_p_Wrt_MSO_Reg", INSTALLSTATE_ABSENT );
450     }
451 
452     if ( IsSetMsiProp( handle, "SELECT_EXCEL" ) )
453     {
454         registerSomeExtensions( handle, EXCEL_START, POWERPOINT_START, true );
455         MsiSetFeatureState( handle, L"gm_p_Calc_MSO_Reg", INSTALLSTATE_LOCAL );
456         OutputDebugStringFormat( "RegisterSomeExtensions: Register for MicroSoft Excel" );
457     }
458     else
459     {
460         registerSomeExtensions( handle, EXCEL_START, POWERPOINT_START, false );
461         MsiSetFeatureState( handle, L"gm_p_Calc_MSO_Reg", INSTALLSTATE_ABSENT );
462     }
463 
464     if ( IsSetMsiProp( handle, "SELECT_POWERPOINT" ) )
465     {
466         registerSomeExtensions( handle, POWERPOINT_START, POWERPOINT_END, true );
467         MsiSetFeatureState( handle, L"gm_p_Impress_MSO_Reg", INSTALLSTATE_LOCAL );
468         OutputDebugStringFormat( "RegisterSomeExtensions: Register for MicroSoft PowerPoint" );
469     }
470     else
471     {
472         registerSomeExtensions( handle, POWERPOINT_START, POWERPOINT_END, false );
473         MsiSetFeatureState( handle, L"gm_p_Impress_MSO_Reg", INSTALLSTATE_ABSENT );
474     }
475 
476     return ERROR_SUCCESS;
477 }
478 
479 //----------------------------------------------------------
480 extern "C" UINT __stdcall FindRegisteredExtensions( MSIHANDLE handle )
481 {
482     if ( IsSetMsiProp( handle, "FILETYPEDIALOGUSED" ) )
483     {
484         OutputDebugStringFormat( "FindRegisteredExtensions: FILETYPEDIALOGUSED!" );
485         return ERROR_SUCCESS;
486     }
487 
488     OutputDebugStringFormat( "FindRegisteredExtensions:" );
489 
490     bool bRegisterAll = IsSetMsiProp( handle, "REGISTER_ALL_MSO_TYPES" );
491 
492     if ( IsSetMsiProp( handle, "REGISTER_NO_MSO_TYPES" ) )
493     {
494         OutputDebugStringFormat( "FindRegisteredExtensions: Register none!" );
495         return ERROR_SUCCESS;
496     }
497     else if ( bRegisterAll )
498         OutputDebugStringFormat( "FindRegisteredExtensions: Force all on" );
499     else
500         OutputDebugStringFormat( "FindRegisteredExtensions: " );
501 
502     // setting the msi properties SELECT_* will force registering for all corresponding
503     // file types
504     if ( IsSetMsiProp( handle, "SELECT_WORD" ) )
505         registerSomeExtensions( handle, WORD_START, EXCEL_START, true );
506     if ( IsSetMsiProp( handle, "SELECT_EXCEL" ) )
507         registerSomeExtensions( handle, EXCEL_START, POWERPOINT_START, true );
508     if ( IsSetMsiProp( handle, "SELECT_POWERPOINT" ) )
509         registerSomeExtensions( handle, POWERPOINT_START, POWERPOINT_END, true );
510 
511     registerForExtensions( handle, bRegisterAll );
512 
513     return ERROR_SUCCESS;
514 }
515 
516 //----------------------------------------------------------
517 extern "C" UINT __stdcall DeleteRegisteredExtensions( MSIHANDLE /*handle*/ )
518 {
519     OutputDebugStringFormat( "DeleteRegisteredExtensions\n" );
520 
521     // remove all file extensions
522     int nIndex = 0;
523     while ( g_Extensions[nIndex] != 0 )
524     {
525         RemoveExtensionInRegistry( g_Extensions[nIndex] );
526         ++nIndex;
527     }
528 
529     return ERROR_SUCCESS;
530 }
531