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