1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_desktop.hxx"
26
27 #include "oo3extensionmigration.hxx"
28 #include <rtl/instance.hxx>
29 #include <osl/file.hxx>
30 #include <osl/thread.h>
31 #include <tools/urlobj.hxx>
32 #include <unotools/bootstrap.hxx>
33 #include <unotools/ucbstreamhelper.hxx>
34 #include <unotools/textsearch.hxx>
35 #include <comphelper/sequence.hxx>
36 #include <comphelper/processfactory.hxx>
37 #include <ucbhelper/content.hxx>
38
39 #include <com/sun/star/task/XInteractionApprove.hpp>
40 #include <com/sun/star/task/XInteractionAbort.hpp>
41 #include <com/sun/star/ucb/XCommandInfo.hpp>
42 #include <com/sun/star/ucb/TransferInfo.hpp>
43 #include <com/sun/star/ucb/NameClash.hpp>
44 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
45 #include <com/sun/star/xml/xpath/XXPathAPI.hpp>
46 #include <com/sun/star/beans/NamedValue.hpp>
47 #include <com/sun/star/deployment/ExtensionManager.hpp>
48
49 #include <com/sun/star/deployment/VersionException.hpp>
50 #include <dp_gui_handleversionexception.hxx>
51 #include <com/sun/star/deployment/DeploymentException.hpp>
52
53 using namespace ::com::sun::star;
54 using namespace ::com::sun::star::uno;
55
56 namespace migration
57 {
58
59 static ::rtl::OUString sExtensionSubDir = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/user/uno_packages/" ) );
60 static ::rtl::OUString sSubDirName = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "cache" ) );
61 static ::rtl::OUString sConfigDir = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/user/registry/data" ) );
62 static ::rtl::OUString sOrgDir = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/user/registry/data/org" ) );
63 static ::rtl::OUString sExcludeDir1 = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/user/registry/data/org" ) );
64 static ::rtl::OUString sExcludeDir2 = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/user/registry/data/org/openoffice" ) );
65 static ::rtl::OUString sDescriptionXmlFile = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/description.xml" ) );
66 static ::rtl::OUString sExtensionRootSubDirName = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/uno_packages" ) );
67
68 static ::rtl::OUString sConfigurationDataType = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("application/vnd.sun.star.configuration-data"));
69 static ::rtl::OUString sConfigurationSchemaType = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("application/vnd.sun.star.configuration-schema"));
70
71 // =============================================================================
72 // component operations
73 // =============================================================================
74
OO3ExtensionMigration_getImplementationName()75 ::rtl::OUString OO3ExtensionMigration_getImplementationName()
76 {
77 static ::rtl::OUString* pImplName = 0;
78 if ( !pImplName )
79 {
80 ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
81 if ( !pImplName )
82 {
83 static ::rtl::OUString aImplName( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.desktop.migration.OOo3Extensions" ) );
84 pImplName = &aImplName;
85 }
86 }
87 return *pImplName;
88 }
89
90 // -----------------------------------------------------------------------------
91
OO3ExtensionMigration_getSupportedServiceNames()92 Sequence< ::rtl::OUString > OO3ExtensionMigration_getSupportedServiceNames()
93 {
94 static Sequence< ::rtl::OUString >* pNames = 0;
95 if ( !pNames )
96 {
97 ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
98 if ( !pNames )
99 {
100 static Sequence< ::rtl::OUString > aNames(1);
101 aNames.getArray()[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.migration.Extensions" ) );
102 pNames = &aNames;
103 }
104 }
105 return *pNames;
106 }
107
108 // =============================================================================
109 // ExtensionMigration
110 // =============================================================================
111
OO3ExtensionMigration(Reference<XComponentContext> const & ctx)112 OO3ExtensionMigration::OO3ExtensionMigration(Reference< XComponentContext > const & ctx) :
113 m_ctx(ctx)
114 {
115 }
116
117 // -----------------------------------------------------------------------------
118
~OO3ExtensionMigration()119 OO3ExtensionMigration::~OO3ExtensionMigration()
120 {
121 }
122
checkAndCreateDirectory(INetURLObject & rDirURL)123 ::osl::FileBase::RC OO3ExtensionMigration::checkAndCreateDirectory( INetURLObject& rDirURL )
124 {
125 ::osl::FileBase::RC aResult = ::osl::Directory::create( rDirURL.GetMainURL( INetURLObject::DECODE_TO_IURI ) );
126 if ( aResult == ::osl::FileBase::E_NOENT )
127 {
128 INetURLObject aBaseURL( rDirURL );
129 aBaseURL.removeSegment();
130 checkAndCreateDirectory( aBaseURL );
131 return ::osl::Directory::create( rDirURL.GetMainURL( INetURLObject::DECODE_TO_IURI ) );
132 }
133 else
134 {
135 return aResult;
136 }
137 }
138
scanUserExtensions(const::rtl::OUString & sSourceDir,TStringVector & aMigrateExtensions)139 void OO3ExtensionMigration::scanUserExtensions( const ::rtl::OUString& sSourceDir, TStringVector& aMigrateExtensions )
140 {
141 osl::Directory aScanRootDir( sSourceDir );
142 osl::FileStatus fs(FileStatusMask_Type | FileStatusMask_FileURL);
143 osl::FileBase::RC nRetCode = aScanRootDir.open();
144 if ( nRetCode == osl::Directory::E_None )
145 {
146 sal_uInt32 nHint( 0 );
147 osl::DirectoryItem aItem;
148 while ( aScanRootDir.getNextItem( aItem, nHint ) == osl::Directory::E_None )
149 {
150 if (( aItem.getFileStatus(fs) == osl::FileBase::E_None ) &&
151 ( fs.getFileType() == osl::FileStatus::Directory ))
152 {
153 //Check next folder as the "real" extension folder is below a temp folder!
154 ::rtl::OUString sExtensionFolderURL = fs.getFileURL();
155
156 osl::DirectoryItem aExtDirItem;
157 osl::Directory aExtensionRootDir( sExtensionFolderURL );
158
159 nRetCode = aExtensionRootDir.open();
160 if (( nRetCode == osl::Directory::E_None ) &&
161 ( aExtensionRootDir.getNextItem( aExtDirItem, nHint ) == osl::Directory::E_None ))
162 {
163 bool bFileStatus = aExtDirItem.getFileStatus(fs) == osl::FileBase::E_None;
164 bool bIsDir = fs.getFileType() == osl::FileStatus::Directory;
165
166 if ( bFileStatus && bIsDir )
167 {
168 sExtensionFolderURL = fs.getFileURL();
169 ScanResult eResult = scanExtensionFolder( sExtensionFolderURL );
170 if ( eResult == SCANRESULT_MIGRATE_EXTENSION )
171 aMigrateExtensions.push_back( sExtensionFolderURL );
172 }
173 }
174 }
175 }
176 }
177 }
178
scanExtensionFolder(const::rtl::OUString & sExtFolder)179 OO3ExtensionMigration::ScanResult OO3ExtensionMigration::scanExtensionFolder( const ::rtl::OUString& sExtFolder )
180 {
181 ScanResult aResult = SCANRESULT_NOTFOUND;
182 osl::Directory aDir(sExtFolder);
183
184 // get sub dirs
185 if (aDir.open() == osl::FileBase::E_None)
186 {
187 // work through directory contents...
188 osl::DirectoryItem item;
189 osl::FileStatus fs(FileStatusMask_Type | FileStatusMask_FileURL);
190 TStringVector aDirectories;
191 while ((aDir.getNextItem(item) == osl::FileBase::E_None ) &&
192 ( aResult == SCANRESULT_NOTFOUND ))
193 {
194 if (item.getFileStatus(fs) == osl::FileBase::E_None)
195 {
196 ::rtl::OUString aDirEntryURL;
197 if (fs.getFileType() == osl::FileStatus::Directory)
198 aDirectories.push_back( fs.getFileURL() );
199 else
200 {
201 aDirEntryURL = fs.getFileURL();
202 if ( aDirEntryURL.indexOf( sDescriptionXmlFile ) > 0 )
203 aResult = scanDescriptionXml( aDirEntryURL ) ? SCANRESULT_MIGRATE_EXTENSION : SCANRESULT_DONTMIGRATE_EXTENSION;
204 }
205 }
206 }
207
208 TStringVector::const_iterator pIter = aDirectories.begin();
209 while ( pIter != aDirectories.end() && aResult == SCANRESULT_NOTFOUND )
210 {
211 aResult = scanExtensionFolder( *pIter );
212 ++pIter;
213 }
214 }
215 return aResult;
216 }
217
scanDescriptionXml(const::rtl::OUString & sDescriptionXmlURL)218 bool OO3ExtensionMigration::scanDescriptionXml( const ::rtl::OUString& sDescriptionXmlURL )
219 {
220 if ( !m_xDocBuilder.is() )
221 {
222 m_xDocBuilder = uno::Reference< xml::dom::XDocumentBuilder >(
223 m_ctx->getServiceManager()->createInstanceWithContext(
224 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.xml.dom.DocumentBuilder")),
225 m_ctx ), uno::UNO_QUERY );
226 }
227
228 if ( !m_xSimpleFileAccess.is() )
229 {
230 m_xSimpleFileAccess = uno::Reference< ucb::XSimpleFileAccess >(
231 m_ctx->getServiceManager()->createInstanceWithContext(
232 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.ucb.SimpleFileAccess")),
233 m_ctx ), uno::UNO_QUERY );
234 }
235
236 ::rtl::OUString aExtIdentifier;
237 if ( m_xDocBuilder.is() && m_xSimpleFileAccess.is() )
238 {
239 try
240 {
241 uno::Reference< io::XInputStream > xIn =
242 m_xSimpleFileAccess->openFileRead( sDescriptionXmlURL );
243
244 if ( xIn.is() )
245 {
246 uno::Reference< xml::dom::XDocument > xDoc = m_xDocBuilder->parse( xIn );
247 if ( xDoc.is() )
248 {
249 uno::Reference< xml::dom::XElement > xRoot = xDoc->getDocumentElement();
250 if ( xRoot.is() &&
251 xRoot->getTagName().equals(::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("description"))) )
252 {
253 uno::Reference< xml::xpath::XXPathAPI > xPath(
254 m_ctx->getServiceManager()->createInstanceWithContext(
255 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.xml.xpath.XPathAPI")),
256 m_ctx),
257 uno::UNO_QUERY);
258
259 xPath->registerNS(
260 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("desc")),
261 xRoot->getNamespaceURI());
262 xPath->registerNS(
263 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("xlink")),
264 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("http://www.w3.org/1999/xlink")));
265
266 try
267 {
268 uno::Reference< xml::dom::XNode > xRootNode( xRoot, uno::UNO_QUERY );
269 uno::Reference< xml::dom::XNode > xNode(
270 xPath->selectSingleNode(
271 xRootNode,
272 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("desc:identifier/@value")) ));
273 if ( xNode.is() )
274 aExtIdentifier = xNode->getNodeValue();
275 }
276 catch ( xml::xpath::XPathException& )
277 {
278 }
279 catch ( xml::dom::DOMException& )
280 {
281 }
282 }
283 }
284 }
285
286 if ( aExtIdentifier.getLength() > 0 )
287 {
288 // scan extension identifier and try to match with our black list entries
289 for ( sal_uInt32 i = 0; i < m_aBlackList.size(); i++ )
290 {
291 utl::SearchParam param(m_aBlackList[i], utl::SearchParam::SRCH_REGEXP);
292 utl::TextSearch ts(param, LANGUAGE_DONTKNOW);
293
294 xub_StrLen start = 0;
295 xub_StrLen end = static_cast<sal_uInt16>(aExtIdentifier.getLength());
296 if (ts.SearchFrwrd(aExtIdentifier, &start, &end))
297 return false;
298 }
299 }
300 }
301 catch ( ucb::CommandAbortedException& )
302 {
303 }
304 catch ( uno::RuntimeException& )
305 {
306 }
307
308 if ( aExtIdentifier.getLength() == 0 )
309 {
310 // Fallback:
311 // Try to use the folder name to match our black list
312 // as some extensions don't provide an identifier in the
313 // description.xml!
314 for ( sal_uInt32 i = 0; i < m_aBlackList.size(); i++ )
315 {
316 utl::SearchParam param(m_aBlackList[i], utl::SearchParam::SRCH_REGEXP);
317 utl::TextSearch ts(param, LANGUAGE_DONTKNOW);
318
319 xub_StrLen start = 0;
320 xub_StrLen end = static_cast<sal_uInt16>(sDescriptionXmlURL.getLength());
321 if (ts.SearchFrwrd(sDescriptionXmlURL, &start, &end))
322 return false;
323 }
324 }
325 }
326
327 return true;
328 }
329
migrateExtension(const::rtl::OUString & sSourceDir)330 bool OO3ExtensionMigration::migrateExtension( const ::rtl::OUString& sSourceDir )
331 {
332 if ( !m_xExtensionManager.is() )
333 {
334 try
335 {
336 m_xExtensionManager = deployment::ExtensionManager::get( m_ctx );
337 }
338 catch ( ucb::CommandFailedException & ){}
339 catch ( uno::RuntimeException & ) {}
340 }
341
342 if ( m_xExtensionManager.is() )
343 {
344 try
345 {
346 TmpRepositoryCommandEnv* pCmdEnv = new TmpRepositoryCommandEnv();
347
348 uno::Reference< ucb::XCommandEnvironment > xCmdEnv(
349 static_cast< cppu::OWeakObject* >( pCmdEnv ), uno::UNO_QUERY );
350 uno::Reference< task::XAbortChannel > xAbortChannel;
351 uno::Reference< deployment::XPackage > xPackage =
352 m_xExtensionManager->addExtension(
353 sSourceDir, uno::Sequence<beans::NamedValue>(),
354 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("user")), xAbortChannel, xCmdEnv );
355
356 if ( xPackage.is() )
357 return true;
358 }
359 catch ( ucb::CommandFailedException& )
360 {
361 }
362 catch ( ucb::CommandAbortedException& )
363 {
364 }
365 catch ( lang::IllegalArgumentException& )
366 {
367 }
368 catch ( deployment::DeploymentException& )
369 {
370 }
371 catch ( uno::RuntimeException & )
372 {
373 }
374 }
375
376 return false;
377 }
378
379
380 // -----------------------------------------------------------------------------
381 // XServiceInfo
382 // -----------------------------------------------------------------------------
383
getImplementationName()384 ::rtl::OUString OO3ExtensionMigration::getImplementationName() throw (RuntimeException)
385 {
386 return OO3ExtensionMigration_getImplementationName();
387 }
388
389 // -----------------------------------------------------------------------------
390
supportsService(const::rtl::OUString & rServiceName)391 sal_Bool OO3ExtensionMigration::supportsService( const ::rtl::OUString& rServiceName ) throw (RuntimeException)
392 {
393 Sequence< ::rtl::OUString > aNames( getSupportedServiceNames() );
394 const ::rtl::OUString* pNames = aNames.getConstArray();
395 const ::rtl::OUString* pEnd = pNames + aNames.getLength();
396 for ( ; pNames != pEnd && !pNames->equals( rServiceName ); ++pNames )
397 ;
398
399 return pNames != pEnd;
400 }
401
402 // -----------------------------------------------------------------------------
403
getSupportedServiceNames()404 Sequence< ::rtl::OUString > OO3ExtensionMigration::getSupportedServiceNames() throw (RuntimeException)
405 {
406 return OO3ExtensionMigration_getSupportedServiceNames();
407 }
408
409 // -----------------------------------------------------------------------------
410 // XInitialization
411 // -----------------------------------------------------------------------------
412
initialize(const Sequence<Any> & aArguments)413 void OO3ExtensionMigration::initialize( const Sequence< Any >& aArguments ) throw (Exception, RuntimeException)
414 {
415 ::osl::MutexGuard aGuard( m_aMutex );
416
417 const Any* pIter = aArguments.getConstArray();
418 const Any* pEnd = pIter + aArguments.getLength();
419 for ( ; pIter != pEnd ; ++pIter )
420 {
421 beans::NamedValue aValue;
422 *pIter >>= aValue;
423 if ( aValue.Name.equalsAscii( "UserData" ) )
424 {
425 if ( !(aValue.Value >>= m_sSourceDir) )
426 {
427 OSL_ENSURE( false, "ExtensionMigration::initialize: argument UserData has wrong type!" );
428 }
429 }
430 else if ( aValue.Name.equalsAscii( "ExtensionBlackList" ) )
431 {
432 Sequence< ::rtl::OUString > aBlackList;
433 if ( (aValue.Value >>= aBlackList ) && ( aBlackList.getLength() > 0 ))
434 {
435 m_aBlackList.resize( aBlackList.getLength() );
436 ::comphelper::sequenceToArray< ::rtl::OUString >( &m_aBlackList[0], aBlackList );
437 }
438 }
439 }
440 }
441
442 // -----------------------------------------------------------------------------
443
getContent(const::rtl::OUString & rBaseURL)444 TStringVectorPtr getContent( const ::rtl::OUString& rBaseURL )
445 {
446 TStringVectorPtr aResult( new TStringVector );
447 ::osl::Directory aDir( rBaseURL);
448 if ( aDir.open() == ::osl::FileBase::E_None )
449 {
450 // iterate over directory content
451 TStringVector aSubDirs;
452 ::osl::DirectoryItem aItem;
453 while ( aDir.getNextItem( aItem ) == ::osl::FileBase::E_None )
454 {
455 ::osl::FileStatus aFileStatus( FileStatusMask_Type | FileStatusMask_FileURL );
456 if ( aItem.getFileStatus( aFileStatus ) == ::osl::FileBase::E_None )
457 aResult->push_back( aFileStatus.getFileURL() );
458 }
459 }
460
461 return aResult;
462 }
463
execute(const Sequence<beans::NamedValue> &)464 Any OO3ExtensionMigration::execute( const Sequence< beans::NamedValue >& )
465 throw (lang::IllegalArgumentException, Exception, RuntimeException)
466 {
467 ::osl::MutexGuard aGuard( m_aMutex );
468
469 ::utl::Bootstrap::PathStatus aStatus = ::utl::Bootstrap::locateUserInstallation( m_sTargetDir );
470 if ( aStatus == ::utl::Bootstrap::PATH_EXISTS )
471 {
472 // copy all extensions
473 ::rtl::OUString sSourceDir( m_sSourceDir );
474 sSourceDir += sExtensionSubDir;
475 sSourceDir += sSubDirName;
476 sSourceDir += sExtensionRootSubDirName;
477 TStringVector aExtensionToMigrate;
478 scanUserExtensions( sSourceDir, aExtensionToMigrate );
479 if ( aExtensionToMigrate.size() > 0 )
480 {
481 TStringVector::iterator pIter = aExtensionToMigrate.begin();
482 while ( pIter != aExtensionToMigrate.end() )
483 {
484 migrateExtension( *pIter );
485 ++pIter;
486 }
487 }
488 }
489
490 return Any();
491 }
492
493 // -----------------------------------------------------------------------------
494 // TmpRepositoryCommandEnv
495 // -----------------------------------------------------------------------------
496
TmpRepositoryCommandEnv()497 TmpRepositoryCommandEnv::TmpRepositoryCommandEnv()
498 {
499 }
500
~TmpRepositoryCommandEnv()501 TmpRepositoryCommandEnv::~TmpRepositoryCommandEnv()
502 {
503 }
504 // XCommandEnvironment
505 //______________________________________________________________________________
getInteractionHandler()506 uno::Reference< task::XInteractionHandler > TmpRepositoryCommandEnv::getInteractionHandler()
507 throw ( uno::RuntimeException )
508 {
509 return this;
510 }
511
512 //______________________________________________________________________________
getProgressHandler()513 uno::Reference< ucb::XProgressHandler > TmpRepositoryCommandEnv::getProgressHandler()
514 throw ( uno::RuntimeException )
515 {
516 return this;
517 }
518
519 // XInteractionHandler
handle(uno::Reference<task::XInteractionRequest> const & xRequest)520 void TmpRepositoryCommandEnv::handle(
521 uno::Reference< task::XInteractionRequest> const & xRequest )
522 throw ( uno::RuntimeException )
523 {
524 uno::Any request( xRequest->getRequest() );
525 OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
526
527 bool approve = true;
528 bool abort = false;
529
530 deployment::VersionException verExc;
531 if ( xRequest->getRequest() >>= verExc )
532 {
533 // choose newest version, if an extension is already been installed.
534 const bool bChooseNewestVersion = true;
535 approve = handleVersionException( verExc, 0, bChooseNewestVersion );
536 abort = !approve;
537 }
538
539 // select:
540 uno::Sequence< Reference< task::XInteractionContinuation > > conts(
541 xRequest->getContinuations() );
542 Reference< task::XInteractionContinuation > const * pConts =
543 conts.getConstArray();
544 sal_Int32 len = conts.getLength();
545 for ( sal_Int32 pos = 0; pos < len; ++pos )
546 {
547 if (approve) {
548 uno::Reference< task::XInteractionApprove > xInteractionApprove(
549 pConts[ pos ], uno::UNO_QUERY );
550 if (xInteractionApprove.is()) {
551 xInteractionApprove->select();
552 // don't query again for ongoing continuations:
553 approve = false;
554 }
555 }
556 else if (abort) {
557 uno::Reference< task::XInteractionAbort > xInteractionAbort(
558 pConts[ pos ], uno::UNO_QUERY );
559 if (xInteractionAbort.is()) {
560 xInteractionAbort->select();
561 // don't query again for ongoing continuations:
562 abort = false;
563 }
564 }
565 }
566 }
567
568 // XProgressHandler
push(uno::Any const &)569 void TmpRepositoryCommandEnv::push( uno::Any const & /*Status*/ )
570 throw (uno::RuntimeException)
571 {
572 }
573
574
update(uno::Any const &)575 void TmpRepositoryCommandEnv::update( uno::Any const & /*Status */)
576 throw (uno::RuntimeException)
577 {
578 }
579
pop()580 void TmpRepositoryCommandEnv::pop() throw (uno::RuntimeException)
581 {
582 }
583
584 // =============================================================================
585 // component operations
586 // =============================================================================
587
OO3ExtensionMigration_create(Reference<XComponentContext> const & ctx)588 Reference< XInterface > SAL_CALL OO3ExtensionMigration_create(
589 Reference< XComponentContext > const & ctx )
590 SAL_THROW( () )
591 {
592 return static_cast< lang::XTypeProvider * >( new OO3ExtensionMigration(
593 ctx) );
594 }
595
596 // -----------------------------------------------------------------------------
597
598 } // namespace migration
599