1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_filter.hxx"
30 #include <com/sun/star/io/XActiveDataControl.hpp>
31 #include <com/sun/star/io/XActiveDataSource.hpp>
32 #include <com/sun/star/frame/XConfigManager.hpp>
33 #include <com/sun/star/io/XInputStream.hpp>
34 #include <com/sun/star/io/XActiveDataSink.hpp>
35 #include <com/sun/star/beans/PropertyValue.hpp>
36 #include <com/sun/star/beans/NamedValue.hpp>
37 #include <com/sun/star/container/XNamed.hpp>
38 #include <com/sun/star/container/XChild.hpp>
39 #include <com/sun/star/util/XChangesBatch.hpp>
40 
41 
42 #include <comphelper/oslfile2streamwrap.hxx>
43 #include <comphelper/storagehelper.hxx>
44 #include <unotools/streamwrap.hxx>
45 #include <tools/stream.hxx>
46 #include <tools/urlobj.hxx>
47 #include <unotools/tempfile.hxx>
48 #include <svl/urihelper.hxx>
49 #include <osl/file.hxx>
50 
51 #include <rtl/uri.hxx>
52 
53 #include "xmlfilterjar.hxx"
54 #include "xmlfilterdialogstrings.hrc"
55 #include "xmlfiltersettingsdialog.hxx"
56 #include "typedetectionexport.hxx"
57 #include "typedetectionimport.hxx"
58 
59 using namespace rtl;
60 using namespace osl;
61 using namespace comphelper;
62 using namespace com::sun::star;
63 using namespace com::sun::star::lang;
64 using namespace com::sun::star::frame;
65 using namespace com::sun::star::uno;
66 using namespace com::sun::star::util;
67 using namespace com::sun::star::container;
68 using namespace com::sun::star::beans;
69 using namespace com::sun::star::io;
70 
71 XMLFilterJarHelper::XMLFilterJarHelper( Reference< XMultiServiceFactory >& xMSF )
72 : mxMSF( xMSF ),
73 	sVndSunStarPackage( RTL_CONSTASCII_USTRINGPARAM( "vnd.sun.star.Package:" ) ),
74 	sXSLTPath( RTL_CONSTASCII_USTRINGPARAM( "$(user)/xslt/" ) ),
75 	sDTDPath( RTL_CONSTASCII_USTRINGPARAM( "$(user)/dtd/" ) ),
76 	sTemplatePath( RTL_CONSTASCII_USTRINGPARAM( "$(user)/template/") ),
77 	sSpecialConfigManager( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.config.SpecialConfigManager" ) ),
78 	sPump( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.io.Pump" ) ),
79 	sProgPath( RTL_CONSTASCII_USTRINGPARAM( "$(prog)/" ) )
80 {
81 	try
82 	{
83 		Reference< XConfigManager > xCfgMgr( xMSF->createInstance(OUString::createFromAscii("com.sun.star.config.SpecialConfigManager")), UNO_QUERY );
84 		if( xCfgMgr.is() )
85 		{
86 			sProgPath = xCfgMgr->substituteVariables( sProgPath );
87 			sXSLTPath = xCfgMgr->substituteVariables( sXSLTPath );
88 			sDTDPath = xCfgMgr->substituteVariables( sDTDPath );
89 			sTemplatePath = xCfgMgr->substituteVariables( sTemplatePath );
90 		}
91 	}
92 	catch(Exception&)
93 	{
94 	}
95 }
96 
97 static OUString encodeZipUri( const OUString& rURI )
98 {
99 	return Uri::encode( rURI, rtl_UriCharClassUric, rtl_UriEncodeCheckEscapes, RTL_TEXTENCODING_UTF8 );
100 }
101 
102 static Reference< XInterface > addFolder( Reference< XInterface >& xRootFolder, Reference< XSingleServiceFactory >& xFactory, const OUString& rName ) throw( Exception )
103 {
104     if ( rName.equals( OUString( RTL_CONSTASCII_USTRINGPARAM( ".." ) ) )
105       || rName.equals( OUString( RTL_CONSTASCII_USTRINGPARAM( "." ) ) ) )
106         throw lang::IllegalArgumentException();
107 
108 	Sequence< Any > aArgs(1);
109 	aArgs[0] <<= (sal_Bool)sal_True;
110 
111 	Reference< XInterface > xFolder( xFactory->createInstanceWithArguments(aArgs) );
112 	Reference< XNamed > xNamed( xFolder, UNO_QUERY );
113 	Reference< XChild > xChild( xFolder, UNO_QUERY );
114 
115 	if( xNamed.is() && xChild.is() )
116 	{
117 		OUString aName( encodeZipUri( rName ) );
118 		xNamed->setName( aName );
119 		xChild->setParent( xRootFolder );
120 	}
121 
122 	return xFolder;
123 }
124 
125 static void _addFile( Reference< XInterface >& xRootFolder, Reference< XSingleServiceFactory >& xFactory, Reference< XInputStream >& xInput, OUString aName ) throw( Exception )
126 {
127 
128     Reference< XActiveDataSink > xSink( xFactory->createInstance(), UNO_QUERY );
129     Reference< XUnoTunnel > xTunnel( xSink, UNO_QUERY );
130 	if( xSink.is() && xTunnel.is())
131 	{
132         Reference< XNameContainer > xNameContainer(xRootFolder, UNO_QUERY );
133         xNameContainer->insertByName(aName = encodeZipUri( aName ), makeAny(xTunnel));
134 		xSink->setInputStream( xInput );
135 	}
136 }
137 
138 /*
139 static void addFile( Reference< XInterface > xRootFolder, Reference< XSingleServiceFactory > xFactory, const OUString& rSourceFile, const OUString& rName ) throw( Exception )
140 {
141 	Reference< XInputStream > xInput(  new utl::OSeekableInputStreamWrapper( new SvFileStream(rSourceFile, STREAM_READ ), true ) );
142 	_addFile( xRootFolder, xFactory, xInput, rName );
143 }
144 */
145 
146 void XMLFilterJarHelper::addFile( Reference< XInterface > xRootFolder, Reference< XSingleServiceFactory > xFactory, const OUString& rSourceFile ) throw( Exception )
147 {
148 	if( rSourceFile.getLength() &&
149 		(rSourceFile.compareToAscii( RTL_CONSTASCII_STRINGPARAM("http:") ) != 0) &&
150 		(rSourceFile.compareToAscii( RTL_CONSTASCII_STRINGPARAM("shttp:") ) != 0) &&
151 		(rSourceFile.compareToAscii( RTL_CONSTASCII_STRINGPARAM("jar:") ) != 0) &&
152 		(rSourceFile.compareToAscii( RTL_CONSTASCII_STRINGPARAM("ftp:") ) != 0))
153 	{
154 		OUString aFileURL( rSourceFile );
155 
156 		if( !aFileURL.matchIgnoreAsciiCase( OUString( RTL_CONSTASCII_USTRINGPARAM("file://") ) ) )
157 		{
158 			aFileURL = URIHelper::SmartRel2Abs( sProgPath, aFileURL, Link(), false );
159 		}
160 
161 		INetURLObject aURL( aFileURL );
162 		OUString aName( aURL.getName() );
163 
164 		SvFileStream* pStream = new SvFileStream(aFileURL, STREAM_READ );
165 		Reference< XInputStream > xInput(  new utl::OSeekableInputStreamWrapper( pStream, true ) );
166 		_addFile( xRootFolder, xFactory, xInput, aName );
167 	}
168 }
169 
170 bool XMLFilterJarHelper::savePackage( const OUString& rPackageURL, const XMLFilterVector& rFilters )
171 {
172 	try
173 	{
174 		osl::File::remove( rPackageURL );
175 
176 		// create the package jar file
177 
178 		Sequence< Any > aArguments( 2 );
179 		aArguments[ 0 ] <<= rPackageURL;
180 
181         // let ZipPackage be used ( no manifest.xml is required )
182         beans::NamedValue aArg;
183         aArg.Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StorageFormat" ) );
184         aArg.Value <<= ZIP_STORAGE_FORMAT_STRING;
185         aArguments[ 1 ] <<= aArg;
186 
187 		Reference< XHierarchicalNameAccess > xIfc(
188 			mxMSF->createInstanceWithArguments(
189 				rtl::OUString::createFromAscii(
190 								"com.sun.star.packages.comp.ZipPackage" ),
191 				aArguments ), UNO_QUERY );
192 
193 		if( xIfc.is() )
194 		{
195 			Reference< XSingleServiceFactory > xFactory( xIfc, UNO_QUERY );
196 
197 			// get root zip folder
198 			Reference< XInterface > xRootFolder;
199 			OUString szRootFolder( RTL_CONSTASCII_USTRINGPARAM("/") );
200 			xIfc->getByHierarchicalName( szRootFolder ) >>= xRootFolder;
201 
202 			// export filters files
203 			XMLFilterVector::const_iterator aIter( rFilters.begin() );
204 			while( aIter != rFilters.end() )
205 			{
206 				const filter_info_impl* pFilter = (*aIter);
207 
208 				Reference< XInterface > xFilterRoot( addFolder( xRootFolder, xFactory, pFilter->maFilterName ) );
209 
210 				if( xFilterRoot.is() )
211 				{
212 					if( pFilter->maDTD.getLength() )
213 						addFile( xFilterRoot, xFactory, pFilter->maDTD );
214 
215 					if( pFilter->maExportXSLT.getLength() )
216 						addFile( xFilterRoot, xFactory, pFilter->maExportXSLT );
217 					try
218 					{
219 						if( pFilter->maImportXSLT.getLength() )
220 							addFile( xFilterRoot, xFactory, pFilter->maImportXSLT );
221 					}
222 					catch( com::sun::star::container::ElementExistException&)
223 					{
224 					// in case of same named import / export XSLT the latter
225 					// is ignored
226 						DBG_ERROR( "XMLFilterJarHelper::same named xslt filter exception!" );
227 					}
228 
229 					if( pFilter->maImportTemplate.getLength() )
230 						addFile( xFilterRoot, xFactory, pFilter->maImportTemplate );
231 				}
232 
233 				aIter++;
234 			}
235 
236 			// create TypeDetection.xcu
237 			utl::TempFile aTempFile;
238 			aTempFile.EnableKillingFile();
239 			OUString aTempFileURL( aTempFile.GetURL() );
240 
241 			{
242 				osl::File aOutputFile( aTempFileURL );
243 				/* osl::File::RC rc = */ aOutputFile.open( OpenFlag_Write );
244 				Reference< XOutputStream > xOS( new OSLOutputStreamWrapper( aOutputFile ) );
245 
246 				TypeDetectionExporter aExporter( mxMSF );
247 				aExporter.doExport(xOS,rFilters);
248 			}
249 
250 			Reference< XInputStream > XIS(  new utl::OSeekableInputStreamWrapper( new SvFileStream(aTempFileURL, STREAM_READ ), true ) );
251 			OUString szTypeDetection( RTL_CONSTASCII_USTRINGPARAM( "TypeDetection.xcu" ) );
252 			_addFile( xRootFolder, xFactory,  XIS, szTypeDetection );
253 
254 			Reference< XChangesBatch > xBatch( xIfc, UNO_QUERY );
255 			if( xBatch.is() )
256 				xBatch->commitChanges();
257 
258 			return true;
259 		}
260 	}
261 	catch( Exception& )
262 	{
263 		DBG_ERROR( "XMLFilterJarHelper::savePackage exception catched!" );
264 	}
265 
266 	osl::File::remove( rPackageURL );
267 
268 	return false;
269 }
270 
271 
272 
273 //
274 
275 void XMLFilterJarHelper::openPackage( const OUString& rPackageURL, XMLFilterVector& rFilters )
276 {
277 	try
278 	{
279 		// create the package jar file
280 
281 		Sequence< Any > aArguments( 2 );
282 		aArguments[ 0 ] <<= rPackageURL;
283 
284         // let ZipPackage be used ( no manifest.xml is required )
285         beans::NamedValue aArg;
286         aArg.Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StorageFormat" ) );
287         aArg.Value <<= ZIP_STORAGE_FORMAT_STRING;
288         aArguments[ 1 ] <<= aArg;
289 
290 		Reference< XHierarchicalNameAccess > xIfc(
291 			mxMSF->createInstanceWithArguments(
292 				rtl::OUString::createFromAscii(
293 								"com.sun.star.packages.comp.ZipPackage" ),
294 				aArguments ), UNO_QUERY );
295 
296 		if( xIfc.is() )
297 		{
298 			Reference< XSingleServiceFactory > xFactory( xIfc, UNO_QUERY );
299 
300 			// get root zip folder
301 			Reference< XInterface > xRootFolder;
302 			OUString szRootFolder( RTL_CONSTASCII_USTRINGPARAM("/") );
303 			xIfc->getByHierarchicalName( szRootFolder ) >>= xRootFolder;
304 
305 			OUString szTypeDetection( RTL_CONSTASCII_USTRINGPARAM("TypeDetection.xcu") );
306 			if( xIfc->hasByHierarchicalName( szTypeDetection ) )
307 			{
308 				Reference< XActiveDataSink > xTypeDetection;
309 				xIfc->getByHierarchicalName( szTypeDetection ) >>= xTypeDetection;
310 
311 				if( xTypeDetection.is() )
312 				{
313 					Reference< XInputStream > xIS( xTypeDetection->getInputStream() );
314 
315 					XMLFilterVector aFilters;
316 					TypeDetectionImporter::doImport( mxMSF, xIS, aFilters );
317 
318 					// copy all files used by the filters imported from the
319 					// typedetection to office/user/xslt
320 					XMLFilterVector::iterator aIter( aFilters.begin() );
321 					while( aIter != aFilters.end() )
322 					{
323 						if( copyFiles( xIfc, (*aIter) ) )
324 						{
325 							rFilters.push_back( (*aIter) );
326 						}
327 						else
328 						{
329 							// failed to copy all files
330 							delete (*aIter);
331 						}
332 						aIter++;
333 					}
334 				}
335 			}
336 		}
337 	}
338 	catch( Exception& )
339 	{
340 		DBG_ERROR( "XMLFilterJarHelper::savePackage exception catched!" );
341 	}
342 }
343 
344 bool XMLFilterJarHelper::copyFiles( Reference< XHierarchicalNameAccess > xIfc, filter_info_impl* pFilter )
345 {
346 	bool bOk = copyFile( xIfc, pFilter->maDTD, sDTDPath );
347 
348 	if( bOk )
349 		bOk = copyFile( xIfc, pFilter->maExportXSLT, sXSLTPath );
350 
351 	if( bOk )
352 		bOk = copyFile( xIfc, pFilter->maImportXSLT, sXSLTPath );
353 
354 	if( bOk )
355 		bOk = copyFile( xIfc, pFilter->maImportTemplate, sTemplatePath );
356 
357 	return bOk;
358 }
359 
360 bool XMLFilterJarHelper::copyFile( Reference< XHierarchicalNameAccess > xIfc, OUString& rURL, const OUString& rTargetURL )
361 {
362 	if( !rURL.matchIgnoreAsciiCase( sVndSunStarPackage ) )
363 		return true;
364 
365 	try
366 	{
367 		OUString szPackagePath( encodeZipUri( rURL.copy( sVndSunStarPackage.getLength() ) ) );
368 
369         if ( ::comphelper::OStorageHelper::PathHasSegment( szPackagePath, OUString( RTL_CONSTASCII_USTRINGPARAM( ".." ) ) )
370           || ::comphelper::OStorageHelper::PathHasSegment( szPackagePath, OUString( RTL_CONSTASCII_USTRINGPARAM( "." ) ) ) )
371             throw lang::IllegalArgumentException();
372 
373 		if( xIfc->hasByHierarchicalName( szPackagePath ) )
374 		{
375 			Reference< XActiveDataSink > xFileEntry;
376 			xIfc->getByHierarchicalName( szPackagePath ) >>= xFileEntry;
377 
378 			if( xFileEntry.is() )
379 			{
380 				Reference< XInputStream > xIS( xFileEntry->getInputStream() );
381 
382 				INetURLObject aBaseURL( rTargetURL );
383 
384 				rURL = URIHelper::SmartRel2Abs( aBaseURL, szPackagePath, Link(), false );
385 
386 				if( rURL.getLength() )
387 				{
388 					// create output directory if needed
389 					if( !createDirectory( rURL ) )
390 						return false;
391 
392                     ::osl::File file(rURL);
393                     ::osl::FileBase::RC rc =
394                         file.open(OpenFlag_Write|OpenFlag_Create);
395                     if (::osl::FileBase::E_EXIST == rc) {
396                         rc = file.open(OpenFlag_Write);
397                         if (::osl::FileBase::E_None == rc) {
398                             file.setSize(0); // #i97170# truncate
399                         }
400                     }
401                     if (::osl::FileBase::E_None != rc) {
402                         throw RuntimeException();
403                     }
404                     Reference< XOutputStream > const xOS(
405                             new comphelper::OSLOutputStreamWrapper(file));
406 
407 					return copyStreams( xIS, xOS );
408 				}
409 			}
410 		}
411 	}
412 	catch( Exception& )
413 	{
414 		DBG_ERROR( "XMLFilterJarHelper::copyFile exception catched" );
415 	}
416 	return false;
417 }
418