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