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