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 "typedetection.hxx"
21 #include "constant.hxx"
22
23 #include <com/sun/star/document/XExtendedFilterDetection.hpp>
24 #include <com/sun/star/frame/Desktop.hpp>
25 #include <com/sun/star/util/URLTransformer.hpp>
26 #include <com/sun/star/util/XURLTransformer.hpp>
27
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <com/sun/star/embed/StorageFormats.hpp>
30 #include <com/sun/star/io/XInputStream.hpp>
31 #include <com/sun/star/io/XSeekable.hpp>
32 #include <com/sun/star/packages/zip/ZipIOException.hpp>
33 #include <com/sun/star/task/XInteractionHandler.hpp>
34
35 #include <sfx2/brokenpackageint.hxx>
36 #include <o3tl/string_view.hxx>
37 #include <tools/wldcrd.hxx>
38 #include <sal/log.hxx>
39 #include <framework/interaction.hxx>
40 #include <comphelper/diagnose_ex.hxx>
41 #include <tools/urlobj.hxx>
42 #include <comphelper/fileurl.hxx>
43 #include <comphelper/lok.hxx>
44 #include <comphelper/sequence.hxx>
45 #include <comphelper/scopeguard.hxx>
46 #include <utility>
47
48 #define DEBUG_TYPE_DETECTION 0
49
50 #if DEBUG_TYPE_DETECTION
51 #include <iostream>
52 using std::cout;
53 using std::endl;
54 #endif
55
56 using namespace com::sun::star;
57
58 namespace filter::config{
59
TypeDetection(const css::uno::Reference<css::uno::XComponentContext> & rxContext)60 TypeDetection::TypeDetection(const css::uno::Reference< css::uno::XComponentContext >& rxContext)
61 : m_xContext(rxContext)
62 , m_xTerminateListener(new TerminateDetection(this))
63 , m_bCancel(false)
64 {
65 css::frame::Desktop::create(m_xContext)->addTerminateListener(m_xTerminateListener);
66 BaseContainer::init(u"com.sun.star.comp.filter.config.TypeDetection"_ustr ,
67 { u"com.sun.star.document.TypeDetection"_ustr },
68 FilterCache::E_TYPE );
69 }
70
71
~TypeDetection()72 TypeDetection::~TypeDetection()
73 {
74 css::frame::Desktop::create(m_xContext)->removeTerminateListener(m_xTerminateListener);
75 }
76
77
queryTypeByURL(const OUString & sURL)78 OUString SAL_CALL TypeDetection::queryTypeByURL(const OUString& sURL)
79 {
80 OUString sType;
81
82 // SAFE ->
83 std::unique_lock aLock(m_aMutex);
84
85 css::util::URL aURL;
86 aURL.Complete = sURL;
87 css::uno::Reference< css::util::XURLTransformer > xParser( css::util::URLTransformer::create(m_xContext) );
88 xParser->parseStrict(aURL);
89
90 // set std types as minimum requirement first!
91 // Only in case no type was found for given URL,
92 // use optional types too ...
93 auto & cache = GetTheFilterCache();
94 FlatDetection lFlatTypes;
95 cache.detectFlatForURL(aURL, lFlatTypes);
96
97 if (
98 (lFlatTypes.empty() ) &&
99 (!cache.isFillState(FilterCache::E_CONTAINS_TYPES))
100 )
101 {
102 cache.load(FilterCache::E_CONTAINS_TYPES);
103 cache.detectFlatForURL(aURL, lFlatTypes);
104 }
105
106 // first item is guaranteed as "preferred" one!
107 if (!lFlatTypes.empty())
108 {
109 const FlatDetectionInfo& aMatch = *(lFlatTypes.begin());
110 sType = aMatch.sType;
111 }
112
113 return sType;
114 // <- SAFE
115 }
116
117 namespace {
118
119 /**
120 * Rank format types in order of complexity. More complex formats are
121 * ranked higher so that they get tested sooner over simpler formats.
122 *
123 * Guidelines to determine how complex a format is (subject to change):
124 *
125 * 1) compressed text (XML, HTML, etc)
126 * 2) binary
127 * 3) non-compressed text
128 * 3.1) structured text
129 * 3.1.1) dialect of a structured text (e.g. docbook XML)
130 * 3.1.2) generic structured text (e.g. generic XML)
131 * 3.2) non-structured text
132 *
133 * In each category, rank them from strictly-structured to
134 * loosely-structured.
135 */
getFlatTypeRank(std::u16string_view rType)136 int getFlatTypeRank(std::u16string_view rType)
137 {
138 // List formats from more complex to less complex.
139 // TODO: Add more.
140 static const char* ranks[] = {
141
142 // Compressed XML (ODF XML zip formats)
143 "writer8_template",
144 "writer8",
145 "calc8_template",
146 "calc8",
147 "impress8_template",
148 "impress8",
149 "draw8_template",
150 "draw8",
151 "chart8",
152 "math8",
153 "writerglobal8_template",
154 "writerglobal8",
155 "writerweb8_writer_template",
156 "StarBase",
157
158 // Compressed XML (OOXML)
159 "writer_OOXML_Text_Template",
160 "writer_OOXML",
161 "writer_MS_Word_2007_Template",
162 "writer_MS_Word_2007",
163 "Office Open XML Spreadsheet Template",
164 "Office Open XML Spreadsheet",
165 "MS Excel 2007 XML Template",
166 "MS Excel 2007 XML",
167 "MS PowerPoint 2007 XML Template",
168 "MS PowerPoint 2007 XML AutoPlay",
169 "MS PowerPoint 2007 XML",
170
171 // Compressed XML (Uniform/Unified Office Format)
172 "Unified_Office_Format_text",
173 "Unified_Office_Format_spreadsheet",
174 "Unified_Office_Format_presentation",
175
176 // Compressed XML (StarOffice XML zip formats)
177 "calc_StarOffice_XML_Calc",
178 "calc_StarOffice_XML_Calc_Template",
179 "chart_StarOffice_XML_Chart",
180 "draw_StarOffice_XML_Draw",
181 "draw_StarOffice_XML_Draw_Template",
182 "impress_StarOffice_XML_Impress",
183 "impress_StarOffice_XML_Impress_Template",
184 "math_StarOffice_XML_Math",
185 "writer_StarOffice_XML_Writer",
186 "writer_StarOffice_XML_Writer_Template",
187 "writer_globaldocument_StarOffice_XML_Writer_GlobalDocument",
188 "writer_web_StarOffice_XML_Writer_Web_Template",
189
190 // Compressed text
191 "pdf_Portable_Document_Format",
192
193 // Binary
194 "writer_T602_Document",
195 "writer_WordPerfect_Document",
196 "writer_MS_Works_Document",
197 "writer_MS_Word_97_Vorlage",
198 "writer_MS_Word_97",
199 "writer_MS_Word_95_Vorlage",
200 "writer_MS_Word_95",
201 "writer_MS_WinWord_60",
202 "writer_MS_WinWord_5",
203 "MS Excel 2007 Binary",
204 "calc_MS_Excel_97_VorlageTemplate",
205 "calc_MS_Excel_97",
206 "calc_MS_Excel_95_VorlageTemplate",
207 "calc_MS_Excel_95",
208 "calc_MS_Excel_5095_VorlageTemplate",
209 "calc_MS_Excel_5095",
210 "calc_MS_Excel_40_VorlageTemplate",
211 "calc_MS_Excel_40",
212 "calc_Pocket_Excel_File",
213 "impress_MS_PowerPoint_97_Vorlage",
214 "impress_MS_PowerPoint_97_AutoPlay",
215 "impress_MS_PowerPoint_97",
216 "calc_Lotus",
217 "calc_QPro",
218 "calc_SYLK",
219 "calc_DIF",
220 "calc_dBase",
221 "Apache Parquet",
222
223 // Binary (raster and vector image files)
224 "emf_MS_Windows_Metafile",
225 "wmf_MS_Windows_Metafile",
226 "met_OS2_Metafile",
227 "svm_StarView_Metafile",
228 "sgv_StarDraw_20",
229 "tif_Tag_Image_File",
230 "tga_Truevision_TARGA",
231 "sgf_StarOffice_Writer_SGF",
232 "ras_Sun_Rasterfile",
233 "psd_Adobe_Photoshop",
234 "png_Portable_Network_Graphic",
235 "jpg_JPEG",
236 "mov_MOV",
237 "gif_Graphics_Interchange",
238 "bmp_MS_Windows",
239 "pcx_Zsoft_Paintbrush",
240 "pct_Mac_Pict",
241 "pcd_Photo_CD_Base",
242 "pcd_Photo_CD_Base4",
243 "pcd_Photo_CD_Base16",
244 "webp_WebP",
245 "impress_CGM_Computer_Graphics_Metafile", // There is binary and ascii variants ?
246 "draw_WordPerfect_Graphics",
247 "draw_Visio_Document",
248 "draw_Publisher_Document",
249 "draw_Corel_Presentation_Exchange",
250 "draw_CorelDraw_Document",
251 "writer_LotusWordPro_Document",
252 "writer_MIZI_Hwp_97", // Hanword (Hancom Office)
253
254 // Non-compressed XML
255 "writer_ODT_FlatXML",
256 "calc_ODS_FlatXML",
257 "impress_ODP_FlatXML",
258 "draw_ODG_FlatXML",
259 "calc_ADO_rowset_XML",
260 "calc_MS_Excel_2003_XML",
261 "writer_MS_Word_2003_XML",
262 "writer_DocBook_File",
263 "XHTML_File",
264 "svg_Scalable_Vector_Graphics",
265 "math_MathML_XML_Math",
266
267 // Non-compressed text
268 "dxf_AutoCAD_Interchange",
269 "eps_Encapsulated_PostScript",
270 "pbm_Portable_Bitmap", // There is 'raw' and 'ascii' variants.
271 "ppm_Portable_Pixelmap", // There is 'raw' and 'ascii' variants.
272 "pgm_Portable_Graymap", // There is 'raw' and 'ascii' variants.
273 "xpm_XPM",
274 "xbm_X_Consortium",
275 "writer_Rich_Text_Format",
276 "writer_web_HTML_help",
277 "generic_HTML",
278
279 "generic_Text", // Plain text (catch all)
280
281 // Anything ranked lower than generic_Text will never be used during
282 // type detection (since generic_Text catches all).
283
284 // Export only
285 "writer_layout_dump_xml",
286 "writer_indexing_export",
287 "graphic_HTML",
288
289 // Internal use only
290 "StarBaseReportChart",
291 "StarBaseReport",
292 "math_MathType_3x", // MathType equation embedded in Word doc.
293 };
294
295 size_t n = std::size(ranks);
296
297 for (size_t i = 0; i < n; ++i)
298 {
299 if (o3tl::equalsAscii(rType, ranks[i]))
300 return n - i - 1;
301 }
302
303 // Not ranked. Treat them equally. Unranked formats have higher priority
304 // than the ranked internal ones since they may be defined externally.
305 return n;
306 }
307
308 /**
309 * Types with matching pattern first, then extension, then custom ranks by
310 * types, then types that are supported by the document service come next.
311 * Lastly, sort them alphabetically.
312 */
313 struct SortByPriority
314 {
operator ()filter::config::__anon458ef9140111::SortByPriority315 bool operator() (const FlatDetectionInfo& r1, const FlatDetectionInfo& r2) const
316 {
317 if (r1.bMatchByPattern != r2.bMatchByPattern)
318 return r1.bMatchByPattern;
319
320 if (r1.bMatchByExtension != r2.bMatchByExtension)
321 return r1.bMatchByExtension;
322
323 int rank1 = getFlatTypeRank(r1.sType);
324 int rank2 = getFlatTypeRank(r2.sType);
325
326 if (rank1 != rank2)
327 return rank1 > rank2;
328
329 if (r1.bPreselectedByDocumentService != r2.bPreselectedByDocumentService)
330 return r1.bPreselectedByDocumentService;
331
332 // All things being equal, sort them alphabetically.
333 return r1.sType > r2.sType;
334 }
335 };
336
337 struct SortByType
338 {
operator ()filter::config::__anon458ef9140111::SortByType339 bool operator() (const FlatDetectionInfo& r1, const FlatDetectionInfo& r2) const
340 {
341 return r1.sType > r2.sType;
342 }
343 };
344
345 struct EqualByType
346 {
operator ()filter::config::__anon458ef9140111::EqualByType347 bool operator() (const FlatDetectionInfo& r1, const FlatDetectionInfo& r2) const
348 {
349 return r1.sType == r2.sType;
350 }
351 };
352
353 class FindByType
354 {
355 OUString maType;
356 public:
FindByType(OUString aType)357 explicit FindByType(OUString aType) : maType(std::move(aType)) {}
operator ()(const FlatDetectionInfo & rInfo) const358 bool operator() (const FlatDetectionInfo& rInfo) const
359 {
360 return rInfo.sType == maType;
361 }
362 };
363
364 #if DEBUG_TYPE_DETECTION
printFlatDetectionList(const char * caption,const FlatDetection & types)365 void printFlatDetectionList(const char* caption, const FlatDetection& types)
366 {
367 cout << "-- " << caption << " (size=" << types.size() << ")" << endl;
368 for (auto const& item : types)
369 {
370 cout << " type='" << item.sType << "'; match by extension (" << item.bMatchByExtension
371 << "); match by pattern (" << item.bMatchByPattern << "); pre-selected by doc service ("
372 << item.bPreselectedByDocumentService << ")" << endl;
373 }
374 cout << "--" << endl;
375 }
376 #endif
377
378 }
379
queryTypeByDescriptor(css::uno::Sequence<css::beans::PropertyValue> & lDescriptor,sal_Bool bAllowDeep)380 OUString SAL_CALL TypeDetection::queryTypeByDescriptor(css::uno::Sequence< css::beans::PropertyValue >& lDescriptor,
381 sal_Bool bAllowDeep )
382 {
383 // make the descriptor more usable :-)
384 utl::MediaDescriptor stlDescriptor(lDescriptor);
385 OUString sType, sURL;
386
387 try
388 {
389 // SAFE -> ----------------------------------
390 std::unique_lock aLock(m_aMutex);
391
392 // parse given URL to split it into e.g. main and jump marks ...
393 sURL = stlDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_URL, OUString());
394
395 #if OSL_DEBUG_LEVEL > 0
396 if (stlDescriptor.find( u"FileName"_ustr ) != stlDescriptor.end())
397 OSL_FAIL("Detect using of deprecated and already unsupported MediaDescriptor property \"FileName\"!");
398 #endif
399
400 css::util::URL aURL;
401 aURL.Complete = sURL;
402 css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext));
403 xParser->parseStrict(aURL);
404
405 OUString aSelectedFilter = stlDescriptor.getUnpackedValueOrDefault(
406 utl::MediaDescriptor::PROP_FILTERNAME, OUString());
407 if (!aSelectedFilter.isEmpty())
408 {
409 // Caller specified the filter type. Honor it. Just get the default
410 // type for that filter, and bail out.
411 if (impl_validateAndSetFilterOnDescriptor(stlDescriptor, aSelectedFilter))
412 return stlDescriptor[utl::MediaDescriptor::PROP_TYPENAME].get<OUString>();
413 }
414
415 FlatDetection lFlatTypes;
416 impl_getAllFormatTypes(aLock, aURL, stlDescriptor, lFlatTypes);
417
418 aLock.unlock();
419 // <- SAFE ----------------------------------
420
421 // Properly prioritize all candidate types.
422 std::stable_sort(lFlatTypes.begin(), lFlatTypes.end(), SortByPriority());
423 auto last = std::unique(lFlatTypes.begin(), lFlatTypes.end(), EqualByType());
424 lFlatTypes.erase(last, lFlatTypes.end());
425
426 OUString sLastChance;
427
428 // verify every flat detected (or preselected!) type
429 // by calling its registered deep detection service.
430 // But break this loop if a type match to the given descriptor
431 // by a URL pattern(!) or if deep detection isn't allowed from
432 // outside (bAllowDeep=sal_False) or break the whole detection by
433 // throwing an exception if creation of the might needed input
434 // stream failed by e.g. an IO exception ...
435 if (!lFlatTypes.empty())
436 sType = impl_detectTypeFlatAndDeep(stlDescriptor, lFlatTypes, bAllowDeep, sLastChance);
437
438 // flat detection failed
439 // pure deep detection failed
440 // => ask might existing InteractionHandler
441 // means: ask user for its decision
442 if (sType.isEmpty() && !m_bCancel)
443 sType = impl_askUserForTypeAndFilterIfAllowed(stlDescriptor);
444
445
446 // no real detected type - but a might valid one.
447 // update descriptor and set last chance for return.
448 if (sType.isEmpty() && !sLastChance.isEmpty() && !m_bCancel)
449 {
450 OSL_FAIL("set first flat detected type without a registered deep detection service as \"last chance\" ... nevertheless some other deep detections said \"NO\". I TRY IT!");
451 sType = sLastChance;
452 }
453 }
454 catch(const css::uno::RuntimeException&)
455 {
456 throw;
457 }
458 catch(const css::uno::Exception&)
459 {
460 TOOLS_WARN_EXCEPTION("filter.config", "caught exception while querying type of " << sURL);
461 sType.clear();
462 }
463
464 // adapt media descriptor, so it contains the right values
465 // for type/filter name/document service/ etcpp.
466 impl_checkResultsAndAddBestFilter(stlDescriptor, sType); // Attention: sType is used as IN/OUT param here and will might be changed inside this method !!!
467 impl_validateAndSetTypeOnDescriptor(stlDescriptor, sType);
468
469 stlDescriptor >> lDescriptor;
470 return sType;
471 }
472
473
impl_checkResultsAndAddBestFilter(utl::MediaDescriptor & rDescriptor,OUString & sType)474 void TypeDetection::impl_checkResultsAndAddBestFilter(utl::MediaDescriptor& rDescriptor,
475 OUString& sType )
476 {
477 // a)
478 // Don't overwrite a might preselected filter!
479 OUString sFilter = rDescriptor.getUnpackedValueOrDefault(
480 utl::MediaDescriptor::PROP_FILTERNAME,
481 OUString());
482 if (!sFilter.isEmpty())
483 return;
484
485 auto & cache = GetTheFilterCache();
486
487 // b)
488 // check a preselected document service too.
489 // Then we have to search a suitable filter within this module.
490 OUString sDocumentService = rDescriptor.getUnpackedValueOrDefault(
491 utl::MediaDescriptor::PROP_DOCUMENTSERVICE,
492 OUString());
493 if (!sDocumentService.isEmpty())
494 {
495 try
496 {
497 OUString sRealType = sType;
498
499 // SAFE ->
500 std::unique_lock aLock(m_aMutex);
501
502 // Attention: For executing next lines of code, We must be sure that
503 // all filters already loaded :-(
504 // That can disturb our "load on demand feature". But we have no other chance!
505 cache.load(FilterCache::E_CONTAINS_FILTERS);
506
507 css::beans::NamedValue lIProps[] {
508 { PROPNAME_DOCUMENTSERVICE, uno::Any(sDocumentService) },
509 { PROPNAME_TYPE, uno::Any(sRealType) } };
510 std::vector<OUString> lFilters = cache.getMatchingItemsByProps(FilterCache::E_FILTER, lIProps);
511
512 aLock.unlock();
513 // <- SAFE
514
515 for (auto const& filter : lFilters)
516 {
517 // SAFE ->
518 aLock.lock();
519 try
520 {
521 CacheItem aFilter = cache.getItem(FilterCache::E_FILTER, filter);
522 sal_Int32 nFlags = 0;
523 aFilter[PROPNAME_FLAGS] >>= nFlags;
524
525 if (static_cast<SfxFilterFlags>(nFlags) & SfxFilterFlags::IMPORT)
526 sFilter = filter;
527 if (static_cast<SfxFilterFlags>(nFlags) & SfxFilterFlags::PREFERED)
528 break;
529 }
530 catch(const css::uno::Exception&) {}
531 aLock.unlock();
532 // <- SAFE
533 }
534
535 if (!sFilter.isEmpty())
536 {
537 rDescriptor[utl::MediaDescriptor::PROP_TYPENAME ] <<= sRealType;
538 rDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] <<= sFilter;
539 sType = sRealType;
540 return;
541 }
542 }
543 catch(const css::uno::Exception&)
544 {}
545 }
546
547 // c)
548 // We can use the preferred filter for the specified type.
549 // Such preferred filter points:
550 // - to the default filter of the preferred application
551 // - or to any other filter if no preferred filter was set.
552 // Note: It's an optimization only!
553 // It's not guaranteed, that such preferred filter exists.
554 sFilter.clear();
555 try
556 {
557 CacheItem aType = cache.getItem(FilterCache::E_TYPE, sType);
558 aType[PROPNAME_PREFERREDFILTER] >>= sFilter;
559 cache.getItem(FilterCache::E_FILTER, sFilter);
560
561 // no exception => found valid type and filter => set it on the given descriptor
562 rDescriptor[utl::MediaDescriptor::PROP_TYPENAME ] <<= sType ;
563 rDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] <<= sFilter;
564 return;
565 }
566 catch(const css::uno::Exception&)
567 {}
568
569 // d)
570 // Search for any import(!) filter, which is registered for this type.
571 sFilter.clear();
572 try
573 {
574 // Attention: For executing next lines of code, We must be sure that
575 // all filters already loaded :-(
576 // That can disturb our "load on demand feature". But we have no other chance!
577 cache.load(FilterCache::E_CONTAINS_FILTERS);
578
579 css::beans::NamedValue lIProps[] {
580 { PROPNAME_TYPE, uno::Any(sType) } };
581 std::vector<OUString> lFilters = cache.getMatchingItemsByProps(FilterCache::E_FILTER, lIProps);
582
583 for (auto const& filter : lFilters)
584 {
585 sFilter = filter;
586
587 try
588 {
589 CacheItem aFilter = cache.getItem(FilterCache::E_FILTER, sFilter);
590 sal_Int32 nFlags = 0;
591 aFilter[PROPNAME_FLAGS] >>= nFlags;
592
593 if (static_cast<SfxFilterFlags>(nFlags) & SfxFilterFlags::IMPORT)
594 break;
595 }
596 catch(const css::uno::Exception&)
597 { continue; }
598
599 sFilter.clear();
600 }
601
602 if (!sFilter.isEmpty())
603 {
604 rDescriptor[utl::MediaDescriptor::PROP_TYPENAME ] <<= sType ;
605 rDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] <<= sFilter;
606 return;
607 }
608 }
609 catch(const css::uno::Exception&)
610 {}
611 }
612
613
impl_getPreselectionForType(std::unique_lock<std::mutex> &,const OUString & sPreSelType,const util::URL & aParsedURL,FlatDetection & rFlatTypes,bool bDocService)614 bool TypeDetection::impl_getPreselectionForType(
615 std::unique_lock<std::mutex>& /*rGuard*/,
616 const OUString& sPreSelType, const util::URL& aParsedURL, FlatDetection& rFlatTypes, bool bDocService)
617 {
618 // Can be used to suppress execution of some parts of this method
619 // if it's already clear that detected type is valid or not.
620 // It's necessary to use shared code at the end, which update
621 // all return parameters consistency!
622 bool bBreakDetection = false;
623
624 // Further we must know if it matches by pattern
625 // Every flat detected type by pattern won't be detected deep!
626 bool bMatchByPattern = false;
627
628 // And we must know if a preselection must be preferred, because
629 // it matches by its extension too.
630 bool bMatchByExtension = false;
631
632 // validate type
633 OUString sType(sPreSelType);
634 CacheItem aType;
635 try
636 {
637 aType = GetTheFilterCache().getItem(FilterCache::E_TYPE, sType);
638 }
639 catch(const css::container::NoSuchElementException&)
640 {
641 sType.clear();
642 bBreakDetection = true;
643 }
644
645 if (!bBreakDetection)
646 {
647 // We can't check a preselected type for a given stream!
648 // So we must believe, that it can work ...
649 if ( aParsedURL.Complete == "private:stream" )
650 bBreakDetection = true;
651 }
652
653 if (!bBreakDetection)
654 {
655 // extract extension from URL .. to check it case-insensitive !
656 INetURLObject aParser (aParsedURL.Main);
657 OUString sExtension = aParser.getExtension(INetURLObject::LAST_SEGMENT ,
658 true ,
659 INetURLObject::DecodeMechanism::WithCharset);
660 sExtension = sExtension.toAsciiLowerCase();
661
662 // otherwise we must know, if it matches to the given URL really.
663 // especially if it matches by its extension or pattern registration.
664 const css::uno::Sequence<OUString> lExtensions = aType[PROPNAME_EXTENSIONS].get<css::uno::Sequence<OUString> >();
665 const css::uno::Sequence<OUString> lURLPattern = aType[PROPNAME_URLPATTERN].get<css::uno::Sequence<OUString> >();
666
667 for (auto const& extension : lExtensions)
668 {
669 OUString sCheckExtension(extension.toAsciiLowerCase());
670 if (sCheckExtension == sExtension)
671 {
672 bBreakDetection = true;
673 bMatchByExtension = true;
674 break;
675 }
676 }
677
678 if (!bBreakDetection)
679 {
680 for (auto const& elem : lURLPattern)
681 {
682 WildCard aCheck(elem);
683 if (aCheck.Matches(aParsedURL.Main))
684 {
685 bMatchByPattern = true;
686 break;
687 }
688 }
689 }
690 }
691
692 // if it's a valid type - set it on all return values!
693 if (!sType.isEmpty())
694 {
695 FlatDetection::iterator it = std::find_if(rFlatTypes.begin(), rFlatTypes.end(), FindByType(sType));
696 if (it != rFlatTypes.end())
697 {
698 if (bMatchByExtension)
699 it->bMatchByExtension = true;
700 if (bMatchByPattern)
701 it->bMatchByPattern = true;
702 if (bDocService)
703 it->bPreselectedByDocumentService = true;
704 }
705
706 return true;
707 }
708
709 // not valid!
710 return false;
711 }
712
impl_getPreselectionForDocumentService(std::unique_lock<std::mutex> & rGuard,const OUString & sPreSelDocumentService,const util::URL & aParsedURL,FlatDetection & rFlatTypes)713 void TypeDetection::impl_getPreselectionForDocumentService(
714 std::unique_lock<std::mutex>& rGuard,
715 const OUString& sPreSelDocumentService, const util::URL& aParsedURL, FlatDetection& rFlatTypes)
716 {
717 // get all filters, which match to this doc service
718 std::vector<OUString> lFilters;
719 try
720 {
721 // Attention: For executing next lines of code, We must be sure that
722 // all filters already loaded :-(
723 // That can disturb our "load on demand feature". But we have no other chance!
724 auto & cache = GetTheFilterCache();
725 cache.load(FilterCache::E_CONTAINS_FILTERS);
726
727 css::beans::NamedValue lIProps[] {
728 { PROPNAME_DOCUMENTSERVICE, css::uno::Any(sPreSelDocumentService) } };
729 lFilters = cache.getMatchingItemsByProps(FilterCache::E_FILTER, lIProps);
730 }
731 catch (const css::container::NoSuchElementException&)
732 {
733 lFilters.clear();
734 }
735
736 // step over all filters, and check if its registered type
737 // match the given URL.
738 // But use temp. list of "preselected types" instead of incoming rFlatTypes list!
739 // The reason behind: we must filter the obtained results. And copying stl entries
740 // is an easier job than removing them .-)
741 for (auto const& filter : lFilters)
742 {
743 OUString aType = impl_getTypeFromFilter(rGuard, filter);
744 if (aType.isEmpty())
745 continue;
746
747 impl_getPreselectionForType(rGuard, aType, aParsedURL, rFlatTypes, true);
748 }
749 }
750
impl_getTypeFromFilter(std::unique_lock<std::mutex> &,const OUString & rFilterName)751 OUString TypeDetection::impl_getTypeFromFilter(std::unique_lock<std::mutex>& /*rGuard*/, const OUString& rFilterName)
752 {
753 CacheItem aFilter;
754 try
755 {
756 aFilter = GetTheFilterCache().getItem(FilterCache::E_FILTER, rFilterName);
757 }
758 catch (const container::NoSuchElementException&)
759 {
760 return OUString();
761 }
762
763 OUString aType;
764 aFilter[PROPNAME_TYPE] >>= aType;
765 return aType;
766 }
767
impl_getAllFormatTypes(std::unique_lock<std::mutex> & rGuard,const util::URL & aParsedURL,utl::MediaDescriptor const & rDescriptor,FlatDetection & rFlatTypes)768 void TypeDetection::impl_getAllFormatTypes(
769 std::unique_lock<std::mutex>& rGuard,
770 const util::URL& aParsedURL, utl::MediaDescriptor const & rDescriptor, FlatDetection& rFlatTypes)
771 {
772 rFlatTypes.clear();
773
774 // Get all filters that we have.
775 std::vector<OUString> aFilterNames;
776 try
777 {
778 auto & cache = GetTheFilterCache();
779 cache.load(FilterCache::E_CONTAINS_FILTERS);
780 aFilterNames = cache.getItemNames(FilterCache::E_FILTER);
781 }
782 catch (const container::NoSuchElementException&)
783 {
784 return;
785 }
786
787 // Retrieve the default type for each of these filters, and store them.
788 for (auto const& filterName : aFilterNames)
789 {
790 OUString aType = impl_getTypeFromFilter(rGuard, filterName);
791
792 if (aType.isEmpty())
793 continue;
794
795 FlatDetectionInfo aInfo; // all flags set to false by default.
796 aInfo.sType = aType;
797 rFlatTypes.push_back(aInfo);
798 }
799
800 {
801 // Get all types that match the URL alone.
802 FlatDetection aFlatByURL;
803 GetTheFilterCache().detectFlatForURL(aParsedURL, aFlatByURL);
804 for (auto const& elem : aFlatByURL)
805 {
806 FlatDetection::iterator itPos = std::find_if(rFlatTypes.begin(), rFlatTypes.end(), FindByType(elem.sType));
807 if (itPos == rFlatTypes.end())
808 // Not in the list yet.
809 rFlatTypes.push_back(elem);
810 else
811 {
812 // Already in the list. Update the flags.
813 FlatDetectionInfo& rInfo = *itPos;
814 const FlatDetectionInfo& rThisInfo = elem;
815 if (rThisInfo.bMatchByExtension)
816 rInfo.bMatchByExtension = true;
817 if (rThisInfo.bMatchByPattern)
818 rInfo.bMatchByPattern = true;
819 if (rThisInfo.bPreselectedByDocumentService)
820 rInfo.bPreselectedByDocumentService = true;
821 }
822 }
823 }
824
825 // Remove duplicates.
826 std::stable_sort(rFlatTypes.begin(), rFlatTypes.end(), SortByType());
827 auto last = std::unique(rFlatTypes.begin(), rFlatTypes.end(), EqualByType());
828 rFlatTypes.erase(last, rFlatTypes.end());
829
830 // Mark pre-selected type (if any) to have it prioritized.
831 OUString sSelectedType = rDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_TYPENAME, OUString());
832 if (!sSelectedType.isEmpty())
833 impl_getPreselectionForType(rGuard, sSelectedType, aParsedURL, rFlatTypes, false);
834
835 // Mark all types preferred by the current document service, to have it prioritized.
836 OUString sSelectedDoc = rDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_DOCUMENTSERVICE, OUString());
837 if (!sSelectedDoc.isEmpty())
838 impl_getPreselectionForDocumentService(rGuard, sSelectedDoc, aParsedURL, rFlatTypes);
839 }
840
841
isBrokenZIP(const css::uno::Reference<css::io::XInputStream> & xStream,const css::uno::Reference<css::uno::XComponentContext> & xContext)842 static bool isBrokenZIP(const css::uno::Reference<css::io::XInputStream>& xStream,
843 const css::uno::Reference<css::uno::XComponentContext>& xContext)
844 {
845 try
846 {
847 // Only consider seekable streams starting with "PK", to avoid false detections
848 css::uno::Reference<css::io::XSeekable> xSeek(xStream, css::uno::UNO_QUERY_THROW);
849 comphelper::ScopeGuard restorePos(
850 [xSeek, nPos = xSeek->getPosition()]
851 {
852 try
853 {
854 xSeek->seek(nPos);
855 }
856 catch (const css::uno::Exception&)
857 {
858 }
859 });
860 css::uno::Sequence<sal_Int8> magic(2);
861 xStream->readBytes(magic, 2);
862 if (magic.getLength() < 2 || magic[0] != 'P' || magic[1] != 'K')
863 return false;
864 }
865 catch (const css::uno::Exception&)
866 {
867 return false;
868 }
869
870 std::vector<css::uno::Any> aArguments{
871 css::uno::Any(xStream),
872 css::uno::Any(css::beans::NamedValue(u"AllowRemoveOnInsert"_ustr, css::uno::Any(false))),
873 css::uno::Any(css::beans::NamedValue(u"StorageFormat"_ustr,
874 css::uno::Any(css::embed::StorageFormats::ZIP))),
875 };
876 try
877 {
878 // If this is a broken ZIP package, or not a ZIP, this would throw ZipIOException
879 xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
880 u"com.sun.star.packages.comp.ZipPackage"_ustr, comphelper::containerToSequence(aArguments),
881 xContext);
882 }
883 catch (const css::packages::zip::ZipIOException&)
884 {
885 // Now test if repair will succeed
886 aArguments.emplace_back(css::beans::NamedValue(u"RepairPackage"_ustr, css::uno::Any(true)));
887 try
888 {
889 // If this is a broken ZIP package that can be repaired, this would succeed,
890 // and the result will be not empty
891 if (css::uno::Reference<css::beans::XPropertySet> xPackage{
892 xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
893 u"com.sun.star.packages.comp.ZipPackage"_ustr,
894 comphelper::containerToSequence(aArguments), xContext),
895 css::uno::UNO_QUERY })
896 if (bool bHasElements; xPackage->getPropertyValue(u"HasElements"_ustr) >>= bHasElements)
897 return bHasElements;
898 }
899 catch (const css::uno::Exception&)
900 {
901 }
902 }
903 catch (const css::uno::Exception&)
904 {
905 }
906 // The package is either not broken, or is not a repairable ZIP
907 return false;
908 }
909
910
impl_detectTypeFlatAndDeep(utl::MediaDescriptor & rDescriptor,const FlatDetection & lFlatTypes,bool bAllowDeep,OUString & rLastChance)911 OUString TypeDetection::impl_detectTypeFlatAndDeep( utl::MediaDescriptor& rDescriptor ,
912 const FlatDetection& lFlatTypes ,
913 bool bAllowDeep ,
914 OUString& rLastChance )
915 {
916 // reset it everytimes, so the outside code can distinguish between
917 // a set and a not set value.
918 rLastChance.clear();
919
920 // tdf#96401: First of all, check if this is a broken ZIP package. Not doing this here would
921 // make some filters silently not recognize their content in broken packages, and some filters
922 // show a warning and mistakenly claim own content based on user choice.
923 if (bAllowDeep && !rDescriptor.getUnpackedValueOrDefault(u"RepairPackage"_ustr, false)
924 && rDescriptor.getUnpackedValueOrDefault(u"RepairAllowed"_ustr, true)
925 && rDescriptor.contains(utl::MediaDescriptor::PROP_INTERACTIONHANDLER))
926 {
927 try
928 {
929 // tdf#161573: do not interact with the user about possible unrelated failures (e.g.,
930 // missing file). If needed, that will happen later, in the main detection phase.
931 auto aInteraction(rDescriptor[utl::MediaDescriptor::PROP_INTERACTIONHANDLER]);
932 rDescriptor.erase(utl::MediaDescriptor::PROP_INTERACTIONHANDLER);
933 comphelper::ScopeGuard interactionHelperGuard([&rDescriptor, &aInteraction]
934 { rDescriptor[utl::MediaDescriptor::PROP_INTERACTIONHANDLER] = aInteraction; });
935 impl_openStream(rDescriptor);
936 if (auto xStream = rDescriptor.getUnpackedValueOrDefault(
937 utl::MediaDescriptor::PROP_INPUTSTREAM,
938 css::uno::Reference<css::io::XInputStream>()))
939 {
940 css::uno::Reference<css::uno::XComponentContext> xContext;
941
942 // SAFE ->
943 {
944 std::unique_lock aLock(m_aMutex);
945 xContext = m_xContext;
946 }
947 // <- SAFE
948
949 if (isBrokenZIP(xStream, xContext))
950 {
951 if (css::uno::Reference<css::task::XInteractionHandler> xInteraction{
952 aInteraction,
953 css::uno::UNO_QUERY })
954 {
955 INetURLObject aURL(rDescriptor.getUnpackedValueOrDefault(
956 utl::MediaDescriptor::PROP_URL, OUString()));
957 OUString aDocumentTitle
958 = aURL.getName(INetURLObject::LAST_SEGMENT, true,
959 INetURLObject::DecodeMechanism::WithCharset);
960
961 // Ask the user whether they wants to try to repair
962 RequestPackageReparation aRequest(aDocumentTitle);
963 xInteraction->handle(aRequest.GetRequest());
964
965 if (aRequest.isApproved())
966 {
967 // lok: we want to overwrite file in jail, so don't use template flag
968 const bool bIsLOK = comphelper::LibreOfficeKit::isActive();
969 rDescriptor[utl::MediaDescriptor::PROP_DOCUMENTTITLE] <<= aDocumentTitle;
970 rDescriptor[utl::MediaDescriptor::PROP_ASTEMPLATE] <<= !bIsLOK;
971 rDescriptor[u"RepairPackage"_ustr] <<= true;
972 }
973 else
974 rDescriptor[u"RepairAllowed"_ustr] <<= false; // Do not ask again
975 }
976 }
977 }
978 }
979 catch (const css::uno::Exception&)
980 {
981 // No problem
982 }
983 }
984
985 // step over all possible types for this URL.
986 // solutions:
987 // a) no types => no detection
988 // b) deep detection not allowed => return first valid type of list (because it's the preferred or the first valid one)
989 // or(!) match by URLPattern => in such case a deep detection will be suppressed!
990 // c) type has no detect service => safe the first occurred type without a detect service
991 // as "last chance"(!). It will be used outside of this method
992 // if no further type could be detected.
993 // It must be the first one, because it can be a preferred type.
994 // Our types list was sorted by such criteria!
995 // d) detect service return a valid result => return its decision
996 // e) detect service return an invalid result
997 // or any needed information could not be
998 // obtained from the cache => ignore it, and continue with search
999
1000 for (auto const& flatTypeInfo : lFlatTypes)
1001 {
1002 if (m_bCancel)
1003 break;
1004 OUString sFlatType = flatTypeInfo.sType;
1005
1006 if (!impl_validateAndSetTypeOnDescriptor(rDescriptor, sFlatType))
1007 continue;
1008
1009 // b)
1010 if (
1011 (!bAllowDeep ) ||
1012 (flatTypeInfo.bMatchByPattern)
1013 )
1014 {
1015 return sFlatType;
1016 }
1017
1018 try
1019 {
1020 // SAFE -> ----------------------------------
1021 std::unique_lock aLock(m_aMutex);
1022 CacheItem aType = GetTheFilterCache().getItem(FilterCache::E_TYPE, sFlatType);
1023 aLock.unlock();
1024
1025 OUString sDetectService;
1026 aType[PROPNAME_DETECTSERVICE] >>= sDetectService;
1027
1028 // c)
1029 if (sDetectService.isEmpty())
1030 {
1031 // flat detected types without any registered deep detection service and not
1032 // preselected by the user can be used as LAST CHANCE in case no other type could
1033 // be detected. Of course only the first type without deep detector can be used.
1034 // Further ones has to be ignored.
1035 if (rLastChance.isEmpty())
1036 rLastChance = sFlatType;
1037
1038 continue;
1039 }
1040
1041 OUString sDeepType = impl_askDetectService(sDetectService, rDescriptor);
1042
1043 // d)
1044 if (!sDeepType.isEmpty())
1045 return sDeepType;
1046 }
1047 catch(const css::container::NoSuchElementException&)
1048 {}
1049 // e)
1050 }
1051
1052 return OUString();
1053 // <- SAFE ----------------------------------
1054 }
1055
impl_seekStreamToZero(utl::MediaDescriptor const & rDescriptor)1056 void TypeDetection::impl_seekStreamToZero(utl::MediaDescriptor const & rDescriptor)
1057 {
1058 // try to seek to 0 ...
1059 // But because XSeekable is an optional interface ... try it only .-)
1060 css::uno::Reference< css::io::XInputStream > xStream = rDescriptor.getUnpackedValueOrDefault(
1061 utl::MediaDescriptor::PROP_INPUTSTREAM,
1062 css::uno::Reference< css::io::XInputStream >());
1063 css::uno::Reference< css::io::XSeekable > xSeek(xStream, css::uno::UNO_QUERY);
1064 if (!xSeek.is())
1065 return;
1066
1067 try
1068 {
1069 xSeek->seek(0);
1070 }
1071 catch(const css::uno::RuntimeException&)
1072 {
1073 throw;
1074 }
1075 catch(const css::uno::Exception&)
1076 {
1077 }
1078 }
1079
impl_askDetectService(const OUString & sDetectService,utl::MediaDescriptor & rDescriptor)1080 OUString TypeDetection::impl_askDetectService(const OUString& sDetectService,
1081 utl::MediaDescriptor& rDescriptor )
1082 {
1083 // Open the stream and add it to the media descriptor if this method is called for the first time.
1084 // All following requests to this method will detect, that there already exists a stream .-)
1085 // Attention: This method throws an exception if the stream could not be opened.
1086 // It's important to break any further detection in such case.
1087 // Catch it on the highest detection level only !!!
1088 impl_openStream(rDescriptor);
1089
1090 // seek to 0 is an optional feature to be more robust against
1091 // "simple implemented detect services" .-)
1092 impl_seekStreamToZero(rDescriptor);
1093
1094 css::uno::Reference< css::document::XExtendedFilterDetection > xDetector;
1095 css::uno::Reference< css::uno::XComponentContext > xContext;
1096
1097 // SAFE ->
1098 {
1099 std::unique_lock aLock(m_aMutex);
1100 xContext = m_xContext;
1101 }
1102 // <- SAFE
1103
1104 try
1105 {
1106 // Attention! If e.g. an office module was not installed sometimes we
1107 // find a registered detect service, which is referred inside the
1108 // configuration ... but not really installed. On the other side we use
1109 // third party components here, which can make trouble anyway. So we
1110 // should handle errors during creation of such services more
1111 // gracefully .-)
1112 xDetector.set(
1113 xContext->getServiceManager()->createInstanceWithContext(sDetectService, xContext),
1114 css::uno::UNO_QUERY_THROW);
1115 }
1116 catch (...)
1117 {
1118 }
1119
1120 if ( ! xDetector.is())
1121 return OUString();
1122
1123 OUString sDeepType;
1124 try
1125 {
1126 // start deep detection
1127 // Don't forget to convert stl descriptor to its uno representation.
1128
1129 /* Attention!
1130 You have to use an explicit instance of this uno sequence...
1131 Because it's used as an in out parameter. And in case of a temp. used object
1132 we will run into memory corruptions!
1133 */
1134 css::uno::Sequence< css::beans::PropertyValue > lDescriptor;
1135 rDescriptor >> lDescriptor;
1136 sDeepType = xDetector->detect(lDescriptor);
1137 rDescriptor << lDescriptor;
1138 }
1139 catch (...)
1140 {
1141 // We should ignore errors here.
1142 // Thrown exceptions mostly will end in crash recovery...
1143 // But might be we find another deep detection service which can detect the same
1144 // document without a problem .-)
1145 sDeepType.clear();
1146 }
1147
1148 // seek to 0 is an optional feature to be more robust against
1149 // "simple implemented detect services" .-)
1150 impl_seekStreamToZero(rDescriptor);
1151
1152 // analyze the results
1153 // a) detect service returns "" => return "" too and remove TYPE/FILTER prop from descriptor
1154 // b) returned type is unknown => return "" too and remove TYPE/FILTER prop from descriptor
1155 // c) returned type is valid => check TYPE/FILTER props inside descriptor and return the type
1156
1157 // this special helper checks for a valid type
1158 // and set right values on the descriptor!
1159 bool bValidType = impl_validateAndSetTypeOnDescriptor(rDescriptor, sDeepType);
1160 if (bValidType)
1161 return sDeepType;
1162
1163 return OUString();
1164 }
1165
1166
impl_askUserForTypeAndFilterIfAllowed(utl::MediaDescriptor & rDescriptor)1167 OUString TypeDetection::impl_askUserForTypeAndFilterIfAllowed(utl::MediaDescriptor& rDescriptor)
1168 {
1169 css::uno::Reference< css::task::XInteractionHandler > xInteraction =
1170 rDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_INTERACTIONHANDLER,
1171 css::uno::Reference< css::task::XInteractionHandler >());
1172
1173 if (!xInteraction.is())
1174 return OUString();
1175
1176 OUString sURL =
1177 rDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_URL,
1178 OUString());
1179
1180 css::uno::Reference< css::io::XInputStream > xStream =
1181 rDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_INPUTSTREAM,
1182 css::uno::Reference< css::io::XInputStream >());
1183
1184 // Don't disturb the user for "non existing files - means empty URLs" or
1185 // if we were forced to detect a stream.
1186 // Reason behind: we must be sure to ask user for "unknown contents" only...
1187 // and not for "missing files". Especially if detection is done by a stream only
1188 // we can't check if the stream points to an "existing content"!
1189 if (
1190 (sURL.isEmpty() ) || // "non existing file" ?
1191 (!xStream.is() ) || // non existing file !
1192 (sURL.equalsIgnoreAsciiCase("private:stream")) // not a good idea .-)
1193 )
1194 return OUString();
1195
1196 try
1197 {
1198 // create a new request to ask user for its decision about the usable filter
1199 ::framework::RequestFilterSelect aRequest(sURL);
1200 xInteraction->handle(aRequest.GetRequest());
1201
1202 // "Cancel" pressed? => return with error
1203 if (aRequest.isAbort())
1204 return OUString();
1205
1206 // "OK" pressed => verify the selected filter, get its corresponding
1207 // type and return it. (BTW: We must update the media descriptor here ...)
1208 // The user selected explicitly a filter ... but normally we are interested on
1209 // a type here only. But we must be sure, that the selected filter is used
1210 // too and no ambiguous filter registration disturb us .-)
1211
1212 OUString sFilter = aRequest.getFilter();
1213 if (!impl_validateAndSetFilterOnDescriptor(rDescriptor, sFilter))
1214 return OUString();
1215 OUString sType;
1216 rDescriptor[utl::MediaDescriptor::PROP_TYPENAME] >>= sType;
1217 return sType;
1218 }
1219 catch(const css::uno::Exception&)
1220 {}
1221
1222 return OUString();
1223 }
1224
1225
impl_openStream(utl::MediaDescriptor & rDescriptor)1226 void TypeDetection::impl_openStream(utl::MediaDescriptor& rDescriptor)
1227 {
1228 bool bSuccess = false;
1229 OUString sURL = rDescriptor.getUnpackedValueOrDefault( utl::MediaDescriptor::PROP_URL, OUString() );
1230 bool bRequestedReadOnly = rDescriptor.getUnpackedValueOrDefault( utl::MediaDescriptor::PROP_READONLY, false );
1231 if ( comphelper::isFileUrl( sURL ) )
1232 {
1233 // OOo uses own file locking mechanics in case of local file
1234 bSuccess = rDescriptor.addInputStreamOwnLock();
1235 }
1236 else
1237 bSuccess = rDescriptor.addInputStream();
1238
1239 if ( !bSuccess )
1240 throw css::uno::Exception(
1241 "Could not open stream for <" + sURL + ">",
1242 getXWeak());
1243
1244 if ( !bRequestedReadOnly )
1245 {
1246 // The MediaDescriptor implementation adds ReadOnly argument if the file can not be opened for writing
1247 // this argument should be either removed or an additional argument should be added so that application
1248 // can separate the case when the user explicitly requests readonly document.
1249 // The current solution is to remove it here.
1250 rDescriptor.erase( utl::MediaDescriptor::PROP_READONLY );
1251 }
1252 }
1253
1254
impl_removeTypeFilterFromDescriptor(utl::MediaDescriptor & rDescriptor)1255 void TypeDetection::impl_removeTypeFilterFromDescriptor(utl::MediaDescriptor& rDescriptor)
1256 {
1257 utl::MediaDescriptor::iterator pItType = rDescriptor.find(utl::MediaDescriptor::PROP_TYPENAME );
1258 utl::MediaDescriptor::iterator pItFilter = rDescriptor.find(utl::MediaDescriptor::PROP_FILTERNAME);
1259 if (pItType != rDescriptor.end())
1260 rDescriptor.erase(pItType);
1261 if (pItFilter != rDescriptor.end())
1262 rDescriptor.erase(pItFilter);
1263 }
1264
1265
impl_validateAndSetTypeOnDescriptor(utl::MediaDescriptor & rDescriptor,const OUString & sType)1266 bool TypeDetection::impl_validateAndSetTypeOnDescriptor( utl::MediaDescriptor& rDescriptor,
1267 const OUString& sType )
1268 {
1269 if (GetTheFilterCache().hasItem(FilterCache::E_TYPE, sType))
1270 {
1271 rDescriptor[utl::MediaDescriptor::PROP_TYPENAME] <<= sType;
1272 return true;
1273 }
1274
1275 // remove all related information from the descriptor
1276 impl_removeTypeFilterFromDescriptor(rDescriptor);
1277 return false;
1278 }
1279
1280
impl_validateAndSetFilterOnDescriptor(utl::MediaDescriptor & rDescriptor,const OUString & sFilter)1281 bool TypeDetection::impl_validateAndSetFilterOnDescriptor( utl::MediaDescriptor& rDescriptor,
1282 const OUString& sFilter )
1283 {
1284 try
1285 {
1286 auto & cache = GetTheFilterCache();
1287 CacheItem aFilter = cache.getItem(FilterCache::E_FILTER, sFilter);
1288 OUString sType;
1289 aFilter[PROPNAME_TYPE] >>= sType;
1290
1291 // found valid type and filter => set it on the given descriptor
1292 rDescriptor[utl::MediaDescriptor::PROP_TYPENAME ] <<= sType ;
1293 rDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] <<= sFilter;
1294 return true;
1295 }
1296 catch(const css::container::NoSuchElementException&){}
1297
1298 // remove all related information from the descriptor
1299 impl_removeTypeFilterFromDescriptor(rDescriptor);
1300 return false;
1301 }
1302
1303 } // namespace filter
1304
1305 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
filter_TypeDetection_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)1306 filter_TypeDetection_get_implementation(
1307 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
1308 {
1309 return cppu::acquire(new filter::config::TypeDetection(context));
1310 }
1311
1312 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1313