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 own types, too
117                 bRet = true;
118             }
119             else if ( strncmp( szBuffer, "ooostub.", 8 ) == 0 )
120             {   // We will replace registration for ooostub, too
121                 bRet = true;
122             }
123             else
124             {
125                 OutputDebugStringFormat( "  Checking OpenWithList of [%s].\n", lpSubKey );
126                 HKEY hSubKey;
127                 lResult = RegOpenKeyExA( hKey, "OpenWithList", 0, KEY_ENUMERATE_SUB_KEYS, &hSubKey );
128                 if ( ERROR_SUCCESS == lResult )
129                 {
130                     DWORD nIndex = 0;
131                     while ( ERROR_SUCCESS == lResult )
132                     {
133                         nSize = sizeof( szBuffer );
134                         lResult = RegEnumKeyExA( hSubKey, nIndex++, szBuffer, &nSize, NULL, NULL, NULL, NULL );
135                         if ( ERROR_SUCCESS == lResult )
136                         {
137                             OutputDebugStringFormat( "    Found value [%s] in OpenWithList of [%s].\n", szBuffer, lpSubKey );
138                             if ( strncmp( szBuffer, "WordPad.exe", 11 ) == 0 )
139                             {   // We will replace registration for word pad
140                                 bRet = true;
141                             }
142                             else if ( nSize > 0 )
143                                 bRet = false;
144                         }
145                     }
146                 }
147                 else
148                 {
149                     OutputDebugStringFormat( "  No OpenWithList found!\n" );
150                 }
151             }
152         }
153         else    // no default value found -> return TRUE to register for that key
154             bRet = true;
155 
156 		RegCloseKey( hKey );
157 	}
158     else // no key found -> return TRUE to register for that key
159         bRet = true;
160 
161     return bRet;
162 }
163 
164 //----------------------------------------------------------
165 static LONG DeleteSubKeyTree( HKEY RootKey, LPCSTR lpKey )
166 {
167 	HKEY hKey;
168 	LONG rc = RegOpenKeyExA( RootKey, lpKey, 0, KEY_READ | DELETE, &hKey );
169 
170 	if (ERROR_SUCCESS == rc)
171 	{
172 		LPCSTR    lpSubKey;
173 		DWORD     nMaxSubKeyLen;
174 
175 		rc = RegQueryInfoKeyA( hKey, 0, 0, 0, 0, &nMaxSubKeyLen, 0, 0, 0, 0, 0, 0 );
176 		nMaxSubKeyLen++; // space for trailing '\0'
177 		lpSubKey = reinterpret_cast<CHAR*>( _alloca( nMaxSubKeyLen*sizeof(CHAR) ) );
178 
179 		while (ERROR_SUCCESS == rc)
180         {
181 			DWORD nLen = nMaxSubKeyLen;
182 			rc = RegEnumKeyExA( hKey, 0, (LPSTR)lpSubKey, &nLen, 0, 0, 0, 0);    // always index zero
183 
184             if ( ERROR_NO_MORE_ITEMS == rc )
185             {
186 				rc = RegDeleteKeyA( RootKey, lpKey );
187                 if ( rc == ERROR_SUCCESS )
188                     OutputDebugStringFormat( "deleted key [%s] from registry.\n", lpKey );
189                 else
190                     OutputDebugStringFormat( "RegDeleteKeyA %s returned %ld.\n", lpKey, rc );
191                 break;
192             }
193             else if ( rc == ERROR_SUCCESS )
194 			{
195 				rc = DeleteSubKeyTree( hKey, lpSubKey );
196                 if ( ERROR_SUCCESS != rc )
197                     OutputDebugStringFormat( "RegDeleteKeyA %s returned %ld.\n", lpSubKey, rc );
198 			}
199 
200 		}
201         RegCloseKey(hKey);
202 	}
203     else
204     {
205         OutputDebugStringFormat( "RegOpenKeyExA %s returned %ld.\n", lpKey, rc );
206     }
207 
208 	return rc;
209 }
210 
211 //----------------------------------------------------------
212 static BOOL RemoveExtensionInRegistry( LPCSTR lpSubKey )
213 {
214     CHAR    szBuffer[4096];
215     DWORD   nSize = sizeof( szBuffer );
216     HKEY    hKey = NULL;
217     HKEY    hSubKey = NULL;
218     LONG    lResult = RegOpenKeyExA( HKEY_LOCAL_MACHINE, "SOFTWARE\\Classes", 0, KEY_QUERY_VALUE, &hKey );
219 
220 	if ( ERROR_SUCCESS == lResult )
221     {
222 		lResult = RegOpenKeyExA( hKey, lpSubKey, 0, KEY_QUERY_VALUE, &hSubKey );
223 
224         if ( ERROR_SUCCESS == lResult )
225         {
226             DWORD nSubKeys = 1;
227             szBuffer[0] = '\0';
228 
229             // we get the value of the default key fist and while we are on querying,
230             // we ask for the subkey count, too
231             lResult = RegQueryValueExA( hSubKey, "", NULL, NULL, (LPBYTE)szBuffer, &nSize );
232             if ( ERROR_SUCCESS == lResult )
233                 RegQueryInfoKeyA( hSubKey, 0, 0, 0, &nSubKeys, 0, 0, 0, 0, 0, 0, 0 );
234             RegCloseKey( hSubKey );
235 
236             // we will remove all key with an default value starting with ooostub but
237             // we have to be careful about MSO keys
238             if ( strncmp( szBuffer, "opendocument.", 13 ) == 0 )
239             {
240                 if ( nSubKeys == 0 )
241                 {
242                     DeleteSubKeyTree( hKey, lpSubKey );
243                 }
244                 else
245                 {
246                     lResult = RegOpenKeyExA( hKey, lpSubKey, 0, KEY_SET_VALUE, &hSubKey );
247                     if ( ERROR_SUCCESS == lResult )
248                         RegDeleteValueA( hSubKey, "" );
249                     else
250                         OutputDebugStringFormat( "Could not open key %s for deleting: RegOpenKeyEx returned %ld.\n", lpSubKey, lResult );
251                 }
252             }
253         }
254 
255         RegCloseKey( hKey );
256     }
257 
258     return ( ERROR_SUCCESS == lResult );
259 }
260 
261 //----------------------------------------------------------
262 bool GetMsiProp( MSIHANDLE handle, LPCSTR name, /*out*/std::string& value )
263 {
264     DWORD sz = 0;
265     LPSTR dummy = "";
266     if (MsiGetPropertyA(handle, name, dummy, &sz) == ERROR_MORE_DATA)
267     {
268         sz++;
269         DWORD nbytes = sz * sizeof(TCHAR);
270         LPSTR buff = reinterpret_cast<LPSTR>(_alloca(nbytes));
271         ZeroMemory(buff, nbytes);
272         MsiGetPropertyA(handle, name, buff, &sz);
273         value = buff;
274         return true;
275     }
276     return false;
277 }
278 
279 //----------------------------------------------------------
280 bool IsSetMsiProp( MSIHANDLE handle, LPCSTR name )
281 {
282     std::string val;
283     GetMsiProp( handle, name, val );
284     return (val == "1");
285 }
286 
287 //----------------------------------------------------------
288 static void registerForExtension( MSIHANDLE handle, const int nIndex, bool bRegister )
289 {
290     CHAR sPropName[256];
291     StringCchCopyA( sPropName, 256, "REGISTER_" );
292     StringCchCatA( sPropName, 256, (g_Extensions[nIndex])+1 );
293     CharUpperBuffA( sPropName+9, 4 );
294 
295     if ( bRegister ) {
296         MsiSetPropertyA( handle, sPropName, "1" );
297         OutputDebugStringFormat( "Set MSI property %s.\n", sPropName );
298     } else {
299         MsiSetPropertyA( handle, sPropName, "0" );
300         OutputDebugStringFormat( "Unset MSI property %s.\n", sPropName );
301     }
302 }
303 
304 //----------------------------------------------------------
305 static void registerForExtensions( MSIHANDLE handle, BOOL bRegisterAll )
306 { // Check all file extensions
307     int nIndex = 0;
308     while ( g_Extensions[nIndex] != 0 )
309     {
310         BOOL bRegister = bRegisterAll || CheckExtensionInRegistry( g_Extensions[nIndex] );
311         if ( bRegister )
312             registerForExtension( handle, nIndex, true );
313         ++nIndex;
314     }
315 }
316 
317 //----------------------------------------------------------
318 static bool checkSomeExtensionInRegistry( const int nStart, const int nEnd )
319 { // Check all file extensions
320     int nIndex = nStart;
321     bool bFound = false;
322 
323     while ( !bFound && ( g_Extensions[nIndex] != 0 ) && ( nIndex < nEnd ) )
324     {
325         bFound = ! CheckExtensionInRegistry( g_Extensions[nIndex] );
326 
327         if ( bFound )
328             OutputDebugStringFormat( "Found registration for [%s].\n", g_Extensions[nIndex] );
329 
330         ++nIndex;
331     }
332     return bFound;
333 }
334 
335 //----------------------------------------------------------
336 static void registerSomeExtensions( MSIHANDLE handle, const int nStart, const int nEnd, bool bRegister )
337 { // Check all file extensions
338     int nIndex = nStart;
339 
340     while ( ( g_Extensions[nIndex] != 0 ) && ( nIndex < nEnd ) )
341     {
342         registerForExtension( handle, nIndex++, bRegister );
343     }
344 }
345 
346 //----------------------------------------------------------
347 //----------------------------------------------------------
348 //----------------------------------------------------------
349 extern "C" UINT __stdcall LookForRegisteredExtensions( MSIHANDLE handle )
350 {
351     OutputDebugStringFormat( "LookForRegisteredExtensions: " );
352 
353     INSTALLSTATE current_state;
354     INSTALLSTATE future_state;
355 
356     bool bWriterEnabled = false;
357     bool bCalcEnabled = false;
358     bool bImpressEnabled = false;
359     bool bRegisterNone = IsSetMsiProp( handle, "REGISTER_NO_MSO_TYPES" );
360 
361     if ( ( ERROR_SUCCESS == MsiGetFeatureState( handle, L"gm_p_Wrt", &current_state, &future_state ) ) &&
362          ( (future_state == INSTALLSTATE_LOCAL) || ((current_state == INSTALLSTATE_LOCAL) && (future_state == INSTALLSTATE_UNKNOWN) ) ) )
363         bWriterEnabled = true;
364 
365     OutputDebugStringFormat( "LookForRegisteredExtensions: Install state Writer is [%d], will be [%d]", current_state, future_state );
366     if ( bWriterEnabled )
367         OutputDebugStringFormat( "LookForRegisteredExtensions: Writer is enabled" );
368     else
369         OutputDebugStringFormat( "LookForRegisteredExtensions: Writer is NOT enabled" );
370 
371     if ( ( ERROR_SUCCESS == MsiGetFeatureState( handle, L"gm_p_Calc", &current_state, &future_state ) ) &&
372          ( (future_state == INSTALLSTATE_LOCAL) || ((current_state == INSTALLSTATE_LOCAL) && (future_state == INSTALLSTATE_UNKNOWN) ) ) )
373         bCalcEnabled = true;
374 
375     OutputDebugStringFormat( "LookForRegisteredExtensions: Install state Calc is [%d], will be [%d]", current_state, future_state );
376     if ( bCalcEnabled )
377         OutputDebugStringFormat( "LookForRegisteredExtensions: Calc is enabled" );
378     else
379         OutputDebugStringFormat( "LookForRegisteredExtensions: Calc is NOT enabled" );
380 
381     if ( ( ERROR_SUCCESS == MsiGetFeatureState( handle, L"gm_p_Impress", &current_state, &future_state ) ) &&
382          ( (future_state == INSTALLSTATE_LOCAL) || ((current_state == INSTALLSTATE_LOCAL) && (future_state == INSTALLSTATE_UNKNOWN) ) ) )
383         bImpressEnabled = true;
384 
385     OutputDebugStringFormat( "LookForRegisteredExtensions: Install state Impress is [%d], will be [%d]", current_state, future_state );
386     if ( bImpressEnabled )
387         OutputDebugStringFormat( "LookForRegisteredExtensions: Impress is enabled" );
388     else
389         OutputDebugStringFormat( "LookForRegisteredExtensions: Impress is NOT enabled" );
390 
391     MsiSetPropertyA( handle, "SELECT_WORD", "" );
392     MsiSetPropertyA( handle, "SELECT_EXCEL", "" );
393     MsiSetPropertyA( handle, "SELECT_POWERPOINT", "" );
394 
395     if ( ! bRegisterNone )
396     {
397         if ( IsSetMsiProp( handle, "REGISTER_ALL_MSO_TYPES" ) )
398         {
399             if ( bWriterEnabled )
400                 MsiSetPropertyA( handle, "SELECT_WORD", "1" );
401             if ( bCalcEnabled )
402                 MsiSetPropertyA( handle, "SELECT_EXCEL", "1" );
403             if ( bImpressEnabled )
404                 MsiSetPropertyA( handle, "SELECT_POWERPOINT", "1" );
405         }
406         else
407         {
408             if ( bWriterEnabled && ! checkSomeExtensionInRegistry( WORD_START, EXCEL_START ) )
409             {
410                 MsiSetPropertyA( handle, "SELECT_WORD", "1" );
411                 OutputDebugStringFormat( "LookForRegisteredExtensions: Register for MicroSoft Word" );
412             }
413             if ( bCalcEnabled && ! checkSomeExtensionInRegistry( EXCEL_START, POWERPOINT_START ) )
414             {
415                 MsiSetPropertyA( handle, "SELECT_EXCEL", "1" );
416                 OutputDebugStringFormat( "LookForRegisteredExtensions: Register for MicroSoft Excel" );
417             }
418             if ( bImpressEnabled && ! checkSomeExtensionInRegistry( POWERPOINT_START, POWERPOINT_END ) )
419             {
420                 MsiSetPropertyA( handle, "SELECT_POWERPOINT", "1" );
421                 OutputDebugStringFormat( "LookForRegisteredExtensions: Register for MicroSoft PowerPoint" );
422             }
423         }
424     }
425 
426     MsiSetPropertyA( handle, "FILETYPEDIALOGUSED", "1" );
427 
428     return ERROR_SUCCESS;
429 }
430 
431 //----------------------------------------------------------
432 extern "C" UINT __stdcall RegisterSomeExtensions( MSIHANDLE handle )
433 {
434     OutputDebugStringFormat( "RegisterSomeExtensions: " );
435 
436     if ( IsSetMsiProp( handle, "SELECT_WORD" ) )
437     {
438         registerSomeExtensions( handle, WORD_START, EXCEL_START, true );
439         MsiSetFeatureState( handle, L"gm_p_Wrt_MSO_Reg", INSTALLSTATE_LOCAL );
440         OutputDebugStringFormat( "RegisterSomeExtensions: Register for MicroSoft Word" );
441     }
442     else
443     {
444         registerSomeExtensions( handle, WORD_START, EXCEL_START, false );
445         MsiSetFeatureState( handle, L"gm_p_Wrt_MSO_Reg", INSTALLSTATE_ABSENT );
446     }
447 
448     if ( IsSetMsiProp( handle, "SELECT_EXCEL" ) )
449     {
450         registerSomeExtensions( handle, EXCEL_START, POWERPOINT_START, true );
451         MsiSetFeatureState( handle, L"gm_p_Calc_MSO_Reg", INSTALLSTATE_LOCAL );
452         OutputDebugStringFormat( "RegisterSomeExtensions: Register for MicroSoft Excel" );
453     }
454     else
455     {
456         registerSomeExtensions( handle, EXCEL_START, POWERPOINT_START, false );
457         MsiSetFeatureState( handle, L"gm_p_Calc_MSO_Reg", INSTALLSTATE_ABSENT );
458     }
459 
460     if ( IsSetMsiProp( handle, "SELECT_POWERPOINT" ) )
461     {
462         registerSomeExtensions( handle, POWERPOINT_START, POWERPOINT_END, true );
463         MsiSetFeatureState( handle, L"gm_p_Impress_MSO_Reg", INSTALLSTATE_LOCAL );
464         OutputDebugStringFormat( "RegisterSomeExtensions: Register for MicroSoft PowerPoint" );
465     }
466     else
467     {
468         registerSomeExtensions( handle, POWERPOINT_START, POWERPOINT_END, false );
469         MsiSetFeatureState( handle, L"gm_p_Impress_MSO_Reg", INSTALLSTATE_ABSENT );
470     }
471 
472     return ERROR_SUCCESS;
473 }
474 
475 //----------------------------------------------------------
476 extern "C" UINT __stdcall FindRegisteredExtensions( MSIHANDLE handle )
477 {
478     if ( IsSetMsiProp( handle, "FILETYPEDIALOGUSED" ) )
479     {
480         OutputDebugStringFormat( "FindRegisteredExtensions: FILETYPEDIALOGUSED!" );
481         return ERROR_SUCCESS;
482     }
483 
484     OutputDebugStringFormat( "FindRegisteredExtensions:" );
485 
486     bool bRegisterAll = IsSetMsiProp( handle, "REGISTER_ALL_MSO_TYPES" );
487 
488     if ( IsSetMsiProp( handle, "REGISTER_NO_MSO_TYPES" ) )
489     {
490         OutputDebugStringFormat( "FindRegisteredExtensions: Register none!" );
491         return ERROR_SUCCESS;
492     }
493     else if ( bRegisterAll )
494         OutputDebugStringFormat( "FindRegisteredExtensions: Force all on" );
495     else
496         OutputDebugStringFormat( "FindRegisteredExtensions: " );
497 
498     // setting the msi properties SELECT_* will force registering for all corresponding
499     // file types
500     if ( IsSetMsiProp( handle, "SELECT_WORD" ) )
501         registerSomeExtensions( handle, WORD_START, EXCEL_START, true );
502     if ( IsSetMsiProp( handle, "SELECT_EXCEL" ) )
503         registerSomeExtensions( handle, EXCEL_START, POWERPOINT_START, true );
504     if ( IsSetMsiProp( handle, "SELECT_POWERPOINT" ) )
505         registerSomeExtensions( handle, POWERPOINT_START, POWERPOINT_END, true );
506 
507     registerForExtensions( handle, bRegisterAll );
508 
509     return ERROR_SUCCESS;
510 }
511 
512 //----------------------------------------------------------
513 extern "C" UINT __stdcall DeleteRegisteredExtensions( MSIHANDLE /*handle*/ )
514 {
515     OutputDebugStringFormat( "DeleteRegisteredExtensions\n" );
516 
517     // remove all file extensions
518     int nIndex = 0;
519     while ( g_Extensions[nIndex] != 0 )
520     {
521         RemoveExtensionInRegistry( g_Extensions[nIndex] );
522         ++nIndex;
523     }
524 
525     return ERROR_SUCCESS;
526 }
527