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