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