xref: /trunk/main/sc/source/ui/unoobj/scdetect.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sc.hxx"
30 
31 #include "scdetect.hxx"
32 
33 #include <framework/interaction.hxx>
34 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
35 #include <com/sun/star/beans/PropertyValue.hpp>
36 #include <com/sun/star/frame/XFrame.hpp>
37 #include <com/sun/star/frame/XModel.hpp>
38 #include <com/sun/star/awt/XWindow.hpp>
39 #include <com/sun/star/lang/XUnoTunnel.hpp>
40 #ifndef _UNOTOOLS_PROCESSFACTORY_HXX
41 #include <comphelper/processfactory.hxx>
42 #endif
43 #include <com/sun/star/beans/PropertyValue.hpp>
44 #include <com/sun/star/container/XNameAccess.hpp>
45 #include <com/sun/star/io/XInputStream.hpp>
46 #include <com/sun/star/task/XInteractionHandler.hpp>
47 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
48 #include <com/sun/star/ucb/CommandAbortedException.hpp>
49 #include <com/sun/star/ucb/InteractiveAppException.hpp>
50 #include <com/sun/star/ucb/XContent.hpp>
51 #include <com/sun/star/packages/zip/ZipIOException.hpp>
52 
53 
54 #include <framework/interaction.hxx>
55 
56 #ifndef _TOOLKIT_UNOHLP_HXX
57 #include <toolkit/helper/vclunohelper.hxx>
58 #endif
59 #include <ucbhelper/simpleinteractionrequest.hxx>
60 
61 #include <svtools/parhtml.hxx>
62 #include <rtl/ustring.h>
63 #include <rtl/logfile.hxx>
64 #include <svl/itemset.hxx>
65 #include <vcl/window.hxx>
66 #include <svl/eitem.hxx>
67 #include <svl/stritem.hxx>
68 #include <tools/urlobj.hxx>
69 #include <vos/mutex.hxx>
70 #include <svtools/sfxecode.hxx>
71 #include <svtools/ehdl.hxx>
72 #include <sot/storinfo.hxx>
73 #include <vcl/svapp.hxx>
74 #include <sfx2/sfxsids.hrc>
75 #include <sfx2/request.hxx>
76 #include <sfx2/docfile.hxx>
77 #include <sfx2/docfilt.hxx>
78 #include <sfx2/fcontnr.hxx>
79 #include <sfx2/app.hxx>
80 #include <sfx2/brokenpackageint.hxx>
81 #include <sot/storage.hxx>
82 
83 using namespace ::com::sun::star;
84 using namespace ::com::sun::star::uno;
85 using namespace ::com::sun::star::io;
86 using namespace ::com::sun::star::frame;
87 using namespace ::com::sun::star::task;
88 using namespace ::com::sun::star::beans;
89 using namespace ::com::sun::star::lang;
90 using namespace ::com::sun::star::ucb;
91 using ::rtl::OUString;
92 
93 ScFilterDetect::ScFilterDetect( const REFERENCE < ::com::sun::star::lang::XMultiServiceFactory >& /* xFactory */ )
94 {
95 }
96 
97 ScFilterDetect::~ScFilterDetect()
98 {
99 }
100 
101 static const sal_Char __FAR_DATA pFilterSc50[]		= "StarCalc 5.0";
102 static const sal_Char __FAR_DATA pFilterSc50Temp[]	= "StarCalc 5.0 Vorlage/Template";
103 static const sal_Char __FAR_DATA pFilterSc40[]		= "StarCalc 4.0";
104 static const sal_Char __FAR_DATA pFilterSc40Temp[]	= "StarCalc 4.0 Vorlage/Template";
105 static const sal_Char __FAR_DATA pFilterSc30[]		= "StarCalc 3.0";
106 static const sal_Char __FAR_DATA pFilterSc30Temp[]	= "StarCalc 3.0 Vorlage/Template";
107 static const sal_Char __FAR_DATA pFilterSc10[]		= "StarCalc 1.0";
108 static const sal_Char __FAR_DATA pFilterXML[]		= "StarOffice XML (Calc)";
109 static const sal_Char __FAR_DATA pFilterAscii[]		= "Text - txt - csv (StarCalc)";
110 static const sal_Char __FAR_DATA pFilterLotus[]		= "Lotus";
111 static const sal_Char __FAR_DATA pFilterQPro6[]		= "Quattro Pro 6.0";
112 static const sal_Char __FAR_DATA pFilterExcel4[]	= "MS Excel 4.0";
113 static const sal_Char __FAR_DATA pFilterEx4Temp[]	= "MS Excel 4.0 Vorlage/Template";
114 static const sal_Char __FAR_DATA pFilterExcel5[]	= "MS Excel 5.0/95";
115 static const sal_Char __FAR_DATA pFilterEx5Temp[]	= "MS Excel 5.0/95 Vorlage/Template";
116 static const sal_Char __FAR_DATA pFilterExcel95[]	= "MS Excel 95";
117 static const sal_Char __FAR_DATA pFilterEx95Temp[]	= "MS Excel 95 Vorlage/Template";
118 static const sal_Char __FAR_DATA pFilterExcel97[]	= "MS Excel 97";
119 static const sal_Char __FAR_DATA pFilterEx97Temp[]	= "MS Excel 97 Vorlage/Template";
120 static const sal_Char __FAR_DATA pFilterDBase[]		= "dBase";
121 static const sal_Char __FAR_DATA pFilterDif[]		= "DIF";
122 static const sal_Char __FAR_DATA pFilterSylk[]		= "SYLK";
123 static const sal_Char __FAR_DATA pFilterHtml[]		= "HTML (StarCalc)";
124 static const sal_Char __FAR_DATA pFilterHtmlWeb[]	= "calc_HTML_WebQuery";
125 static const sal_Char __FAR_DATA pFilterRtf[]		= "Rich Text Format (StarCalc)";
126 
127 
128 static sal_Bool lcl_MayBeAscii( SvStream& rStream )
129 {
130     // ASCII/CSV is considered possible if there are no null bytes, or a Byte
131     // Order Mark is present, or if, for Unicode UCS2/UTF-16, all null bytes
132     // are on either even or uneven byte positions.
133 
134 	rStream.Seek(STREAM_SEEK_TO_BEGIN);
135 
136     const size_t nBufSize = 2048;
137 	sal_uInt16 aBuffer[ nBufSize ];
138     sal_uInt8* pByte = reinterpret_cast<sal_uInt8*>(aBuffer);
139 	sal_uLong nBytesRead = rStream.Read( pByte, nBufSize*2);
140 
141 	if ( nBytesRead >= 2 && (aBuffer[0] == 0xfffe || aBuffer[0] == 0xfeff) )
142 	{
143         // Unicode BOM file may contain null bytes.
144 		return sal_True;
145 	}
146 
147 	const sal_uInt16* p = aBuffer;
148     sal_uInt16 nMask = 0xffff;
149     nBytesRead /= 2;
150 	while( nBytesRead-- && nMask )
151     {
152         sal_uInt16 nVal = *p++ & nMask;
153         if (!(nVal & 0x00ff))
154             nMask &= 0xff00;
155         if (!(nVal & 0xff00))
156             nMask &= 0x00ff;
157     }
158 
159 	return nMask != 0;
160 }
161 
162 static sal_Bool lcl_MayBeDBase( SvStream& rStream )
163 {
164     // Look for dbf marker, see connectivity/source/inc/dbase/DTable.hxx
165     // DBFType for values.
166     const sal_uInt8 nValidMarks[] = {
167         0x03, 0x04, 0x05, 0x30, 0x43, 0xB3, 0x83, 0x8b, 0x8e, 0xf5 };
168     sal_uInt8 nMark;
169     rStream.Seek(STREAM_SEEK_TO_BEGIN);
170     rStream >> nMark;
171     bool bValidMark = false;
172     for (size_t i=0; i < sizeof(nValidMarks)/sizeof(nValidMarks[0]) && !bValidMark; ++i)
173     {
174         if (nValidMarks[i] == nMark)
175             bValidMark = true;
176     }
177     if ( !bValidMark )
178         return sal_False;
179 
180     const size_t nHeaderBlockSize = 32;
181     // Empty dbf is >= 32*2+1 bytes in size.
182     const size_t nEmptyDbf = nHeaderBlockSize * 2 + 1;
183 
184 	rStream.Seek(STREAM_SEEK_TO_END);
185 	sal_uLong nSize = rStream.Tell();
186 	if ( nSize < nEmptyDbf )
187 		return sal_False;
188 
189 	// length of header starts at 8
190 	rStream.Seek(8);
191 	sal_uInt16 nHeaderLen;
192 	rStream >> nHeaderLen;
193 
194 	if ( nHeaderLen < nEmptyDbf || nSize < nHeaderLen )
195 		return sal_False;
196 
197     // Last byte of header must be 0x0d, this is how it's specified.
198     // #i9581#,#i26407# but some applications don't follow the specification
199     // and pad the header with one byte 0x00 to reach an
200     // even boundary. Some (#i88577# ) even pad more or pad using a 0x1a ^Z
201     // control character (#i8857#). This results in:
202     // Last byte of header must be 0x0d on 32 bytes boundary.
203 	sal_uInt16 nBlocks = (nHeaderLen - 1) / nHeaderBlockSize;
204 	sal_uInt8 nEndFlag = 0;
205 	while ( nBlocks > 1 && nEndFlag != 0x0d ) {
206 		rStream.Seek( nBlocks-- * nHeaderBlockSize );
207 		rStream >> nEndFlag;
208 	}
209 
210 	return ( 0x0d == nEndFlag );
211 }
212 
213 #if 0
214 static sal_Bool lcl_IsAnyXMLFilter( const SfxFilter* pFilter )
215 {
216 	if ( !pFilter )
217 		return sal_False;
218 
219 	//	sal_True for XML file or template
220 	//	(template filter has no internal name -> allow configuration key names)
221 
222 	String aName(pFilter->GetFilterName());
223 	return aName.EqualsAscii(pFilterXML) ||
224 		   aName.EqualsAscii("calc_StarOffice_XML_Calc") ||
225 		   aName.EqualsAscii("calc_StarOffice_XML_Calc_Template");
226 }
227 #endif
228 
229 ::rtl::OUString SAL_CALL ScFilterDetect::detect( ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& lDescriptor ) throw( ::com::sun::star::uno::RuntimeException )
230 {
231     REFERENCE< XInputStream > xStream;
232     REFERENCE< XContent > xContent;
233     REFERENCE< XInteractionHandler > xInteraction;
234     String aURL;
235 	::rtl::OUString sTemp;
236     String aTypeName;            // a name describing the type (from MediaDescriptor, usually from flat detection)
237     String aPreselectedFilterName;      // a name describing the filter to use (from MediaDescriptor, usually from UI action)
238 
239 	::rtl::OUString aDocumentTitle; // interesting only if set in this method
240 
241 	// opening as template is done when a parameter tells to do so and a template filter can be detected
242     // (otherwise no valid filter would be found) or if the detected filter is a template filter and
243 	// there is no parameter that forbids to open as template
244 	sal_Bool bOpenAsTemplate = sal_False;
245     sal_Bool bWasReadOnly = sal_False, bReadOnly = sal_False;
246 
247 	sal_Bool bRepairPackage = sal_False;
248 	sal_Bool bRepairAllowed = sal_False;
249 
250 	// now some parameters that can already be in the array, but may be overwritten or new inserted here
251 	// remember their indices in the case new values must be added to the array
252 	sal_Int32 nPropertyCount = lDescriptor.getLength();
253     sal_Int32 nIndexOfFilterName = -1;
254     sal_Int32 nIndexOfInputStream = -1;
255     sal_Int32 nIndexOfContent = -1;
256     sal_Int32 nIndexOfReadOnlyFlag = -1;
257     sal_Int32 nIndexOfTemplateFlag = -1;
258     sal_Int32 nIndexOfDocumentTitle = -1;
259     bool bFakeXLS = false;
260 
261     for( sal_Int32 nProperty=0; nProperty<nPropertyCount; ++nProperty )
262 	{
263         // extract properties
264         if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("URL")) )
265 		{
266 			lDescriptor[nProperty].Value >>= sTemp;
267 			aURL = sTemp;
268 		}
269         else if( !aURL.Len() && lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("FileName")) )
270 		{
271 			lDescriptor[nProperty].Value >>= sTemp;
272 			aURL = sTemp;
273 		}
274         else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("TypeName")) )
275 		{
276 			lDescriptor[nProperty].Value >>= sTemp;
277             aTypeName = sTemp;
278 		}
279         else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("FilterName")) )
280 		{
281 			lDescriptor[nProperty].Value >>= sTemp;
282             aPreselectedFilterName = sTemp;
283 
284             // if the preselected filter name is not correct, it must be erased after detection
285             // remember index of property to get access to it later
286             nIndexOfFilterName = nProperty;
287 		}
288         else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("InputStream")) )
289             nIndexOfInputStream = nProperty;
290         else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("ReadOnly")) )
291             nIndexOfReadOnlyFlag = nProperty;
292         else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("UCBContent")) )
293             nIndexOfContent = nProperty;
294         else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("AsTemplate")) )
295 		{
296 			lDescriptor[nProperty].Value >>= bOpenAsTemplate;
297             nIndexOfTemplateFlag = nProperty;
298 		}
299         else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("InteractionHandler")) )
300             lDescriptor[nProperty].Value >>= xInteraction;
301         else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("RepairPackage")) )
302             lDescriptor[nProperty].Value >>= bRepairPackage;
303         else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("DocumentTitle")) )
304             nIndexOfDocumentTitle = nProperty;
305 	}
306 
307     // can't check the type for external filters, so set the "dont" flag accordingly
308     ::vos::OGuard aGuard( Application::GetSolarMutex() );
309     //SfxFilterFlags nMust = SFX_FILTER_IMPORT, nDont = SFX_FILTER_NOTINSTALLED;
310 
311     SfxAllItemSet *pSet = new SfxAllItemSet( SFX_APP()->GetPool() );
312     TransformParameters( SID_OPENDOC, lDescriptor, *pSet );
313     SFX_ITEMSET_ARG( pSet, pItem, SfxBoolItem, SID_DOC_READONLY, sal_False );
314 
315     bWasReadOnly = pItem && pItem->GetValue();
316 
317 	const SfxFilter* pFilter = 0;
318 	String aPrefix = String::CreateFromAscii( "private:factory/" );
319 	if( aURL.Match( aPrefix ) == aPrefix.Len() )
320 	{
321 		String aPattern( aPrefix );
322 		aPattern += String::CreateFromAscii("scalc");
323 		if ( aURL.Match( aPattern ) >= aPattern.Len() )
324 			pFilter = SfxFilter::GetDefaultFilterFromFactory( aURL );
325 	}
326 	else
327 	{
328 		// container for Calc filters
329 		SfxFilterMatcher aMatcher( String::CreateFromAscii("scalc") );
330 		if ( aPreselectedFilterName.Len() )
331 			pFilter = SfxFilter::GetFilterByName( aPreselectedFilterName );
332 		else if( aTypeName.Len() )
333 			pFilter = aMatcher.GetFilter4EA( aTypeName );
334 
335 	    // ctor of SfxMedium uses owner transition of ItemSet
336 	    SfxMedium aMedium( aURL, bWasReadOnly ? STREAM_STD_READ : STREAM_STD_READWRITE, sal_False, NULL, pSet );
337 	    aMedium.UseInteractionHandler( sal_True );
338 
339 	    sal_Bool bIsStorage = aMedium.IsStorage();
340 	    if ( aMedium.GetErrorCode() == ERRCODE_NONE )
341 	    {
342 	        // remember input stream and content and put them into the descriptor later
343 			// should be done here since later the medium can switch to a version
344 	        xStream.set(aMedium.GetInputStream());
345 			xContent.set(aMedium.GetContent());
346         	bReadOnly = aMedium.IsReadOnly();
347 
348 	        // maybe that IsStorage() already created an error!
349 	        if ( bIsStorage )
350 			{
351                 uno::Reference < embed::XStorage > xStorage(aMedium.GetStorage( sal_False ));
352 				if ( aMedium.GetLastStorageCreationState() != ERRCODE_NONE )
353 				{
354 					// error during storage creation means _here_ that the medium
355 					// is broken, but we can not handle it in medium since unpossibility
356 					// to create a storage does not _always_ means that the medium is broken
357 					aMedium.SetError( aMedium.GetLastStorageCreationState(), ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ) );
358 					if ( xInteraction.is() )
359 					{
360 						OUString empty;
361 						try
362 						{
363 							InteractiveAppException xException( empty,
364 															REFERENCE< XInterface >(),
365 															InteractionClassification_ERROR,
366 															aMedium.GetError() );
367 
368                             REFERENCE< XInteractionRequest > xRequest(
369 								new ucbhelper::SimpleInteractionRequest( makeAny( xException ),
370 																 	 ucbhelper::CONTINUATION_APPROVE ) );
371 							xInteraction->handle( xRequest );
372 						}
373 						catch ( Exception & ) {};
374 					}
375 				}
376                 else if ( xStorage.is() )
377 				{
378 					try
379 					{
380                         String aFilterName;
381                         if ( pFilter )
382                             aFilterName = pFilter->GetName();
383                         aTypeName = SfxFilter::GetTypeFromStorage( xStorage, pFilter ? pFilter->IsOwnTemplateFormat() : sal_False, &aFilterName );
384 					}
385 					catch( lang::WrappedTargetException& aWrap )
386 					{
387 						packages::zip::ZipIOException aZipException;
388 
389 						// repairing is done only if this type is requested from outside
390 						if ( ( aWrap.TargetException >>= aZipException ) && aTypeName.Len() )
391 						{
392 							if ( xInteraction.is() )
393 							{
394 								// the package is broken one
395        							aDocumentTitle = aMedium.GetURLObject().getName(
396 															INetURLObject::LAST_SEGMENT,
397 															true,
398 															INetURLObject::DECODE_WITH_CHARSET );
399 
400 								if ( !bRepairPackage )
401 								{
402                                     // ask the user whether he wants to try to repair
403                                     RequestPackageReparation aRequest( aDocumentTitle );
404                                     xInteraction->handle( aRequest.GetRequest() );
405                                     bRepairAllowed = aRequest.isApproved();
406 								}
407 
408 								if ( !bRepairAllowed )
409 								{
410 									// repair either not allowed or not successful
411                                     NotifyBrokenPackage aNotifyRequest( aDocumentTitle );
412                                     xInteraction->handle( aNotifyRequest.GetRequest() );
413 								}
414 							}
415 
416 							if ( !bRepairAllowed )
417 								aTypeName.Erase();
418 						}
419 					}
420 					catch( uno::RuntimeException& )
421 					{
422 						throw;
423 					}
424 					catch( uno::Exception& )
425 					{
426 						aTypeName.Erase();
427 					}
428 
429                    	if ( aTypeName.Len() )
430                        	pFilter = SfxFilterMatcher( String::CreateFromAscii("scalc") ).GetFilter4EA( aTypeName );
431 
432 				}
433 			}
434 			else
435 			{
436                 bool bIsXLS = false;
437                 SvStream* pStream = aMedium.GetInStream();
438                 const SfxFilter* pPreselectedFilter = pFilter;
439                 if ( pPreselectedFilter && pPreselectedFilter->GetName().SearchAscii("Excel") != STRING_NOTFOUND )
440                     bIsXLS = true;
441                 pFilter = 0;
442                 if ( pStream )
443                 {
444                     SotStorageRef aStorage = new SotStorage ( pStream, sal_False );
445                     if ( !aStorage->GetError() )
446                     {
447                         // Excel-5: detect through contained streams
448                         // there are some "excel" formats from 3rd party vendors that need to be distinguished
449                         String aStreamName(RTL_CONSTASCII_STRINGPARAM("Workbook"));
450                         sal_Bool bExcel97Stream = ( aStorage->IsStream( aStreamName ) );
451 
452                         aStreamName = String(RTL_CONSTASCII_STRINGPARAM("Book"));
453                         sal_Bool bExcel5Stream = ( aStorage->IsStream( aStreamName ) );
454                         if ( bExcel97Stream || bExcel5Stream )
455                         {
456                             if ( bExcel97Stream )
457                             {
458                                 String aOldName;
459                                 sal_Bool bIsCalcFilter = sal_True;
460                                 if ( pPreselectedFilter )
461                                 {
462                                     // cross filter; now this should be a type detection only, not a filter detection
463                                     // we can simulate it by preserving the preselected filter if the type matches
464                                     // example: Excel filters for Writer
465                                     aOldName = pPreselectedFilter->GetFilterName();
466                                     bIsCalcFilter = pPreselectedFilter->GetServiceName().EqualsAscii("com.sun.star.sheet.SpreadsheetDocument");
467                                 }
468 
469                                 if ( aOldName.EqualsAscii(pFilterEx97Temp) || !bIsCalcFilter )
470                                 {
471                                     //  Excel 97 template selected -> keep selection
472                                 }
473                                 else if ( bExcel5Stream &&
474                                             ( aOldName.EqualsAscii(pFilterExcel5) || aOldName.EqualsAscii(pFilterEx5Temp) ||
475                                             aOldName.EqualsAscii(pFilterExcel95) || aOldName.EqualsAscii(pFilterEx95Temp) ) )
476                                 {
477                                     //  dual format file and Excel 5 selected -> keep selection
478                                 }
479                                 else
480                                 {
481                                     //  else use Excel 97 filter
482                                     pFilter = aMatcher.GetFilter4FilterName( String::CreateFromAscii(pFilterExcel97) );
483                                 }
484                             }
485                             else if ( bExcel5Stream )
486                             {
487                                 String aOldName;
488                                 sal_Bool bIsCalcFilter = sal_True;
489                                 if ( pPreselectedFilter )
490                                 {
491                                     // cross filter; now this should be a type detection only, not a filter detection
492                                     // we can simulate it by preserving the preselected filter if the type matches
493                                     // example: Excel filters for Writer
494                                     aOldName = pPreselectedFilter->GetFilterName();
495                                     bIsCalcFilter = pPreselectedFilter->GetServiceName().EqualsAscii("com.sun.star.sheet.SpreadsheetDocument");
496                                 }
497 
498                                 if ( aOldName.EqualsAscii(pFilterExcel95) || aOldName.EqualsAscii(pFilterEx95Temp) ||
499                                         aOldName.EqualsAscii(pFilterEx5Temp) || !bIsCalcFilter )
500                                 {
501                                     //  Excel 95 oder Vorlage (5 oder 95) eingestellt -> auch gut
502                                 }
503                                 else if ( aOldName.EqualsAscii(pFilterEx97Temp) )
504                                 {
505                                     // #101923# auto detection has found template -> return Excel5 template
506                                     pFilter = aMatcher.GetFilter4FilterName( String::CreateFromAscii(pFilterEx5Temp) );
507                                 }
508                                 else
509                                 {
510                                     //  sonst wird als Excel 5-Datei erkannt
511                                     pFilter = aMatcher.GetFilter4FilterName( String::CreateFromAscii(pFilterExcel5) );
512                                 }
513                             }
514                         }
515                     }
516                     else
517                     {
518                         SvStream &rStr = *pStream;
519 
520                         // Tabelle mit Suchmustern
521                         // Bedeutung der Sequenzen
522                         // 0x00??: genau Byte 0x?? muss an dieser Stelle stehen
523                         // 0x0100: ein Byte ueberlesen (don't care)
524                         // 0x02nn: ein Byte aus 0xnn Alternativen folgt
525                         // 0x8000: Erkennung abgeschlossen
526                         //
527 
528         #define M_DC        0x0100
529         #define M_ALT(ANZ)  (0x0200+(ANZ))
530         #define M_ENDE      0x8000
531 
532                         static const sal_uInt16 pLotus[] =      // Lotus 1/1A/2
533                             { 0x0000, 0x0000, 0x0002, 0x0000,
534                             M_ALT(2), 0x0004, 0x0006,
535                             0x0004, M_ENDE };
536 
537                         static const sal_uInt16 pLotusNew[] =   // Lotus >= 9.7
538                             { 0x0000, 0x0000, M_DC, 0x0000,     // Rec# + Len (0x1a)
539                               M_ALT(3), 0x0003, 0x0004, 0x0005, // File Revision Code 97->ME
540                               0x0010, 0x0004, 0x0000, 0x0000,
541                               M_ENDE };
542 
543                         static const sal_uInt16 pExcel1[] =     // Excel BIFF2, BIFF3, BIFF4
544                             {   0x09,                                   // lobyte of BOF rec ID (0x0009, 0x0209, 0x0409)
545                                 M_ALT(3), 0x00, 0x02, 0x04,             // hibyte of BOF rec ID (0x0009, 0x0209, 0x0409)
546                                 M_ALT(3), 4, 6, 8,                      // lobyte of BOF rec size (4, 6, 8, 16)
547                                 0x00,                                   // hibyte of BOF rec size (4, 6, 8, 16)
548                                 M_DC, M_DC,                             // any version
549                                 M_ALT(3), 0x10, 0x20, 0x40,             // lobyte of data type (0x0010, 0x0020, 0x0040)
550                                 0x00,                                   // hibyte of data type (0x0010, 0x0020, 0x0040)
551                                 M_ENDE };
552 
553                         static const sal_uInt16 pExcel2[] =     // Excel BIFF4 Workspace
554                             {   0x09,                                   // lobyte of BOF rec ID (0x0409)
555                                 0x04,                                   // hibyte of BOF rec ID (0x0409)
556                                 M_ALT(3), 4, 6, 8,                      // lobyte of BOF rec size (4, 6, 8, 16)
557                                 0x00,                                   // hibyte of BOF rec size (4, 6, 8, 16)
558                                 M_DC, M_DC,                             // any version
559                                 0x00,                                   // lobyte of data type (0x0100)
560                                 0x01,                                   // hibyte of data type (0x0100)
561                                 M_ENDE };
562 
563                         static const sal_uInt16 pExcel3[] =     // #i23425# Excel BIFF5, BIFF7, BIFF8 (simple book stream)
564                             {   0x09,                                   // lobyte of BOF rec ID (0x0809)
565                                 0x08,                                   // hibyte of BOF rec ID (0x0809)
566                                 M_ALT(4), 4, 6, 8, 16,                  // lobyte of BOF rec size
567                                 0x00,                                   // hibyte of BOF rec size
568                                 M_DC, M_DC,                             // any version
569                                 M_ALT(5), 0x05, 0x06, 0x10, 0x20, 0x40, // lobyte of data type
570                                 0x00,                                   // hibyte of data type
571                                 M_ENDE };
572 
573                         static const sal_uInt16 pSc10[] =       // StarCalc 1.0 Dokumente
574                             { 'B', 'l', 'a', 'i', 's', 'e', '-', 'T', 'a', 'b', 'e', 'l', 'l',
575                             'e', 0x000A, 0x000D, 0x0000,    // Sc10CopyRight[16]
576                             M_DC, M_DC, M_DC, M_DC, M_DC, M_DC, M_DC, M_DC, M_DC, M_DC, M_DC,
577                             M_DC, M_DC,                   // Sc10CopyRight[29]
578                             M_ALT(2), 0x0065, 0x0066,     // Versionsnummer 101 oder 102
579                             0x0000,
580                             M_ENDE };
581 
582                         static const sal_uInt16 pLotus2[] =     // Lotus >3
583                             { 0x0000, 0x0000, 0x001A, 0x0000,   // Rec# + Len (26)
584                             M_ALT(2), 0x0000, 0x0002,         // File Revision Code
585                             0x0010,
586                             0x0004, 0x0000,                   // File Revision Subcode
587                             M_ENDE };
588 
589                         static const sal_uInt16 pQPro[] =
590                                { 0x0000, 0x0000, 0x0002, 0x0000,
591                                  M_ALT(4), 0x0001, 0x0002, // WB1, WB2
592                                  0x0006, 0x0007,           // QPro 6/7 (?)
593                                  0x0010,
594                                  M_ENDE };
595 
596                         static const sal_uInt16 pDIF1[] =       // DIF mit CR-LF
597                             {
598                             'T', 'A', 'B', 'L', 'E',
599                             M_DC, M_DC,
600                             '0', ',', '1',
601                             M_DC, M_DC,
602                             '\"',
603                             M_ENDE };
604 
605                         static const sal_uInt16 pDIF2[] =       // DIF mit CR oder LF
606                             {
607                             'T', 'A', 'B', 'L', 'E',
608                             M_DC,
609                             '0', ',', '1',
610                             M_DC,
611                             '\"',
612                             M_ENDE };
613 
614                         static const sal_uInt16 pSylk[] =       // Sylk
615                             {
616                             'I', 'D', ';',
617                             M_ALT(3), 'P', 'N', 'E',        // 'P' plus undocumented Excel extensions 'N' and 'E'
618                             M_ENDE };
619 
620                         static const sal_uInt16 *ppFilterPatterns[] =      // Arrays mit Suchmustern
621                             {
622                             pLotus,
623                             pExcel1,
624                             pExcel2,
625                             pExcel3,
626                             pSc10,
627                             pDIF1,
628                             pDIF2,
629                             pSylk,
630                             pLotusNew,
631                             pLotus2,
632                             pQPro
633                             };
634                         const sal_uInt16 nFilterCount = sizeof(ppFilterPatterns) / sizeof(ppFilterPatterns[0]);
635 
636                         static const sal_Char* const pFilterName[] =     // zugehoerige Filter
637                             {
638                             pFilterLotus,
639                             pFilterExcel4,
640                             pFilterExcel4,
641                             pFilterExcel4,
642                             pFilterSc10,
643                             pFilterDif,
644                             pFilterDif,
645                             pFilterSylk,
646                             pFilterLotus,
647                             pFilterLotus,
648                             pFilterQPro6
649                             };
650 
651                         // const sal_uInt16 nByteMask = 0xFF;
652 
653                         // suchen Sie jetzt!
654                         // ... realisiert ueber 'Mustererkennung'
655 
656                         sal_uInt8            nAkt;
657                         sal_Bool            bSync;          // Datei und Muster stimmen ueberein
658                         sal_uInt16          nFilter;        // Zaehler ueber alle Filter
659                         const sal_uInt16    *pSearch;       // aktuelles Musterwort
660 
661                         for ( nFilter = 0 ; nFilter < nFilterCount ; nFilter++ )
662                         {
663                             rStr.Seek( 0 ); // am Anfang war alles Uebel...
664                             rStr >> nAkt;
665                             pSearch = ppFilterPatterns[ nFilter ];
666                             bSync = sal_True;
667                             while( !rStr.IsEof() && bSync )
668                             {
669                                 register sal_uInt16 nMuster = *pSearch;
670 
671                                 if( nMuster < 0x0100 )
672                                 { //                                direkter Byte-Vergleich
673                                     if( ( sal_uInt8 ) nMuster != nAkt )
674                                         bSync = sal_False;
675                                 }
676                                 else if( nMuster & M_DC )
677                                 { //                                             don't care
678                                 }
679                                 else if( nMuster & M_ALT(0) )
680                                 { //                                      alternative Bytes
681                                     sal_uInt8 nAnzAlt = ( sal_uInt8 ) nMuster;
682                                     bSync = sal_False;          // zunaechst unsynchron
683                                     while( nAnzAlt > 0 )
684                                     {
685                                         pSearch++;
686                                         if( ( sal_uInt8 ) *pSearch == nAkt )
687                                             bSync = sal_True;   // jetzt erst Synchronisierung
688                                         nAnzAlt--;
689                                     }
690                                 }
691                                 else if( nMuster & M_ENDE )
692                                 { //                                        Format detected
693                                     if ( pFilterName[nFilter] == pFilterExcel4 && pPreselectedFilter &&
694                                         ( (pPreselectedFilter)->GetFilterName().EqualsAscii(pFilterEx4Temp) || pPreselectedFilter->GetTypeName().EqualsAscii("calc_MS_Excel_40") ) )
695                                     {
696                                         //  Excel 4 erkannt, Excel 4 Vorlage eingestellt -> auch gut
697                                         // oder Excel 4 Filter anderer Applikation (simulated type detection!)
698                                     }
699                                     else
700                                     {   // gefundenen Filter einstellen
701                                         pFilter = aMatcher.GetFilter4FilterName( String::CreateFromAscii(pFilterName[ nFilter ]) );
702                                     }
703                                     bSync = sal_False;              // leave inner loop
704                                     nFilter = nFilterCount;     // leave outer loop
705                                 }
706                                 else
707                                 { //                                         Tabellenfehler
708                                     DBG_ERROR( "-ScApplication::DetectFilter(): Fehler in Mustertabelle");
709                                 }
710 
711                                 pSearch++;
712                                 rStr >> nAkt;
713                             }
714                         }
715 
716                         if ( pPreselectedFilter && !pFilter )
717                         {
718                             // further checks for filters only if they are preselected: ASCII, HTML, RTF, DBase
719                             // without the preselection other filters (Writer) take precedence
720                             // DBase can't be detected reliably, so it also needs preselection
721                             bool bMaybeText = lcl_MayBeAscii( rStr );
722                             if ( pPreselectedFilter->GetFilterName().EqualsAscii(pFilterAscii) && bMaybeText )
723                             {
724                                 // Text filter is accepted if preselected
725                                 pFilter = pPreselectedFilter;
726                             }
727                             else
728                             {
729                                 // get file header
730                                 rStr.Seek( 0 );
731                                 const int nTrySize = 80;
732                                 ByteString aHeader;
733                                 for ( int j = 0; j < nTrySize && !rStr.IsEof(); j++ )
734                                 {
735                                     sal_Char c;
736                                     rStr >> c;
737                                     aHeader += c;
738                                 }
739                                 aHeader += '\0';
740 
741                                 if ( HTMLParser::IsHTMLFormat( aHeader.GetBuffer() ) )
742                                 {
743                                     // test for HTML
744                                     if ( pPreselectedFilter->GetName().EqualsAscii(pFilterHtml) )
745                                     {
746                                         pFilter = pPreselectedFilter;
747                                     }
748                                     else
749                                     {
750                                         pFilter = aMatcher.GetFilter4FilterName( String::CreateFromAscii(pFilterHtmlWeb) );
751                                         if ( bIsXLS )
752                                             bFakeXLS = true;
753                                     }
754                                 }
755                                 else if ( bIsXLS && bMaybeText )
756                                 {
757                                     pFilter = aMatcher.GetFilter4FilterName( String::CreateFromAscii(pFilterAscii) );
758                                     bFakeXLS = true;
759                                 }
760                                 else if ( aHeader.CompareTo( "{\\rtf", 5 ) == COMPARE_EQUAL )
761                                 {
762                                     // test for RTF
763                                     pFilter = aMatcher.GetFilter4FilterName( String::CreateFromAscii(pFilterRtf) );
764                                 }
765                                 else if ( pPreselectedFilter->GetName().EqualsAscii(pFilterDBase) && lcl_MayBeDBase( rStr ) )
766                                     pFilter = pPreselectedFilter;
767                             }
768                         }
769                     }
770 				}
771 			}
772 		}
773 	}
774 
775     if ( nIndexOfInputStream == -1 && xStream.is() )
776     {
777         // if input stream wasn't part of the descriptor, now it should be, otherwise the content would be opend twice
778         lDescriptor.realloc( nPropertyCount + 1 );
779         lDescriptor[nPropertyCount].Name = ::rtl::OUString::createFromAscii("InputStream");
780         lDescriptor[nPropertyCount].Value <<= xStream;
781         nPropertyCount++;
782     }
783 
784     if ( nIndexOfContent == -1 && xContent.is() )
785     {
786         // if input stream wasn't part of the descriptor, now it should be, otherwise the content would be opend twice
787         lDescriptor.realloc( nPropertyCount + 1 );
788         lDescriptor[nPropertyCount].Name = ::rtl::OUString::createFromAscii("UCBContent");
789         lDescriptor[nPropertyCount].Value <<= xContent;
790         nPropertyCount++;
791     }
792 
793     if ( bReadOnly != bWasReadOnly )
794     {
795         if ( nIndexOfReadOnlyFlag == -1 )
796         {
797             lDescriptor.realloc( nPropertyCount + 1 );
798             lDescriptor[nPropertyCount].Name = ::rtl::OUString::createFromAscii("ReadOnly");
799             lDescriptor[nPropertyCount].Value <<= bReadOnly;
800             nPropertyCount++;
801         }
802         else
803             lDescriptor[nIndexOfReadOnlyFlag].Value <<= bReadOnly;
804     }
805 
806 	if ( !bRepairPackage && bRepairAllowed )
807 	{
808         lDescriptor.realloc( nPropertyCount + 1 );
809         lDescriptor[nPropertyCount].Name = ::rtl::OUString::createFromAscii("RepairPackage");
810         lDescriptor[nPropertyCount].Value <<= bRepairAllowed;
811         nPropertyCount++;
812 
813 		bOpenAsTemplate = sal_True;
814 
815 		// TODO/LATER: set progress bar that should be used
816 	}
817 
818 	if ( bOpenAsTemplate )
819 	{
820 		if ( nIndexOfTemplateFlag == -1 )
821 		{
822         	lDescriptor.realloc( nPropertyCount + 1 );
823         	lDescriptor[nPropertyCount].Name = ::rtl::OUString::createFromAscii("AsTemplate");
824         	lDescriptor[nPropertyCount].Value <<= bOpenAsTemplate;
825         	nPropertyCount++;
826 		}
827 		else
828         	lDescriptor[nIndexOfTemplateFlag].Value <<= bOpenAsTemplate;
829 	}
830 
831 	if ( aDocumentTitle.getLength() )
832 	{
833 		// the title was set here
834 		if ( nIndexOfDocumentTitle == -1 )
835 		{
836         	lDescriptor.realloc( nPropertyCount + 1 );
837         	lDescriptor[nPropertyCount].Name = ::rtl::OUString::createFromAscii("DocumentTitle");
838         	lDescriptor[nPropertyCount].Value <<= aDocumentTitle;
839         	nPropertyCount++;
840 		}
841 		else
842         	lDescriptor[nIndexOfDocumentTitle].Value <<= aDocumentTitle;
843 	}
844 
845     if ( bFakeXLS )
846     {
847         if ( nIndexOfFilterName == -1 )
848         {
849             lDescriptor.realloc( nPropertyCount + 1 );
850             lDescriptor[nPropertyCount].Name = ::rtl::OUString::createFromAscii("FilterName");
851             lDescriptor[nPropertyCount].Value <<= rtl::OUString(pFilter->GetName());
852             nPropertyCount++;
853         }
854         else
855             lDescriptor[nIndexOfFilterName].Value <<= rtl::OUString(pFilter->GetName());
856     }
857 
858     if ( pFilter )
859         aTypeName = pFilter->GetTypeName();
860     else
861         aTypeName.Erase();
862     return aTypeName;
863 }
864 
865 SFX_IMPL_SINGLEFACTORY( ScFilterDetect )
866 
867 /* XServiceInfo */
868 UNOOUSTRING SAL_CALL ScFilterDetect::getImplementationName() throw( UNORUNTIMEEXCEPTION )
869 {
870     return impl_getStaticImplementationName();
871 }
872                                                                                                                                 \
873 /* XServiceInfo */
874 sal_Bool SAL_CALL ScFilterDetect::supportsService( const UNOOUSTRING& sServiceName ) throw( UNORUNTIMEEXCEPTION )
875 {
876     UNOSEQUENCE< UNOOUSTRING >  seqServiceNames(getSupportedServiceNames());
877     const UNOOUSTRING*          pArray          =   seqServiceNames.getConstArray();
878     for ( sal_Int32 nCounter=0; nCounter<seqServiceNames.getLength(); nCounter++ )
879     {
880         if ( pArray[nCounter] == sServiceName )
881         {
882             return sal_True ;
883         }
884     }
885     return sal_False ;
886 }
887 
888 /* XServiceInfo */
889 UNOSEQUENCE< UNOOUSTRING > SAL_CALL ScFilterDetect::getSupportedServiceNames() throw( UNORUNTIMEEXCEPTION )
890 {
891     return impl_getStaticSupportedServiceNames();
892 }
893 
894 /* Helper for XServiceInfo */
895 UNOSEQUENCE< UNOOUSTRING > ScFilterDetect::impl_getStaticSupportedServiceNames()
896 {
897     UNOMUTEXGUARD aGuard( UNOMUTEX::getGlobalMutex() );
898     UNOSEQUENCE< UNOOUSTRING > seqServiceNames( 1 );
899     seqServiceNames.getArray() [0] = UNOOUSTRING::createFromAscii( "com.sun.star.frame.ExtendedTypeDetection"  );
900     return seqServiceNames ;
901 }
902 
903 /* Helper for XServiceInfo */
904 UNOOUSTRING ScFilterDetect::impl_getStaticImplementationName()
905 {
906     return UNOOUSTRING::createFromAscii( "com.sun.star.comp.calc.FormatDetector" );
907 }
908 
909 /* Helper for registry */
910 UNOREFERENCE< UNOXINTERFACE > SAL_CALL ScFilterDetect::impl_createInstance( const UNOREFERENCE< UNOXMULTISERVICEFACTORY >& xServiceManager ) throw( UNOEXCEPTION )
911 {
912     return UNOREFERENCE< UNOXINTERFACE >( *new ScFilterDetect( xServiceManager ) );
913 }
914 
915