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 <com/sun/star/beans/PropertyValue.hpp>
21 #include <com/sun/star/configuration/theDefaultProvider.hpp>
22 #include <com/sun/star/container/XContainerQuery.hpp>
23 #include <com/sun/star/container/XNameAccess.hpp>
24 #include <com/sun/star/embed/VerbDescriptor.hpp>
25 #include <com/sun/star/document/XTypeDetection.hpp>
26 
27 #include <osl/diagnose.h>
28 
29 #include <comphelper/fileformat.h>
30 #include <comphelper/mimeconfighelper.hxx>
31 #include <comphelper/classids.hxx>
32 #include <comphelper/sequenceashashmap.hxx>
33 #include <comphelper/documentconstants.hxx>
34 #include <comphelper/propertysequence.hxx>
35 #include <rtl/ustrbuf.hxx>
36 
37 
38 using namespace ::com::sun::star;
39 using namespace comphelper;
40 
41 
MimeConfigurationHelper(const uno::Reference<uno::XComponentContext> & rxContext)42 MimeConfigurationHelper::MimeConfigurationHelper( const uno::Reference< uno::XComponentContext >& rxContext )
43 : m_xContext( rxContext )
44 {
45     if ( !m_xContext.is() )
46         throw uno::RuntimeException();
47 }
48 
49 
GetStringClassIDRepresentation(const uno::Sequence<sal_Int8> & aClassID)50 OUString MimeConfigurationHelper::GetStringClassIDRepresentation( const uno::Sequence< sal_Int8 >& aClassID )
51 {
52     OUStringBuffer aResult;
53 
54     if ( aClassID.getLength() == 16 )
55     {
56         for ( sal_Int32 nInd = 0; nInd < aClassID.getLength(); nInd++ )
57         {
58             if ( nInd == 4 || nInd == 6 || nInd == 8 || nInd == 10 )
59                 aResult.append("-");
60 
61             sal_Int32 nDigit1 = static_cast<sal_Int32>( static_cast<sal_uInt8>(aClassID[nInd]) / 16 );
62             sal_Int32 nDigit2 = static_cast<sal_uInt8>(aClassID[nInd]) % 16;
63             aResult.append( OUString::number(nDigit1, 16) + OUString::number( nDigit2, 16 ) );
64         }
65     }
66 
67     return aResult.makeStringAndClear();
68 }
69 
70 
GetDigit_Impl(char aChar)71 static sal_uInt8 GetDigit_Impl( char aChar )
72 {
73     if ( aChar >= '0' && aChar <= '9' )
74         return aChar - '0';
75     else if ( aChar >= 'a' && aChar <= 'f' )
76         return aChar - 'a' + 10;
77     else if ( aChar >= 'A' && aChar <= 'F' )
78         return aChar - 'A' + 10;
79     else
80         return 16;
81 }
82 
83 
GetSequenceClassIDRepresentation(const OUString & aClassID)84 uno::Sequence< sal_Int8 > MimeConfigurationHelper::GetSequenceClassIDRepresentation( const OUString& aClassID )
85 {
86     sal_Int32 nLength = aClassID.getLength();
87     if ( nLength == 36 )
88     {
89         OString aCharClassID = OUStringToOString( aClassID, RTL_TEXTENCODING_ASCII_US );
90         uno::Sequence< sal_Int8 > aResult( 16 );
91 
92         sal_Int32 nStrPointer = 0;
93         sal_Int32 nSeqInd = 0;
94         while( nSeqInd < 16 && nStrPointer + 1 < nLength )
95         {
96             sal_uInt8 nDigit1 = GetDigit_Impl( aCharClassID[nStrPointer++] );
97             sal_uInt8 nDigit2 = GetDigit_Impl( aCharClassID[nStrPointer++] );
98 
99             if ( nDigit1 > 15 || nDigit2 > 15 )
100                 break;
101 
102             aResult[nSeqInd++] = static_cast<sal_Int8>( nDigit1 * 16 + nDigit2 );
103 
104             if ( nStrPointer < nLength && aCharClassID[nStrPointer] == '-' )
105                 nStrPointer++;
106         }
107 
108         if ( nSeqInd == 16 && nStrPointer == nLength )
109             return aResult;
110     }
111 
112     return uno::Sequence< sal_Int8 >();
113 }
114 
115 
GetConfigurationByPath(const OUString & aPath)116 uno::Reference< container::XNameAccess > MimeConfigurationHelper::GetConfigurationByPath( const OUString& aPath )
117 {
118     osl::MutexGuard aGuard( m_aMutex );
119 
120     uno::Reference< container::XNameAccess > xConfig;
121 
122     try
123     {
124         if ( !m_xConfigProvider.is() )
125             m_xConfigProvider = configuration::theDefaultProvider::get( m_xContext );
126 
127         uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
128         {
129             {"nodepath", uno::Any(aPath)}
130         }));
131         xConfig.set( m_xConfigProvider->createInstanceWithArguments(
132                         "com.sun.star.configuration.ConfigurationAccess",
133                         aArgs ),
134                      uno::UNO_QUERY );
135     }
136     catch( uno::Exception& )
137     {}
138 
139     return xConfig;
140 }
141 
142 
GetObjConfiguration()143 uno::Reference< container::XNameAccess > MimeConfigurationHelper::GetObjConfiguration()
144 {
145     osl::MutexGuard aGuard( m_aMutex );
146 
147     if ( !m_xObjectConfig.is() )
148         m_xObjectConfig = GetConfigurationByPath(
149                                          "/org.openoffice.Office.Embedding/Objects" );
150 
151     return m_xObjectConfig;
152 }
153 
154 
GetVerbsConfiguration()155 uno::Reference< container::XNameAccess > MimeConfigurationHelper::GetVerbsConfiguration()
156 {
157     osl::MutexGuard aGuard( m_aMutex );
158 
159     if ( !m_xVerbsConfig.is() )
160         m_xVerbsConfig = GetConfigurationByPath(
161                                         "/org.openoffice.Office.Embedding/Verbs");
162 
163     return m_xVerbsConfig;
164 }
165 
166 
GetMediaTypeConfiguration()167 uno::Reference< container::XNameAccess > MimeConfigurationHelper::GetMediaTypeConfiguration()
168 {
169     osl::MutexGuard aGuard( m_aMutex );
170 
171     if ( !m_xMediaTypeConfig.is() )
172         m_xMediaTypeConfig = GetConfigurationByPath(
173                     "/org.openoffice.Office.Embedding/MimeTypeClassIDRelations");
174 
175     return m_xMediaTypeConfig;
176 }
177 
178 
GetFilterFactory()179 uno::Reference< container::XNameAccess > MimeConfigurationHelper::GetFilterFactory()
180 {
181     osl::MutexGuard aGuard( m_aMutex );
182 
183     if ( !m_xFilterFactory.is() )
184         m_xFilterFactory.set(
185             m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.FilterFactory", m_xContext),
186             uno::UNO_QUERY );
187 
188     return m_xFilterFactory;
189 }
190 
191 
GetDocServiceNameFromFilter(const OUString & aFilterName)192 OUString MimeConfigurationHelper::GetDocServiceNameFromFilter( const OUString& aFilterName )
193 {
194     OUString aDocServiceName;
195 
196     try
197     {
198         uno::Reference< container::XNameAccess > xFilterFactory(
199             GetFilterFactory(),
200             uno::UNO_SET_THROW );
201 
202         uno::Any aFilterAnyData = xFilterFactory->getByName( aFilterName );
203         uno::Sequence< beans::PropertyValue > aFilterData;
204         if ( aFilterAnyData >>= aFilterData )
205         {
206             for ( const auto & prop : std::as_const(aFilterData) )
207                 if ( prop.Name == "DocumentService" )
208                     prop.Value >>= aDocServiceName;
209         }
210     }
211     catch( uno::Exception& )
212     {}
213 
214     return aDocServiceName;
215 }
216 
217 
GetDocServiceNameFromMediaType(const OUString & aMediaType)218 OUString MimeConfigurationHelper::GetDocServiceNameFromMediaType( const OUString& aMediaType )
219 {
220     uno::Reference< container::XContainerQuery > xTypeCFG(
221             m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", m_xContext),
222             uno::UNO_QUERY );
223 
224     if ( xTypeCFG.is() )
225     {
226         try
227         {
228             // make query for all types matching the properties
229             uno::Sequence < beans::NamedValue > aSeq { { "MediaType", css::uno::Any(aMediaType) } };
230 
231             uno::Reference < container::XEnumeration > xEnum = xTypeCFG->createSubSetEnumerationByProperties( aSeq );
232             while ( xEnum->hasMoreElements() )
233             {
234                 uno::Sequence< beans::PropertyValue > aType;
235                 if ( xEnum->nextElement() >>= aType )
236                 {
237                     for ( const auto & prop : std::as_const(aType) )
238                     {
239                         OUString aFilterName;
240                         if ( prop.Name == "PreferredFilter"
241                           && ( prop.Value >>= aFilterName ) && !aFilterName.isEmpty() )
242                         {
243                             OUString aDocumentName = GetDocServiceNameFromFilter( aFilterName );
244                             if ( !aDocumentName.isEmpty() )
245                                 return aDocumentName;
246                         }
247                     }
248                 }
249             }
250         }
251         catch( uno::Exception& )
252         {}
253     }
254 
255     return OUString();
256 }
257 
258 
GetVerbByShortcut(const OUString & aVerbShortcut,embed::VerbDescriptor & aDescriptor)259 bool MimeConfigurationHelper::GetVerbByShortcut( const OUString& aVerbShortcut,
260                                                 embed::VerbDescriptor& aDescriptor )
261 {
262     bool bResult = false;
263 
264     uno::Reference< container::XNameAccess > xVerbsConfig = GetVerbsConfiguration();
265     uno::Reference< container::XNameAccess > xVerbsProps;
266     try
267     {
268         if ( xVerbsConfig.is() && ( xVerbsConfig->getByName( aVerbShortcut ) >>= xVerbsProps ) && xVerbsProps.is() )
269         {
270             embed::VerbDescriptor aTempDescr;
271             if ( ( xVerbsProps->getByName("VerbID") >>= aTempDescr.VerbID )
272               && ( xVerbsProps->getByName("VerbUIName") >>= aTempDescr.VerbName )
273               && ( xVerbsProps->getByName("VerbFlags") >>= aTempDescr.VerbFlags )
274               && ( xVerbsProps->getByName("VerbAttributes") >>= aTempDescr.VerbAttributes ) )
275             {
276                 aDescriptor = aTempDescr;
277                 bResult = true;
278             }
279         }
280     }
281     catch( uno::Exception& )
282     {
283     }
284 
285     return bResult;
286 }
287 
288 
GetObjPropsFromConfigEntry(const uno::Sequence<sal_Int8> & aClassID,const uno::Reference<container::XNameAccess> & xObjectProps)289 uno::Sequence< beans::NamedValue > MimeConfigurationHelper::GetObjPropsFromConfigEntry(
290                                             const uno::Sequence< sal_Int8 >& aClassID,
291                                             const uno::Reference< container::XNameAccess >& xObjectProps )
292 {
293     uno::Sequence< beans::NamedValue > aResult;
294 
295     if ( aClassID.getLength() == 16 )
296     {
297         try
298         {
299             uno::Sequence< OUString > aObjPropNames = xObjectProps->getElementNames();
300 
301             aResult.realloc( aObjPropNames.getLength() + 1 );
302             aResult[0].Name = "ClassID";
303             aResult[0].Value <<= aClassID;
304 
305             for ( sal_Int32 nInd = 0; nInd < aObjPropNames.getLength(); nInd++ )
306             {
307                 aResult[nInd + 1].Name = aObjPropNames[nInd];
308 
309                 if ( aObjPropNames[nInd] == "ObjectVerbs" )
310                 {
311                     uno::Sequence< OUString > aVerbShortcuts;
312                     if ( !(xObjectProps->getByName( aObjPropNames[nInd] ) >>= aVerbShortcuts) )
313                         throw uno::RuntimeException();
314                     uno::Sequence< embed::VerbDescriptor > aVerbDescriptors( aVerbShortcuts.getLength() );
315                     for ( sal_Int32 nVerbI = 0; nVerbI < aVerbShortcuts.getLength(); nVerbI++ )
316                         if ( !GetVerbByShortcut( aVerbShortcuts[nVerbI], aVerbDescriptors[nVerbI] ) )
317                             throw uno::RuntimeException();
318 
319                     aResult[nInd+1].Value <<= aVerbDescriptors;
320                 }
321                 else
322                     aResult[nInd+1].Value = xObjectProps->getByName( aObjPropNames[nInd] );
323             }
324         }
325         catch( uno::Exception& )
326         {
327             aResult.realloc( 0 );
328         }
329     }
330 
331     return aResult;
332 }
333 
334 
GetExplicitlyRegisteredObjClassID(const OUString & aMediaType)335 OUString MimeConfigurationHelper::GetExplicitlyRegisteredObjClassID( const OUString& aMediaType )
336 {
337     OUString aStringClassID;
338 
339     uno::Reference< container::XNameAccess > xMediaTypeConfig = GetMediaTypeConfiguration();
340     try
341     {
342         if ( xMediaTypeConfig.is() )
343             xMediaTypeConfig->getByName( aMediaType ) >>= aStringClassID;
344     }
345     catch( uno::Exception& )
346     {
347     }
348 
349     return aStringClassID;
350 
351 }
352 
353 
GetObjectPropsByStringClassID(const OUString & aStringClassID)354 uno::Sequence< beans::NamedValue > MimeConfigurationHelper::GetObjectPropsByStringClassID(
355                                                                 const OUString& aStringClassID )
356 {
357     uno::Sequence< beans::NamedValue > aObjProps;
358 
359     uno::Sequence< sal_Int8 > aClassID = GetSequenceClassIDRepresentation( aStringClassID );
360     if ( ClassIDsEqual( aClassID, GetSequenceClassID( SO3_DUMMY_CLASSID ) ) )
361     {
362         aObjProps.realloc(2);
363         aObjProps[0].Name = "ObjectFactory";
364         aObjProps[0].Value <<= OUString( "com.sun.star.embed.OOoSpecialEmbeddedObjectFactory" );
365         aObjProps[1].Name = "ClassID";
366         aObjProps[1].Value <<= aClassID;
367         return aObjProps;
368     }
369 
370     if ( aClassID.getLength() == 16 )
371     {
372         uno::Reference< container::XNameAccess > xObjConfig = GetObjConfiguration();
373         uno::Reference< container::XNameAccess > xObjectProps;
374         try
375         {
376             // TODO/LATER: allow to provide ClassID string in any format, only digits are counted
377             if ( xObjConfig.is() && ( xObjConfig->getByName( aStringClassID.toAsciiUpperCase() ) >>= xObjectProps ) && xObjectProps.is() )
378                 aObjProps = GetObjPropsFromConfigEntry( aClassID, xObjectProps );
379         }
380         catch( uno::Exception& )
381         {
382         }
383     }
384 
385     return aObjProps;
386 }
387 
388 
GetObjectPropsByClassID(const uno::Sequence<sal_Int8> & aClassID)389 uno::Sequence< beans::NamedValue > MimeConfigurationHelper::GetObjectPropsByClassID(
390                                                                 const uno::Sequence< sal_Int8 >& aClassID )
391 {
392     uno::Sequence< beans::NamedValue > aObjProps;
393     if ( ClassIDsEqual( aClassID, GetSequenceClassID( SO3_DUMMY_CLASSID ) ) )
394     {
395         aObjProps.realloc(2);
396         aObjProps[0].Name = "ObjectFactory";
397         aObjProps[0].Value <<= OUString( "com.sun.star.embed.OOoSpecialEmbeddedObjectFactory" );
398         aObjProps[1].Name = "ClassID";
399         aObjProps[1].Value <<= aClassID;
400     }
401 
402     OUString aStringClassID = GetStringClassIDRepresentation( aClassID );
403     if ( !aStringClassID.isEmpty() )
404     {
405         uno::Reference< container::XNameAccess > xObjConfig = GetObjConfiguration();
406         uno::Reference< container::XNameAccess > xObjectProps;
407         try
408         {
409             if ( xObjConfig.is() && ( xObjConfig->getByName( aStringClassID.toAsciiUpperCase() ) >>= xObjectProps ) && xObjectProps.is() )
410                 aObjProps = GetObjPropsFromConfigEntry( aClassID, xObjectProps );
411         }
412         catch( uno::Exception& )
413         {
414         }
415     }
416 
417     return aObjProps;
418 }
419 
420 
GetObjectPropsByMediaType(const OUString & aMediaType)421 uno::Sequence< beans::NamedValue > MimeConfigurationHelper::GetObjectPropsByMediaType( const OUString& aMediaType )
422 {
423     uno::Sequence< beans::NamedValue > aObject =
424                                     GetObjectPropsByStringClassID( GetExplicitlyRegisteredObjClassID( aMediaType ) );
425     if ( aObject.hasElements() )
426         return aObject;
427 
428     OUString aDocumentName = GetDocServiceNameFromMediaType( aMediaType );
429     if ( !aDocumentName.isEmpty() )
430         return GetObjectPropsByDocumentName( aDocumentName );
431 
432     return uno::Sequence< beans::NamedValue >();
433 }
434 
435 
GetObjectPropsByFilter(const OUString & aFilterName)436 uno::Sequence< beans::NamedValue > MimeConfigurationHelper::GetObjectPropsByFilter( const OUString& aFilterName )
437 {
438     OUString aDocumentName = GetDocServiceNameFromFilter( aFilterName );
439     if ( !aDocumentName.isEmpty() )
440         return GetObjectPropsByDocumentName( aDocumentName );
441 
442     return uno::Sequence< beans::NamedValue >();
443 }
444 
445 
GetObjectPropsByDocumentName(std::u16string_view aDocName)446 uno::Sequence< beans::NamedValue > MimeConfigurationHelper::GetObjectPropsByDocumentName( std::u16string_view aDocName )
447 {
448     if ( !aDocName.empty() )
449     {
450         uno::Reference< container::XNameAccess > xObjConfig = GetObjConfiguration();
451         if ( xObjConfig.is() )
452         {
453             try
454             {
455                 const uno::Sequence< OUString > aClassIDs = xObjConfig->getElementNames();
456                 for ( const OUString & id : aClassIDs )
457                 {
458                     uno::Reference< container::XNameAccess > xObjectProps;
459                     OUString aEntryDocName;
460 
461                     if ( ( xObjConfig->getByName( id ) >>= xObjectProps ) && xObjectProps.is()
462                       && ( xObjectProps->getByName("ObjectDocumentServiceName") >>= aEntryDocName )
463                       && aEntryDocName == aDocName )
464                     {
465                         return GetObjPropsFromConfigEntry( GetSequenceClassIDRepresentation( id ),
466                                                             xObjectProps );
467                     }
468                 }
469             }
470             catch( uno::Exception& )
471             {}
472         }
473     }
474 
475     return uno::Sequence< beans::NamedValue >();
476 }
477 
478 
GetFactoryNameByClassID(const uno::Sequence<sal_Int8> & aClassID)479 OUString MimeConfigurationHelper::GetFactoryNameByClassID( const uno::Sequence< sal_Int8 >& aClassID )
480 {
481     return GetFactoryNameByStringClassID( GetStringClassIDRepresentation( aClassID ) );
482 }
483 
484 
GetFactoryNameByStringClassID(const OUString & aStringClassID)485 OUString MimeConfigurationHelper::GetFactoryNameByStringClassID( const OUString& aStringClassID )
486 {
487     OUString aResult;
488 
489     if ( !aStringClassID.isEmpty() )
490     {
491         uno::Reference< container::XNameAccess > xObjConfig = GetObjConfiguration();
492         uno::Reference< container::XNameAccess > xObjectProps;
493         try
494         {
495             if ( xObjConfig.is() && ( xObjConfig->getByName( aStringClassID.toAsciiUpperCase() ) >>= xObjectProps ) && xObjectProps.is() )
496                 xObjectProps->getByName("ObjectFactory") >>= aResult;
497         }
498         catch( uno::Exception& )
499         {
500             uno::Sequence< sal_Int8 > aClassID = GetSequenceClassIDRepresentation( aStringClassID );
501             if ( ClassIDsEqual( aClassID, GetSequenceClassID( SO3_DUMMY_CLASSID ) ) )
502                 return "com.sun.star.embed.OOoSpecialEmbeddedObjectFactory";
503         }
504     }
505 
506     return aResult;
507 }
508 
509 
GetFactoryNameByDocumentName(std::u16string_view aDocName)510 OUString MimeConfigurationHelper::GetFactoryNameByDocumentName( std::u16string_view aDocName )
511 {
512     OUString aResult;
513 
514     if ( !aDocName.empty() )
515     {
516         uno::Reference< container::XNameAccess > xObjConfig = GetObjConfiguration();
517         if ( xObjConfig.is() )
518         {
519             try
520             {
521                 const uno::Sequence< OUString > aClassIDs = xObjConfig->getElementNames();
522                 for ( const OUString & id : aClassIDs )
523                 {
524                     uno::Reference< container::XNameAccess > xObjectProps;
525                     OUString aEntryDocName;
526 
527                     if ( ( xObjConfig->getByName( id ) >>= xObjectProps ) && xObjectProps.is()
528                       && ( xObjectProps->getByName( "ObjectDocumentServiceName" ) >>= aEntryDocName )
529                       && aEntryDocName == aDocName )
530                     {
531                         xObjectProps->getByName("ObjectFactory") >>= aResult;
532                         break;
533                     }
534                 }
535             }
536             catch( uno::Exception& )
537             {}
538         }
539     }
540 
541     return aResult;
542 }
543 
544 
GetFactoryNameByMediaType(const OUString & aMediaType)545 OUString MimeConfigurationHelper::GetFactoryNameByMediaType( const OUString& aMediaType )
546 {
547     OUString aResult = GetFactoryNameByStringClassID( GetExplicitlyRegisteredObjClassID( aMediaType ) );
548 
549     if ( aResult.isEmpty() )
550     {
551         OUString aDocumentName = GetDocServiceNameFromMediaType( aMediaType );
552         if ( !aDocumentName.isEmpty() )
553             aResult = GetFactoryNameByDocumentName( aDocumentName );
554     }
555 
556     return aResult;
557 }
558 
559 
UpdateMediaDescriptorWithFilterName(uno::Sequence<beans::PropertyValue> & aMediaDescr,bool bIgnoreType)560 OUString MimeConfigurationHelper::UpdateMediaDescriptorWithFilterName(
561                                         uno::Sequence< beans::PropertyValue >& aMediaDescr,
562                                         bool bIgnoreType )
563 {
564     OUString aFilterName;
565 
566     for ( const auto & prop : std::as_const(aMediaDescr) )
567         if ( prop.Name == "FilterName" )
568             prop.Value >>= aFilterName;
569 
570     if ( aFilterName.isEmpty() )
571     {
572         // filter name is not specified, so type detection should be done
573 
574         uno::Reference< document::XTypeDetection > xTypeDetection(
575                 m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", m_xContext),
576                 uno::UNO_QUERY_THROW );
577 
578         // typedetection can change the mode, add a stream and so on, thus a copy should be used
579         uno::Sequence< beans::PropertyValue > aTempMD( aMediaDescr );
580 
581         // get TypeName
582         OUString aTypeName = xTypeDetection->queryTypeByDescriptor( aTempMD, true );
583 
584         // get FilterName
585         for ( const auto & prop : std::as_const(aTempMD) )
586             if ( prop.Name == "FilterName" )
587                 prop.Value >>= aFilterName;
588 
589         if ( !aFilterName.isEmpty() )
590         {
591             sal_Int32 nOldLen = aMediaDescr.getLength();
592             aMediaDescr.realloc( nOldLen + 1 );
593             aMediaDescr[nOldLen].Name = "FilterName";
594             aMediaDescr[ nOldLen ].Value <<= aFilterName;
595 
596         }
597         else if ( !aTypeName.isEmpty() && !bIgnoreType )
598         {
599             uno::Reference< container::XNameAccess > xNameAccess( xTypeDetection, uno::UNO_QUERY );
600             uno::Sequence< beans::PropertyValue > aTypes;
601 
602             if ( xNameAccess.is() && ( xNameAccess->getByName( aTypeName ) >>= aTypes ) )
603             {
604                 for ( const auto & prop : std::as_const(aTypes) )
605                 {
606                     if ( prop.Name == "PreferredFilter" && ( prop.Value >>= aFilterName ) )
607                     {
608                         sal_Int32 nOldLen = aMediaDescr.getLength();
609                         aMediaDescr.realloc( nOldLen + 1 );
610                         aMediaDescr[nOldLen].Name = "FilterName";
611                         aMediaDescr[ nOldLen ].Value = prop.Value;
612                         break;
613                     }
614                 }
615             }
616         }
617     }
618 
619     return aFilterName;
620 }
621 
UpdateMediaDescriptorWithFilterName(uno::Sequence<beans::PropertyValue> & aMediaDescr,uno::Sequence<beans::NamedValue> & aObject)622 OUString MimeConfigurationHelper::UpdateMediaDescriptorWithFilterName(
623                         uno::Sequence< beans::PropertyValue >& aMediaDescr,
624                         uno::Sequence< beans::NamedValue >& aObject )
625 {
626     OUString aDocName;
627     for ( const auto & nv : std::as_const(aObject) )
628         if ( nv.Name == "ObjectDocumentServiceName" )
629         {
630             nv.Value >>= aDocName;
631             break;
632         }
633 
634     OSL_ENSURE( !aDocName.isEmpty(), "The name must exist at this point!" );
635 
636 
637     bool bNeedsAddition = true;
638     for ( sal_Int32 nMedInd = 0; nMedInd < aMediaDescr.getLength(); nMedInd++ )
639         if ( aMediaDescr[nMedInd].Name == "DocumentService" )
640         {
641             aMediaDescr[nMedInd].Value <<= aDocName;
642             bNeedsAddition = false;
643             break;
644         }
645 
646     if ( bNeedsAddition )
647     {
648         sal_Int32 nOldLen = aMediaDescr.getLength();
649         aMediaDescr.realloc( nOldLen + 1 );
650         aMediaDescr[nOldLen].Name = "DocumentService";
651         aMediaDescr[nOldLen].Value <<= aDocName;
652     }
653 
654     return UpdateMediaDescriptorWithFilterName( aMediaDescr, true );
655 }
656 
657 #ifdef _WIN32
658 
GetFilterFlags(const OUString & aFilterName)659 SfxFilterFlags MimeConfigurationHelper::GetFilterFlags( const OUString& aFilterName )
660 {
661     SfxFilterFlags nFlags = SfxFilterFlags::NONE;
662     try
663     {
664         if ( !aFilterName.isEmpty() )
665         {
666             uno::Reference< container::XNameAccess > xFilterFactory(
667                 GetFilterFactory(),
668                 uno::UNO_SET_THROW );
669 
670             uno::Any aFilterAny = xFilterFactory->getByName( aFilterName );
671             uno::Sequence< beans::PropertyValue > aData;
672             if ( aFilterAny >>= aData )
673             {
674                 SequenceAsHashMap aFilterHM( aData );
675                 nFlags = static_cast<SfxFilterFlags>(aFilterHM.getUnpackedValueOrDefault( "Flags", sal_Int32(0) ));
676             }
677         }
678     } catch( uno::Exception& )
679     {}
680 
681     return nFlags;
682 }
683 
AddFilterNameCheckOwnFile(uno::Sequence<beans::PropertyValue> & aMediaDescr)684 bool MimeConfigurationHelper::AddFilterNameCheckOwnFile(
685                         uno::Sequence< beans::PropertyValue >& aMediaDescr )
686 {
687     OUString aFilterName = UpdateMediaDescriptorWithFilterName( aMediaDescr, false );
688     if ( !aFilterName.isEmpty() )
689     {
690         SfxFilterFlags nFlags = GetFilterFlags( aFilterName );
691         // check the OWN flag
692         return bool(nFlags & SfxFilterFlags::OWN);
693     }
694 
695     return false;
696 }
697 
698 #endif
699 
GetDefaultFilterFromServiceName(const OUString & aServiceName,sal_Int32 nVersion)700 OUString MimeConfigurationHelper::GetDefaultFilterFromServiceName( const OUString& aServiceName, sal_Int32 nVersion )
701 {
702     OUString aResult;
703 
704     if ( !aServiceName.isEmpty() && nVersion )
705         try
706         {
707             uno::Reference< container::XContainerQuery > xFilterQuery(
708                 GetFilterFactory(),
709                 uno::UNO_QUERY_THROW );
710 
711             uno::Sequence< beans::NamedValue > aSearchRequest
712             {
713                 { "DocumentService", css::uno::Any(aServiceName) },
714                 { "FileFormatVersion", css::uno::Any(nVersion) }
715             };
716 
717             uno::Reference< container::XEnumeration > xFilterEnum =
718                                             xFilterQuery->createSubSetEnumerationByProperties( aSearchRequest );
719 
720             // use the first filter that is found
721             if ( xFilterEnum.is() )
722                 while ( xFilterEnum->hasMoreElements() )
723                 {
724                     uno::Sequence< beans::PropertyValue > aProps;
725                     if ( xFilterEnum->nextElement() >>= aProps )
726                     {
727                         SequenceAsHashMap aPropsHM( aProps );
728                         SfxFilterFlags nFlags = static_cast<SfxFilterFlags>(aPropsHM.getUnpackedValueOrDefault( "Flags", sal_Int32(0) ));
729 
730                         // that should be import, export, own filter and not a template filter ( TemplatePath flag )
731                         SfxFilterFlags const nRequired = SfxFilterFlags::OWN
732                             // fdo#78159 for OOoXML, there is code to convert
733                             // to ODF in OCommonEmbeddedObject::store*
734                             // so accept it even though there's no export
735                             | (SOFFICE_FILEFORMAT_60 == nVersion ? SfxFilterFlags::NONE : SfxFilterFlags::EXPORT)
736                             | SfxFilterFlags::IMPORT;
737                         if ( ( ( nFlags & nRequired ) == nRequired ) && !( nFlags & SfxFilterFlags::TEMPLATEPATH ) )
738                         {
739                             // if there are more than one filter the preferred one should be used
740                             // if there is no preferred filter the first one will be used
741                             if ( aResult.isEmpty() || ( nFlags & SfxFilterFlags::PREFERED ) )
742                                 aResult = aPropsHM.getUnpackedValueOrDefault( "Name", OUString() );
743                             if ( nFlags & SfxFilterFlags::PREFERED )
744                                 break; // the preferred filter was found
745                         }
746                     }
747                 }
748         }
749         catch( uno::Exception& )
750         {}
751 
752     return aResult;
753 }
754 
755 
GetExportFilterFromImportFilter(const OUString & aImportFilterName)756 OUString MimeConfigurationHelper::GetExportFilterFromImportFilter( const OUString& aImportFilterName )
757 {
758     OUString aExportFilterName;
759 
760     try
761     {
762         if ( !aImportFilterName.isEmpty() )
763         {
764             uno::Reference< container::XNameAccess > xFilterFactory(
765                 GetFilterFactory(),
766                 uno::UNO_SET_THROW );
767 
768             uno::Any aImpFilterAny = xFilterFactory->getByName( aImportFilterName );
769             uno::Sequence< beans::PropertyValue > aImpData;
770             if ( aImpFilterAny >>= aImpData )
771             {
772                 SequenceAsHashMap aImpFilterHM( aImpData );
773                 SfxFilterFlags nFlags = static_cast<SfxFilterFlags>(aImpFilterHM.getUnpackedValueOrDefault( "Flags", sal_Int32(0) ));
774 
775                 if ( !( nFlags & SfxFilterFlags::IMPORT ) )
776                 {
777                     OSL_FAIL( "This is no import filter!" );
778                     throw uno::Exception("this is no import filter", nullptr);
779                 }
780 
781                 if ( nFlags & SfxFilterFlags::EXPORT )
782                 {
783                     aExportFilterName = aImportFilterName;
784                 }
785                 else
786                 {
787                     OUString aDocumentServiceName = aImpFilterHM.getUnpackedValueOrDefault( "DocumentService", OUString() );
788                     OUString aTypeName = aImpFilterHM.getUnpackedValueOrDefault( "Type", OUString() );
789 
790                     OSL_ENSURE( !aDocumentServiceName.isEmpty() && !aTypeName.isEmpty(), "Incomplete filter data!" );
791                     if ( !(aDocumentServiceName.isEmpty() || aTypeName.isEmpty()) )
792                     {
793                         uno::Sequence< beans::NamedValue > aSearchRequest
794                         {
795                             { "Type", css::uno::Any(aTypeName) },
796                             { "DocumentService", css::uno::Any(aDocumentServiceName) }
797                         };
798 
799                         uno::Sequence< beans::PropertyValue > aExportFilterProps = SearchForFilter(
800                             uno::Reference< container::XContainerQuery >( xFilterFactory, uno::UNO_QUERY_THROW ),
801                             aSearchRequest,
802                             SfxFilterFlags::EXPORT,
803                             SfxFilterFlags::INTERNAL );
804 
805                         if ( aExportFilterProps.hasElements() )
806                         {
807                             SequenceAsHashMap aExpPropsHM( aExportFilterProps );
808                             aExportFilterName = aExpPropsHM.getUnpackedValueOrDefault( "Name", OUString() );
809                         }
810                     }
811                 }
812             }
813         }
814     }
815     catch( uno::Exception& )
816     {}
817 
818     return aExportFilterName;
819 }
820 
821 
822 // static
SearchForFilter(const uno::Reference<container::XContainerQuery> & xFilterQuery,const uno::Sequence<beans::NamedValue> & aSearchRequest,SfxFilterFlags nMustFlags,SfxFilterFlags nDontFlags)823 uno::Sequence< beans::PropertyValue > MimeConfigurationHelper::SearchForFilter(
824                                                         const uno::Reference< container::XContainerQuery >& xFilterQuery,
825                                                         const uno::Sequence< beans::NamedValue >& aSearchRequest,
826                                                         SfxFilterFlags nMustFlags,
827                                                         SfxFilterFlags nDontFlags )
828 {
829     uno::Sequence< beans::PropertyValue > aFilterProps;
830     uno::Reference< container::XEnumeration > xFilterEnum =
831                                             xFilterQuery->createSubSetEnumerationByProperties( aSearchRequest );
832 
833     // the first default filter will be taken,
834     // if there is no filter with flag default the first acceptable filter will be taken
835     if ( xFilterEnum.is() )
836     {
837         while ( xFilterEnum->hasMoreElements() )
838         {
839             uno::Sequence< beans::PropertyValue > aProps;
840             if ( xFilterEnum->nextElement() >>= aProps )
841             {
842                 SequenceAsHashMap aPropsHM( aProps );
843                 SfxFilterFlags nFlags = static_cast<SfxFilterFlags>(aPropsHM.getUnpackedValueOrDefault("Flags",
844                                                                         sal_Int32(0) ));
845                 if ( ( ( nFlags & nMustFlags ) == nMustFlags ) && !( nFlags & nDontFlags ) )
846                 {
847                     if ( ( nFlags & SfxFilterFlags::DEFAULT ) == SfxFilterFlags::DEFAULT )
848                     {
849                         aFilterProps = aProps;
850                         break;
851                     }
852                     else if ( !aFilterProps.hasElements() )
853                         aFilterProps = aProps;
854                 }
855             }
856         }
857     }
858 
859     return aFilterProps;
860 }
861 
862 
ClassIDsEqual(const uno::Sequence<sal_Int8> & aClassID1,const uno::Sequence<sal_Int8> & aClassID2)863 bool MimeConfigurationHelper::ClassIDsEqual( const uno::Sequence< sal_Int8 >& aClassID1, const uno::Sequence< sal_Int8 >& aClassID2 )
864 {
865     return aClassID1 == aClassID2;
866 }
867 
868 
GetSequenceClassID(sal_uInt32 n1,sal_uInt16 n2,sal_uInt16 n3,sal_uInt8 b8,sal_uInt8 b9,sal_uInt8 b10,sal_uInt8 b11,sal_uInt8 b12,sal_uInt8 b13,sal_uInt8 b14,sal_uInt8 b15)869 uno::Sequence< sal_Int8 > MimeConfigurationHelper::GetSequenceClassID( sal_uInt32 n1, sal_uInt16 n2, sal_uInt16 n3,
870                                                 sal_uInt8 b8, sal_uInt8 b9, sal_uInt8 b10, sal_uInt8 b11,
871                                                 sal_uInt8 b12, sal_uInt8 b13, sal_uInt8 b14, sal_uInt8 b15 )
872 {
873     uno::Sequence< sal_Int8 > aResult( 16 );
874     aResult[0] = static_cast<sal_Int8>( n1 >> 24 );
875     aResult[1] = static_cast<sal_Int8>( ( n1 << 8 ) >> 24 );
876     aResult[2] = static_cast<sal_Int8>( ( n1 << 16 ) >> 24 );
877     aResult[3] = static_cast<sal_Int8>( ( n1 << 24 ) >> 24 );
878     aResult[4] = static_cast<sal_Int8>( n2 >> 8 );
879     aResult[5] = static_cast<sal_Int8>( ( n2 << 8 ) >> 8 );
880     aResult[6] = static_cast<sal_Int8>( n3 >> 8 );
881     aResult[7] = static_cast<sal_Int8>( ( n3 << 8 ) >> 8 );
882     aResult[8] = b8;
883     aResult[9] = b9;
884     aResult[10] = b10;
885     aResult[11] = b11;
886     aResult[12] = b12;
887     aResult[13] = b13;
888     aResult[14] = b14;
889     aResult[15] = b15;
890 
891     return aResult;
892 }
893 
894 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
895