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_starmath.hxx" 26 27 #include "smdetect.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/io/XInputStream.hpp> 41 #include <com/sun/star/task/XInteractionHandler.hpp> 42 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> 43 #include <com/sun/star/ucb/CommandAbortedException.hpp> 44 #include <com/sun/star/ucb/InteractiveAppException.hpp> 45 #include <com/sun/star/ucb/XContent.hpp> 46 #include <com/sun/star/packages/zip/ZipIOException.hpp> 47 #include <framework/interaction.hxx> 48 49 #ifndef _TOOLKIT_UNOHLP_HXX 50 #include <toolkit/helper/vclunohelper.hxx> 51 #endif 52 #include <ucbhelper/simpleinteractionrequest.hxx> 53 54 #include <rtl/ustring.h> 55 #include <rtl/logfile.hxx> 56 #include <svl/itemset.hxx> 57 #include <vcl/window.hxx> 58 #include <svl/eitem.hxx> 59 #include <svl/stritem.hxx> 60 #include <tools/urlobj.hxx> 61 #include <vos/mutex.hxx> 62 #include <svtools/sfxecode.hxx> 63 #include <svtools/ehdl.hxx> 64 #include <sot/storinfo.hxx> 65 #include <vcl/svapp.hxx> 66 #include <sfx2/app.hxx> 67 #include <sfx2/sfxsids.hrc> 68 #include <sfx2/request.hxx> 69 #include <sfx2/docfile.hxx> 70 #include <sfx2/docfilt.hxx> 71 #include <sfx2/fcontnr.hxx> 72 #include <sfx2/brokenpackageint.hxx> 73 74 #include "document.hxx" 75 #include "eqnolefilehdr.hxx" 76 77 78 using namespace ::com::sun::star; 79 using namespace ::com::sun::star::uno; 80 using namespace ::com::sun::star::io; 81 using namespace ::com::sun::star::frame; 82 using namespace ::com::sun::star::task; 83 using namespace ::com::sun::star::beans; 84 using namespace ::com::sun::star::lang; 85 using namespace ::com::sun::star::ucb; 86 using namespace ::rtl; 87 88 SmFilterDetect::SmFilterDetect( const REFERENCE < ::com::sun::star::lang::XMultiServiceFactory >& /*xFactory*/ ) 89 { 90 } 91 92 SmFilterDetect::~SmFilterDetect() 93 { 94 } 95 96 ::rtl::OUString SAL_CALL SmFilterDetect::detect( ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& lDescriptor ) throw( ::com::sun::star::uno::RuntimeException ) 97 { 98 REFERENCE< XInputStream > xStream; 99 REFERENCE< XContent > xContent; 100 REFERENCE< XInteractionHandler > xInteraction; 101 String aURL; 102 ::rtl::OUString sTemp; 103 String aTypeName; // a name describing the type (from MediaDescriptor, usually from flat detection) 104 String aPreselectedFilterName; // a name describing the filter to use (from MediaDescriptor, usually from UI action) 105 106 ::rtl::OUString aDocumentTitle; // interesting only if set in this method 107 108 // opening as template is done when a parameter tells to do so and a template filter can be detected 109 // (otherwise no valid filter would be found) or if the detected filter is a template filter and 110 // there is no parameter that forbids to open as template 111 sal_Bool bOpenAsTemplate = sal_False; 112 sal_Bool bWasReadOnly = sal_False, bReadOnly = sal_False; 113 114 sal_Bool bRepairPackage = sal_False; 115 sal_Bool bRepairAllowed = sal_False; 116 117 // now some parameters that can already be in the array, but may be overwritten or new inserted here 118 // remember their indices in the case new values must be added to the array 119 sal_Int32 nPropertyCount = lDescriptor.getLength(); 120 sal_Int32 nIndexOfFilterName = -1; 121 sal_Int32 nIndexOfInputStream = -1; 122 sal_Int32 nIndexOfContent = -1; 123 sal_Int32 nIndexOfReadOnlyFlag = -1; 124 sal_Int32 nIndexOfTemplateFlag = -1; 125 sal_Int32 nIndexOfDocumentTitle = -1; 126 127 for( sal_Int32 nProperty=0; nProperty<nPropertyCount; ++nProperty ) 128 { 129 // extract properties 130 if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("URL")) ) 131 { 132 lDescriptor[nProperty].Value >>= sTemp; 133 aURL = sTemp; 134 } 135 else if( !aURL.Len() && lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("FileName")) ) 136 { 137 lDescriptor[nProperty].Value >>= sTemp; 138 aURL = sTemp; 139 } 140 else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("TypeName")) ) 141 { 142 lDescriptor[nProperty].Value >>= sTemp; 143 aTypeName = sTemp; 144 } 145 else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("FilterName")) ) 146 { 147 lDescriptor[nProperty].Value >>= sTemp; 148 aPreselectedFilterName = sTemp; 149 150 // if the preselected filter name is not correct, it must be erased after detection 151 // remember index of property to get access to it later 152 nIndexOfFilterName = nProperty; 153 } 154 else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("InputStream")) ) 155 nIndexOfInputStream = nProperty; 156 else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("ReadOnly")) ) 157 nIndexOfReadOnlyFlag = nProperty; 158 else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("UCBContent")) ) 159 nIndexOfContent = nProperty; 160 else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("AsTemplate")) ) 161 { 162 lDescriptor[nProperty].Value >>= bOpenAsTemplate; 163 nIndexOfTemplateFlag = nProperty; 164 } 165 else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("InteractionHandler")) ) 166 lDescriptor[nProperty].Value >>= xInteraction; 167 else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("RepairPackage")) ) 168 lDescriptor[nProperty].Value >>= bRepairPackage; 169 else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("DocumentTitle")) ) 170 nIndexOfDocumentTitle = nProperty; 171 } 172 173 // can't check the type for external filters, so set the "dont" flag accordingly 174 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 175 //SfxFilterFlags nMust = SFX_FILTER_IMPORT, nDont = SFX_FILTER_NOTINSTALLED; 176 177 SfxApplication* pApp = SFX_APP(); 178 SfxAllItemSet *pSet = new SfxAllItemSet( pApp->GetPool() ); 179 TransformParameters( SID_OPENDOC, lDescriptor, *pSet ); 180 SFX_ITEMSET_ARG( pSet, pItem, SfxBoolItem, SID_DOC_READONLY, sal_False ); 181 182 bWasReadOnly = pItem && pItem->GetValue(); 183 184 String aFilterName; 185 String aPrefix = String::CreateFromAscii( "private:factory/" ); 186 if( aURL.Match( aPrefix ) == aPrefix.Len() ) 187 { 188 const SfxFilter* pFilter = 0; 189 String aPattern( aPrefix ); 190 aPattern += String::CreateFromAscii("smath"); 191 if ( aURL.Match( aPattern ) >= aPattern.Len() ) 192 { 193 pFilter = SfxFilter::GetDefaultFilterFromFactory( aURL ); 194 aTypeName = pFilter->GetTypeName(); 195 aFilterName = pFilter->GetName(); 196 } 197 } 198 else 199 { 200 // ctor of SfxMedium uses owner transition of ItemSet 201 SfxMedium aMedium( aURL, bWasReadOnly ? STREAM_STD_READ : STREAM_STD_READWRITE, sal_False, NULL, pSet ); 202 aMedium.UseInteractionHandler( sal_True ); 203 204 sal_Bool bIsStorage = aMedium.IsStorage(); 205 if ( aMedium.GetErrorCode() == ERRCODE_NONE ) 206 { 207 // remember input stream and content and put them into the descriptor later 208 // should be done here since later the medium can switch to a version 209 xStream = aMedium.GetInputStream(); 210 xContent = aMedium.GetContent(); 211 bReadOnly = aMedium.IsReadOnly(); 212 213 if ( bIsStorage ) 214 { 215 //TODO/LATER: factor this out! 216 uno::Reference < embed::XStorage > xStorage = aMedium.GetStorage( sal_False ); 217 if ( aMedium.GetLastStorageCreationState() != ERRCODE_NONE ) 218 { 219 // error during storage creation means _here_ that the medium 220 // is broken, but we can not handle it in medium since unpossibility 221 // to create a storage does not _always_ means that the medium is broken 222 aMedium.SetError( aMedium.GetLastStorageCreationState(), ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ) ); 223 if ( xInteraction.is() ) 224 { 225 OUString empty; 226 try 227 { 228 InteractiveAppException xException( empty, 229 REFERENCE< XInterface >(), 230 InteractionClassification_ERROR, 231 aMedium.GetError() ); 232 233 REFERENCE< XInteractionRequest > xRequest( 234 new ucbhelper::SimpleInteractionRequest( makeAny( xException ), 235 ucbhelper::CONTINUATION_APPROVE ) ); 236 xInteraction->handle( xRequest ); 237 } 238 catch ( Exception & ) {}; 239 } 240 } 241 else 242 { 243 aFilterName.Erase(); 244 245 try 246 { 247 const SfxFilter* pFilter = aPreselectedFilterName.Len() ? 248 SfxFilterMatcher().GetFilter4FilterName( aPreselectedFilterName ) : aTypeName.Len() ? 249 SfxFilterMatcher(String::CreateFromAscii("smath")).GetFilter4EA( aTypeName ) : 0; 250 String aTmpFilterName; 251 if ( pFilter ) 252 aTmpFilterName = pFilter->GetName(); 253 aTypeName = SfxFilter::GetTypeFromStorage( xStorage, pFilter ? pFilter->IsAllowedAsTemplate() : sal_False, &aTmpFilterName ); 254 } 255 catch( lang::WrappedTargetException& aWrap ) 256 { 257 packages::zip::ZipIOException aZipException; 258 259 // repairing is done only if this type is requested from outside 260 if ( ( aWrap.TargetException >>= aZipException ) && aTypeName.Len() ) 261 { 262 if ( xInteraction.is() ) 263 { 264 // the package is broken one 265 aDocumentTitle = aMedium.GetURLObject().getName( 266 INetURLObject::LAST_SEGMENT, 267 true, 268 INetURLObject::DECODE_WITH_CHARSET ); 269 270 if ( !bRepairPackage ) 271 { 272 // ask the user whether he wants to try to repair 273 RequestPackageReparation aRequest( aDocumentTitle ); 274 xInteraction->handle( aRequest.GetRequest() ); 275 bRepairAllowed = aRequest.isApproved(); 276 } 277 278 if ( !bRepairAllowed ) 279 { 280 // repair either not allowed or not successful 281 NotifyBrokenPackage aNotifyRequest( aDocumentTitle ); 282 xInteraction->handle( aNotifyRequest.GetRequest() ); 283 } 284 } 285 286 if ( !bRepairAllowed ) 287 aTypeName.Erase(); 288 } 289 } 290 catch( uno::RuntimeException& ) 291 { 292 throw; 293 } 294 catch( uno::Exception& ) 295 { 296 aTypeName.Erase(); 297 } 298 299 if ( aTypeName.Len() ) 300 { 301 const SfxFilter* pFilter = 302 SfxFilterMatcher( String::CreateFromAscii("smath") ).GetFilter4EA( aTypeName ); 303 if ( pFilter ) 304 aFilterName = pFilter->GetName(); 305 } 306 } 307 } 308 else 309 { 310 //Test to see if this begins with xml and if so run it through 311 //the MathML filter. There are all sorts of things wrong with 312 //this approach, to be fixed at a better level than here 313 SvStream *pStrm = aMedium.GetInStream(); 314 aTypeName.Erase(); 315 if (pStrm && !pStrm->GetError()) 316 { 317 SotStorageRef aStorage = new SotStorage ( pStrm, sal_False ); 318 if ( !aStorage->GetError() ) 319 { 320 if ( aStorage->IsStream( String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "Equation Native" ) ) ) ) 321 { 322 sal_uInt8 nVersion; 323 if (GetMathTypeVersion( aStorage, nVersion ) && nVersion <=3) 324 aTypeName.AssignAscii( "math_MathType_3x" ); 325 } 326 } 327 else 328 { 329 // #124636# detection should not only check for xml, but at least also for 330 // the math start element and the MathML URL. Additionally take their order 331 // into account. Also allow the case where the start element has a namespace 332 // (e.g. <bla:math), but in that case ensure that it is in front of an evtl. 333 // xmlns:math namespace declaration and thus not part of that 334 const sal_uInt16 nReadSize(4095); 335 sal_Char aBuffer[nReadSize+1]; 336 pStrm->Seek( STREAM_SEEK_TO_BEGIN ); 337 const sal_uLong nBytesRead(pStrm->Read( aBuffer, nReadSize )); 338 339 if(nBytesRead > (5 + 1 + 34 + 5)) // xml + '>' + URL + '(<|:)math' 340 { 341 // end string with null 342 aBuffer[nBytesRead + 1] = 0; 343 344 // is it a xml file? 345 const sal_Char* pXML = strstr(aBuffer, "<?xml"); 346 bool isMathFile(false); 347 348 if(pXML) 349 { 350 // does it have the MathML URL? 351 const sal_Char* pURL = strstr(aBuffer, "http://www.w3.org/1998/Math/MathML"); 352 353 // URL has to be after XML start 354 if(pURL && pURL > pXML) 355 { 356 // look if we have a direct math start element 357 sal_Char* pMathStart = strstr(aBuffer, "<math"); 358 359 if(!pMathStart) 360 { 361 // if not, look if we have a math start element in another namespace 362 pMathStart = strstr(aBuffer, ":math"); 363 364 if(pMathStart) 365 { 366 // if found, this has to be in front of the evtl. also existing namespace 367 // declaration also containing :math to be the start element 368 sal_Char* pNamespaceMath = strstr(aBuffer, "xmlns:math"); 369 370 if(pNamespaceMath && pMathStart > pNamespaceMath) 371 { 372 // invalid :math found (probably part of the namespace declaration) 373 // -> this cannot be the math start element 374 pMathStart = 0; 375 } 376 } 377 } 378 379 // MathStart has to be before the URL 380 if(pMathStart && pMathStart < pURL) 381 { 382 isMathFile = true; 383 } 384 } 385 } 386 387 if(isMathFile) 388 { 389 static const sal_Char sFltrNm_2[] = MATHML_XML; 390 static const sal_Char sTypeNm_2[] = "math_MathML_XML_Math"; 391 392 aFilterName.AssignAscii( sFltrNm_2 ); 393 aTypeName.AssignAscii( sTypeNm_2 ); 394 } 395 } 396 } 397 398 if ( aTypeName.Len() ) 399 { 400 const SfxFilter* pFilt = SfxFilterMatcher( String::CreateFromAscii("smath") ).GetFilter4EA( aTypeName ); 401 if ( pFilt ) 402 aFilterName = pFilt->GetName(); 403 } 404 } 405 } 406 } 407 } 408 409 if ( nIndexOfInputStream == -1 && xStream.is() ) 410 { 411 // if input stream wasn't part of the descriptor, now it should be, otherwise the content would be opend twice 412 lDescriptor.realloc( nPropertyCount + 1 ); 413 lDescriptor[nPropertyCount].Name = ::rtl::OUString::createFromAscii("InputStream"); 414 lDescriptor[nPropertyCount].Value <<= xStream; 415 nPropertyCount++; 416 } 417 418 if ( nIndexOfContent == -1 && xContent.is() ) 419 { 420 // if input stream wasn't part of the descriptor, now it should be, otherwise the content would be opend twice 421 lDescriptor.realloc( nPropertyCount + 1 ); 422 lDescriptor[nPropertyCount].Name = ::rtl::OUString::createFromAscii("UCBContent"); 423 lDescriptor[nPropertyCount].Value <<= xContent; 424 nPropertyCount++; 425 } 426 427 if ( bReadOnly != bWasReadOnly ) 428 { 429 if ( nIndexOfReadOnlyFlag == -1 ) 430 { 431 lDescriptor.realloc( nPropertyCount + 1 ); 432 lDescriptor[nPropertyCount].Name = ::rtl::OUString::createFromAscii("ReadOnly"); 433 lDescriptor[nPropertyCount].Value <<= bReadOnly; 434 nPropertyCount++; 435 } 436 else 437 lDescriptor[nIndexOfReadOnlyFlag].Value <<= bReadOnly; 438 } 439 440 if ( !bRepairPackage && bRepairAllowed ) 441 { 442 lDescriptor.realloc( nPropertyCount + 1 ); 443 lDescriptor[nPropertyCount].Name = ::rtl::OUString::createFromAscii("RepairPackage"); 444 lDescriptor[nPropertyCount].Value <<= bRepairAllowed; 445 nPropertyCount++; 446 447 bOpenAsTemplate = sal_True; 448 449 // TODO/LATER: set progress bar that should be used 450 } 451 452 if ( bOpenAsTemplate ) 453 { 454 if ( nIndexOfTemplateFlag == -1 ) 455 { 456 lDescriptor.realloc( nPropertyCount + 1 ); 457 lDescriptor[nPropertyCount].Name = ::rtl::OUString::createFromAscii("AsTemplate"); 458 lDescriptor[nPropertyCount].Value <<= bOpenAsTemplate; 459 nPropertyCount++; 460 } 461 else 462 lDescriptor[nIndexOfTemplateFlag].Value <<= bOpenAsTemplate; 463 } 464 465 if ( aDocumentTitle.getLength() ) 466 { 467 // the title was set here 468 if ( nIndexOfDocumentTitle == -1 ) 469 { 470 lDescriptor.realloc( nPropertyCount + 1 ); 471 lDescriptor[nPropertyCount].Name = ::rtl::OUString::createFromAscii("DocumentTitle"); 472 lDescriptor[nPropertyCount].Value <<= aDocumentTitle; 473 nPropertyCount++; 474 } 475 else 476 lDescriptor[nIndexOfDocumentTitle].Value <<= aDocumentTitle; 477 } 478 479 if ( !aFilterName.Len() ) 480 aTypeName.Erase(); 481 482 return aTypeName; 483 } 484 485 SFX_IMPL_SINGLEFACTORY( SmFilterDetect ) 486 487 /* XServiceInfo */ 488 UNOOUSTRING SAL_CALL SmFilterDetect::getImplementationName() throw( UNORUNTIMEEXCEPTION ) 489 { 490 return impl_getStaticImplementationName(); 491 } 492 \ 493 /* XServiceInfo */ 494 sal_Bool SAL_CALL SmFilterDetect::supportsService( const UNOOUSTRING& sServiceName ) throw( UNORUNTIMEEXCEPTION ) 495 { 496 UNOSEQUENCE< UNOOUSTRING > seqServiceNames = getSupportedServiceNames(); 497 const UNOOUSTRING* pArray = seqServiceNames.getConstArray(); 498 for ( sal_Int32 nCounter=0; nCounter<seqServiceNames.getLength(); nCounter++ ) 499 { 500 if ( pArray[nCounter] == sServiceName ) 501 { 502 return sal_True ; 503 } 504 } 505 return sal_False ; 506 } 507 508 /* XServiceInfo */ 509 UNOSEQUENCE< UNOOUSTRING > SAL_CALL SmFilterDetect::getSupportedServiceNames() throw( UNORUNTIMEEXCEPTION ) 510 { 511 return impl_getStaticSupportedServiceNames(); 512 } 513 514 /* Helper for XServiceInfo */ 515 UNOSEQUENCE< UNOOUSTRING > SmFilterDetect::impl_getStaticSupportedServiceNames() 516 { 517 UNOMUTEXGUARD aGuard( UNOMUTEX::getGlobalMutex() ); 518 UNOSEQUENCE< UNOOUSTRING > seqServiceNames( 1 ); 519 seqServiceNames.getArray() [0] = UNOOUSTRING::createFromAscii( "com.sun.star.frame.ExtendedTypeDetection" ); 520 return seqServiceNames ; 521 } 522 523 /* Helper for XServiceInfo */ 524 UNOOUSTRING SmFilterDetect::impl_getStaticImplementationName() 525 { 526 return UNOOUSTRING::createFromAscii( "com.sun.star.comp.math.FormatDetector" ); 527 } 528 529 /* Helper for registry */ 530 UNOREFERENCE< UNOXINTERFACE > SAL_CALL SmFilterDetect::impl_createInstance( const UNOREFERENCE< UNOXMULTISERVICEFACTORY >& xServiceManager ) throw( UNOEXCEPTION ) 531 { 532 return UNOREFERENCE< UNOXINTERFACE >( *new SmFilterDetect( xServiceManager ) ); 533 } 534 535