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