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_sw.hxx" 26 #include "swdetect.hxx" 27 28 #include <framework/interaction.hxx> 29 #include <com/sun/star/frame/XFrame.hpp> 30 #include <com/sun/star/frame/XModel.hpp> 31 #include <com/sun/star/lang/XUnoTunnel.hpp> 32 #ifndef _UNOTOOLS_PROCESSFACTORY_HXX 33 #include <comphelper/processfactory.hxx> 34 #endif 35 #include <com/sun/star/container/XNameAccess.hpp> 36 #include <com/sun/star/io/XInputStream.hpp> 37 #include <com/sun/star/task/XInteractionHandler.hpp> 38 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> 39 #include <com/sun/star/ucb/CommandAbortedException.hpp> 40 #include <com/sun/star/ucb/InteractiveAppException.hpp> 41 #include <com/sun/star/ucb/XContent.hpp> 42 #include <com/sun/star/packages/zip/ZipIOException.hpp> 43 #ifndef _TOOLKIT_UNOHLP_HXX 44 #include <toolkit/helper/vclunohelper.hxx> 45 #endif 46 #include <ucbhelper/simpleinteractionrequest.hxx> 47 #include <rtl/ustring.h> 48 #include <rtl/logfile.hxx> 49 #include <svl/itemset.hxx> 50 #include <vcl/window.hxx> 51 #include <svl/eitem.hxx> 52 #include <svl/stritem.hxx> 53 #include <tools/urlobj.hxx> 54 #include <vos/mutex.hxx> 55 #include <svtools/sfxecode.hxx> 56 #include <svtools/ehdl.hxx> 57 #include <sot/storinfo.hxx> 58 #include <vcl/svapp.hxx> 59 #include <sfx2/app.hxx> 60 #include <sfx2/sfxsids.hrc> 61 #include <sfx2/request.hxx> 62 #include <sfx2/docfile.hxx> 63 #include <sfx2/docfilt.hxx> 64 #include <sfx2/fcontnr.hxx> 65 #include <sfx2/brokenpackageint.hxx> 66 #include <svtools/FilterConfigItem.hxx> 67 #include <unotools/moduleoptions.hxx> 68 #include <com/sun/star/util/XArchiver.hpp> 69 #include <comphelper/ihwrapnofilter.hxx> 70 71 #include <swdll.hxx> 72 73 using namespace ::com::sun::star; 74 using namespace ::com::sun::star::uno; 75 using namespace ::com::sun::star::io; 76 using namespace ::com::sun::star::frame; 77 using namespace ::com::sun::star::task; 78 using namespace ::com::sun::star::beans; 79 using namespace ::com::sun::star::lang; 80 using namespace ::com::sun::star::ucb; 81 using ::rtl::OUString; 82 83 SwFilterDetect::SwFilterDetect( const REFERENCE < lang::XMultiServiceFactory >& /*xFactory*/ ) 84 { 85 } 86 87 SwFilterDetect::~SwFilterDetect() 88 { 89 } 90 91 ::rtl::OUString SAL_CALL SwFilterDetect::detect( uno::Sequence< beans::PropertyValue >& lDescriptor ) throw( uno::RuntimeException ) 92 { 93 REFERENCE< XInputStream > xStream; 94 REFERENCE< XContent > xContent; 95 REFERENCE< XInteractionHandler > xInteraction; 96 String aURL; 97 ::rtl::OUString sTemp; 98 String aTypeName; // a name describing the type (from MediaDescriptor, usually from flat detection) 99 String aPreselectedFilterName; // a name describing the filter to use (from MediaDescriptor, usually from UI action) 100 101 ::rtl::OUString aDocumentTitle; // interesting only if set in this method 102 103 // opening as template is done when a parameter tells to do so and a template filter can be detected 104 // (otherwise no valid filter would be found) or if the detected filter is a template filter and 105 // there is no parameter that forbids to open as template 106 sal_Bool bOpenAsTemplate = sal_False; 107 sal_Bool bWasReadOnly = sal_False, bReadOnly = sal_False; 108 109 sal_Bool bRepairPackage = sal_False; 110 sal_Bool bRepairAllowed = sal_False; 111 112 // now some parameters that can already be in the array, but may be overwritten or new inserted here 113 // remember their indices in the case new values must be added to the array 114 sal_Int32 nPropertyCount = lDescriptor.getLength(); 115 sal_Int32 nIndexOfFilterName = -1; 116 sal_Int32 nIndexOfInputStream = -1; 117 sal_Int32 nIndexOfContent = -1; 118 sal_Int32 nIndexOfReadOnlyFlag = -1; 119 sal_Int32 nIndexOfTemplateFlag = -1; 120 sal_Int32 nIndexOfDocumentTitle = -1; 121 sal_Int32 nIndexOfInteractionHandler = -1; 122 123 for( sal_Int32 nProperty=0; nProperty<nPropertyCount; ++nProperty ) 124 { 125 // extract properties 126 if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("URL")) ) 127 { 128 lDescriptor[nProperty].Value >>= sTemp; 129 aURL = sTemp; 130 } 131 else if( !aURL.Len() && lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("FileName")) ) 132 { 133 lDescriptor[nProperty].Value >>= sTemp; 134 aURL = sTemp; 135 } 136 else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("TypeName")) ) 137 { 138 lDescriptor[nProperty].Value >>= sTemp; 139 aTypeName = sTemp; 140 } 141 else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("FilterName")) ) 142 { 143 lDescriptor[nProperty].Value >>= sTemp; 144 aPreselectedFilterName = sTemp; 145 146 // if the preselected filter name is not correct, it must be erased after detection 147 // remember index of property to get access to it later 148 nIndexOfFilterName = nProperty; 149 } 150 else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("InputStream")) ) 151 nIndexOfInputStream = nProperty; 152 else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("ReadOnly")) ) 153 nIndexOfReadOnlyFlag = nProperty; 154 else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("UCBContent")) ) 155 nIndexOfContent = nProperty; 156 else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("AsTemplate")) ) 157 { 158 lDescriptor[nProperty].Value >>= bOpenAsTemplate; 159 nIndexOfTemplateFlag = nProperty; 160 } 161 else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("InteractionHandler")) ) 162 { 163 lDescriptor[nProperty].Value >>= xInteraction; 164 nIndexOfInteractionHandler = nProperty; 165 } 166 else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("RepairPackage")) ) 167 lDescriptor[nProperty].Value >>= bRepairPackage; 168 else if( lDescriptor[nProperty].Name == OUString(RTL_CONSTASCII_USTRINGPARAM("DocumentTitle")) ) 169 nIndexOfDocumentTitle = nProperty; 170 } 171 172 // can't check the type for external filters, so set the "dont" flag accordingly 173 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 174 //SfxFilterFlags nMust = SFX_FILTER_IMPORT, nDont = SFX_FILTER_NOTINSTALLED; 175 176 SfxApplication* pApp = SFX_APP(); 177 SfxAllItemSet *pSet = new SfxAllItemSet( pApp->GetPool() ); 178 TransformParameters( SID_OPENDOC, lDescriptor, *pSet ); 179 SFX_ITEMSET_ARG( pSet, pItem, SfxBoolItem, SID_DOC_READONLY, sal_False ); 180 181 bWasReadOnly = pItem && pItem->GetValue(); 182 183 const SfxFilter* pFilter = 0; 184 String aPrefix = String::CreateFromAscii( "private:factory/" ); 185 if( aURL.Match( aPrefix ) == aPrefix.Len() ) 186 { 187 if( SvtModuleOptions().IsWriter() ) 188 { 189 String aPattern( aPrefix ); 190 aPattern += String::CreateFromAscii("swriter"); 191 if ( aURL.Match( aPattern ) >= aPattern.Len() ) 192 //pFilter = SfxFilter::GetDefaultFilterFromFactory( aURL ); 193 return aTypeName; 194 } 195 } 196 else 197 { 198 // ctor of SfxMedium uses owner transition of ItemSet 199 SfxMedium aMedium( aURL, bWasReadOnly ? STREAM_STD_READ : STREAM_STD_READWRITE, sal_False, NULL, pSet ); 200 aMedium.UseInteractionHandler( sal_True ); 201 if ( aMedium.GetErrorCode() == ERRCODE_NONE ) 202 { 203 // remember input stream and content and put them into the descriptor later 204 // should be done here since later the medium can switch to a version 205 xStream = aMedium.GetInputStream(); 206 xContent = aMedium.GetContent(); 207 bReadOnly = aMedium.IsReadOnly(); 208 209 sal_Bool bIsStorage = aMedium.IsStorage(); 210 if ( bIsStorage ) 211 { 212 uno::Reference< embed::XStorage > xStorage = aMedium.GetStorage( sal_False ); 213 if ( aMedium.GetLastStorageCreationState() != ERRCODE_NONE ) 214 { 215 // error during storage creation means _here_ that the medium 216 // is broken, but we can not handle it in medium since impossibility 217 // to create a storage does not _always_ means that the medium is broken 218 aMedium.SetError( aMedium.GetLastStorageCreationState(), ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ) ); 219 if ( xInteraction.is() ) 220 { 221 OUString empty; 222 try 223 { 224 InteractiveAppException xException( empty, 225 REFERENCE< XInterface >(), 226 InteractionClassification_ERROR, 227 aMedium.GetError() ); 228 229 REFERENCE< XInteractionRequest > xRequest( 230 new ucbhelper::SimpleInteractionRequest( makeAny( xException ), 231 ucbhelper::CONTINUATION_APPROVE ) ); 232 xInteraction->handle( xRequest ); 233 } 234 catch ( Exception & ) {}; 235 } 236 } 237 else 238 { 239 DBG_ASSERT( xStorage.is(), "At this point storage must exist!" ); 240 241 try 242 { 243 const SfxFilter* pPreFilter = aPreselectedFilterName.Len() ? 244 SfxFilterMatcher().GetFilter4FilterName( aPreselectedFilterName ) : aTypeName.Len() ? 245 SfxFilterMatcher(String::CreateFromAscii("swriter")).GetFilter4EA( aTypeName ) : 0; 246 if (!pPreFilter) 247 pPreFilter = SfxFilterMatcher(String::CreateFromAscii("sweb")).GetFilter4EA( aTypeName ); 248 String aFilterName; 249 if ( pPreFilter ) 250 { 251 aFilterName = pPreFilter->GetName(); 252 aTypeName = pPreFilter->GetTypeName(); 253 } 254 255 aTypeName = SfxFilter::GetTypeFromStorage( xStorage, pPreFilter ? pPreFilter->IsOwnTemplateFormat() : sal_False, &aFilterName ); 256 } 257 catch( lang::WrappedTargetException& aWrap ) 258 { 259 packages::zip::ZipIOException aZipException; 260 261 // repairing is done only if this type is requested from outside 262 // we don't do any type detection on broken packages (f.e. because it might be impossible), so any requested 263 // type will be accepted if the user allows to repair the file 264 if ( ( aWrap.TargetException >>= aZipException ) && ( aTypeName.Len() || aPreselectedFilterName.Len() ) ) 265 { 266 if ( xInteraction.is() ) 267 { 268 // the package is a broken one 269 aDocumentTitle = aMedium.GetURLObject().getName( 270 INetURLObject::LAST_SEGMENT, 271 true, 272 INetURLObject::DECODE_WITH_CHARSET ); 273 274 if ( !bRepairPackage ) 275 { 276 // ask the user whether he wants to try to repair 277 RequestPackageReparation aRequest( aDocumentTitle ); 278 xInteraction->handle( aRequest.GetRequest() ); 279 bRepairAllowed = aRequest.isApproved(); 280 } 281 282 if ( !bRepairAllowed ) 283 { 284 // repair either not allowed or not successful 285 // repair either not allowed or not successful 286 NotifyBrokenPackage aNotifyRequest( aDocumentTitle ); 287 xInteraction->handle( aNotifyRequest.GetRequest() ); 288 289 rtl::Reference< ::comphelper::OIHWrapNoFilterDialog > xHandler = new ::comphelper::OIHWrapNoFilterDialog( xInteraction ); 290 if ( nIndexOfInteractionHandler != -1 ) 291 lDescriptor[nIndexOfInteractionHandler].Value <<= uno::Reference< XInteractionHandler >( static_cast< task::XInteractionHandler* >( xHandler.get() ) ); 292 293 aMedium.SetError( ERRCODE_ABORT, ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ) ); 294 } 295 } 296 else 297 // no interaction, error handling as usual 298 aMedium.SetError( ERRCODE_IO_BROKENPACKAGE, ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ) ); 299 300 if ( !bRepairAllowed ) 301 { 302 aTypeName.Erase(); 303 aPreselectedFilterName.Erase(); 304 } 305 } 306 } 307 catch( uno::RuntimeException& ) 308 { 309 throw; 310 } 311 catch( uno::Exception& ) 312 { 313 aTypeName.Erase(); 314 aPreselectedFilterName.Erase(); 315 } 316 } 317 } 318 else 319 { 320 aMedium.GetInStream(); 321 if ( aMedium.GetErrorCode() == ERRCODE_NONE ) 322 { 323 if ( aPreselectedFilterName.Len() ) 324 pFilter = SfxFilter::GetFilterByName( aPreselectedFilterName ); 325 else 326 pFilter = SfxFilterMatcher().GetFilter4EA( aTypeName ); 327 328 sal_Bool bTestWriter = !pFilter || pFilter->GetServiceName().EqualsAscii("com.sun.star.text.TextDocument") || 329 pFilter->GetServiceName().EqualsAscii("com.sun.star.text.WebDocument"); 330 sal_Bool bTestGlobal = !pFilter || pFilter->GetServiceName().EqualsAscii("com.sun.star.text.GlobalDocument"); 331 332 const SfxFilter* pOrigFilter = NULL; 333 if ( !bTestWriter && !bTestGlobal && pFilter ) 334 { 335 // cross filter; now this should be a type detection only, not a filter detection 336 // we can simulate it by preserving the preselected filter if the type matches 337 // example: HTML filter for Calc 338 pOrigFilter = pFilter; 339 pFilter = SfxFilterMatcher().GetFilter4EA( pFilter->GetTypeName() ); 340 bTestWriter = sal_True; 341 } 342 343 sal_uLong nErr = ERRCODE_NONE; 344 if ( pFilter || bTestWriter ) 345 nErr = DetectFilter( aMedium, &pFilter ); 346 if ( nErr != ERRCODE_NONE ) 347 pFilter = NULL; 348 else if ( pOrigFilter && pFilter && pFilter->GetTypeName() == pOrigFilter->GetTypeName() ) 349 // cross filter, see above 350 pFilter = pOrigFilter; 351 } 352 353 if ( pFilter ) 354 aTypeName = pFilter->GetTypeName(); 355 else 356 aTypeName.Erase(); 357 } 358 } 359 } 360 361 if ( nIndexOfInputStream == -1 && xStream.is() ) 362 { 363 // if input stream wasn't part of the descriptor, now it should be, otherwise the content would be opend twice 364 lDescriptor.realloc( nPropertyCount + 1 ); 365 lDescriptor[nPropertyCount].Name = ::rtl::OUString::createFromAscii("InputStream"); 366 lDescriptor[nPropertyCount].Value <<= xStream; 367 nPropertyCount++; 368 } 369 370 if ( nIndexOfContent == -1 && xContent.is() ) 371 { 372 // if input stream wasn't part of the descriptor, now it should be, otherwise the content would be opend twice 373 lDescriptor.realloc( nPropertyCount + 1 ); 374 lDescriptor[nPropertyCount].Name = ::rtl::OUString::createFromAscii("UCBContent"); 375 lDescriptor[nPropertyCount].Value <<= xContent; 376 nPropertyCount++; 377 } 378 379 if ( bReadOnly != bWasReadOnly ) 380 { 381 if ( nIndexOfReadOnlyFlag == -1 ) 382 { 383 lDescriptor.realloc( nPropertyCount + 1 ); 384 lDescriptor[nPropertyCount].Name = ::rtl::OUString::createFromAscii("ReadOnly"); 385 lDescriptor[nPropertyCount].Value <<= bReadOnly; 386 nPropertyCount++; 387 } 388 else 389 lDescriptor[nIndexOfReadOnlyFlag].Value <<= bReadOnly; 390 } 391 392 if ( !bRepairPackage && bRepairAllowed ) 393 { 394 lDescriptor.realloc( nPropertyCount + 1 ); 395 lDescriptor[nPropertyCount].Name = ::rtl::OUString::createFromAscii("RepairPackage"); 396 lDescriptor[nPropertyCount].Value <<= bRepairAllowed; 397 nPropertyCount++; 398 bOpenAsTemplate = sal_True; 399 // TODO/LATER: set progress bar that should be used 400 } 401 402 if ( bOpenAsTemplate ) 403 { 404 if ( nIndexOfTemplateFlag == -1 ) 405 { 406 lDescriptor.realloc( nPropertyCount + 1 ); 407 lDescriptor[nPropertyCount].Name = ::rtl::OUString::createFromAscii("AsTemplate"); 408 lDescriptor[nPropertyCount].Value <<= bOpenAsTemplate; 409 nPropertyCount++; 410 } 411 else 412 lDescriptor[nIndexOfTemplateFlag].Value <<= bOpenAsTemplate; 413 } 414 415 if ( aDocumentTitle.getLength() ) 416 { 417 // the title was set here 418 if ( nIndexOfDocumentTitle == -1 ) 419 { 420 lDescriptor.realloc( nPropertyCount + 1 ); 421 lDescriptor[nPropertyCount].Name = ::rtl::OUString::createFromAscii("DocumentTitle"); 422 lDescriptor[nPropertyCount].Value <<= aDocumentTitle; 423 nPropertyCount++; 424 } 425 else 426 lDescriptor[nIndexOfDocumentTitle].Value <<= aDocumentTitle; 427 } 428 429 430 return aTypeName; 431 } 432 433 SFX_IMPL_SINGLEFACTORY( SwFilterDetect ) 434 435 /* XServiceInfo */ 436 UNOOUSTRING SAL_CALL SwFilterDetect::getImplementationName() throw( UNORUNTIMEEXCEPTION ) 437 { 438 return impl_getStaticImplementationName(); 439 } 440 \ 441 /* XServiceInfo */ 442 sal_Bool SAL_CALL SwFilterDetect::supportsService( const UNOOUSTRING& sServiceName ) throw( UNORUNTIMEEXCEPTION ) 443 { 444 UNOSEQUENCE< UNOOUSTRING > seqServiceNames = getSupportedServiceNames(); 445 const UNOOUSTRING* pArray = seqServiceNames.getConstArray(); 446 for ( sal_Int32 nCounter=0; nCounter<seqServiceNames.getLength(); nCounter++ ) 447 { 448 if ( pArray[nCounter] == sServiceName ) 449 { 450 return sal_True ; 451 } 452 } 453 return sal_False ; 454 } 455 456 /* XServiceInfo */ 457 UNOSEQUENCE< UNOOUSTRING > SAL_CALL SwFilterDetect::getSupportedServiceNames() throw( UNORUNTIMEEXCEPTION ) 458 { 459 return impl_getStaticSupportedServiceNames(); 460 } 461 462 /* Helper for XServiceInfo */ 463 UNOSEQUENCE< UNOOUSTRING > SwFilterDetect::impl_getStaticSupportedServiceNames() 464 { 465 UNOMUTEXGUARD aGuard( UNOMUTEX::getGlobalMutex() ); 466 UNOSEQUENCE< UNOOUSTRING > seqServiceNames( 3 ); 467 seqServiceNames.getArray() [0] = UNOOUSTRING::createFromAscii( "com.sun.star.frame.ExtendedTypeDetection" ); 468 seqServiceNames.getArray() [1] = UNOOUSTRING::createFromAscii( "com.sun.star.text.FormatDetector" ); 469 seqServiceNames.getArray() [2] = UNOOUSTRING::createFromAscii( "com.sun.star.text.W4WFormatDetector" ); 470 return seqServiceNames ; 471 } 472 473 /* Helper for XServiceInfo */ 474 UNOOUSTRING SwFilterDetect::impl_getStaticImplementationName() 475 { 476 return UNOOUSTRING::createFromAscii( "com.sun.star.comp.writer.FormatDetector" ); 477 } 478 479 /* Helper for registry */ 480 UNOREFERENCE< UNOXINTERFACE > SAL_CALL SwFilterDetect::impl_createInstance( const UNOREFERENCE< UNOXMULTISERVICEFACTORY >& xServiceManager ) throw( UNOEXCEPTION ) 481 { 482 return UNOREFERENCE< UNOXINTERFACE >( *new SwFilterDetect( xServiceManager ) ); 483 } 484 485