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_desktop.hxx"
30 
31 #include "../../deployment/gui/dp_gui.hrc"
32 #include "../../deployment/gui/dp_gui_shared.hxx"
33 #include "unopkg_shared.h"
34 #include "osl/thread.h"
35 #include "rtl/memory.h"
36 #include "tools/string.hxx"
37 #include "tools/resmgr.hxx"
38 #include "cppuhelper/implbase3.hxx"
39 #include "cppuhelper/exc_hlp.hxx"
40 #include "comphelper/anytostring.hxx"
41 #include "unotools/configmgr.hxx"
42 #include "com/sun/star/lang/WrappedTargetException.hpp"
43 #include "com/sun/star/task/XInteractionAbort.hpp"
44 #include "com/sun/star/task/XInteractionApprove.hpp"
45 #include "com/sun/star/deployment/InstallException.hpp"
46 #include "com/sun/star/container/ElementExistException.hpp"
47 #include "com/sun/star/deployment/LicenseException.hpp"
48 #include "com/sun/star/deployment/VersionException.hpp"
49 #include "com/sun/star/deployment/PlatformException.hpp"
50 #include "com/sun/star/i18n/XCollator.hpp"
51 #include "com/sun/star/i18n/CollatorOptions.hpp"
52 
53 #include <stdio.h>
54 #include "deployment.hrc"
55 #include "dp_version.hxx"
56 
57 namespace css = ::com::sun::star;
58 using namespace ::com::sun::star;
59 using namespace ::com::sun::star::ucb;
60 using namespace ::com::sun::star::uno;
61 using namespace ::unopkg;
62 using ::rtl::OUString;
63 
64 
65 namespace {
66 
67 //==============================================================================
68 struct OfficeLocale :
69         public rtl::StaticWithInit<const lang::Locale, OfficeLocale> {
70     const lang::Locale operator () () {
71         OUString slang;
72         if (! (::utl::ConfigManager::GetDirectConfigProperty(
73                    ::utl::ConfigManager::LOCALE ) >>= slang))
74             throw RuntimeException( OUSTR("Cannot determine language!"), 0 );
75         return toLocale(slang);
76     }
77 };
78 
79 //==============================================================================
80 class CommandEnvironmentImpl
81     : public ::cppu::WeakImplHelper3< XCommandEnvironment,
82                                       task::XInteractionHandler,
83                                       XProgressHandler >
84 {
85     sal_Int32 m_logLevel;
86     bool m_option_force_overwrite;
87     bool m_option_verbose;
88 	Reference< XComponentContext > m_xComponentContext;
89     Reference< XProgressHandler > m_xLogFile;
90 
91     void update_( Any const & Status ) throw (RuntimeException);
92 	void printLicense(const OUString & sName,const OUString& sLicense,
93                       bool & accept, bool & decline);
94 
95 public:
96     virtual ~CommandEnvironmentImpl();
97     CommandEnvironmentImpl(
98         Reference<XComponentContext> const & xComponentContext,
99         OUString const & log_file,
100         bool option_force_overwrite,
101         bool option_verbose);
102 
103     // XCommandEnvironment
104     virtual Reference< task::XInteractionHandler > SAL_CALL
105     getInteractionHandler() throw (RuntimeException);
106     virtual Reference< XProgressHandler > SAL_CALL getProgressHandler()
107         throw (RuntimeException);
108 
109     // XInteractionHandler
110     virtual void SAL_CALL handle(
111         Reference< task::XInteractionRequest > const & xRequest )
112         throw (RuntimeException);
113 
114     // XProgressHandler
115     virtual void SAL_CALL push( Any const & Status ) throw (RuntimeException);
116     virtual void SAL_CALL update( Any const & Status ) throw (RuntimeException);
117     virtual void SAL_CALL pop() throw (RuntimeException);
118 };
119 
120 
121 //______________________________________________________________________________
122 CommandEnvironmentImpl::CommandEnvironmentImpl(
123     Reference<XComponentContext> const & xComponentContext,
124     OUString const & log_file,
125     bool option_force_overwrite,
126     bool option_verbose)
127     : m_logLevel(0),
128       m_option_force_overwrite( option_force_overwrite ),
129       m_option_verbose( option_verbose ),
130 	  m_xComponentContext(xComponentContext)
131 {
132     if (log_file.getLength() > 0) {
133         const Any logfile(log_file);
134         m_xLogFile.set(
135             xComponentContext->getServiceManager()
136             ->createInstanceWithArgumentsAndContext(
137                 OUSTR("com.sun.star.comp.deployment.ProgressLog"),
138                 Sequence<Any>( &logfile, 1 ), xComponentContext ),
139             UNO_QUERY_THROW );
140     }
141 }
142 
143 //______________________________________________________________________________
144 CommandEnvironmentImpl::~CommandEnvironmentImpl()
145 {
146     try {
147         Reference< lang::XComponent > xComp( m_xLogFile, UNO_QUERY );
148         if (xComp.is())
149             xComp->dispose();
150     }
151     catch (RuntimeException & exc) {
152         (void) exc;
153         OSL_ENSURE( 0, ::rtl::OUStringToOString(
154                         exc.Message, osl_getThreadTextEncoding() ).getStr() );
155     }
156 }
157 
158 //May throw exceptions
159 void CommandEnvironmentImpl::printLicense(
160     const OUString & sName, const OUString& sLicense, bool & accept, bool &decline)
161 {
162     ResMgr * pResMgr = DeploymentResMgr::get();
163     String s1tmp(ResId(RID_STR_UNOPKG_ACCEPT_LIC_1, *pResMgr));
164     s1tmp.SearchAndReplaceAllAscii( "$NAME", sName );
165 	OUString s1(s1tmp);
166 	OUString s2 = String(ResId(RID_STR_UNOPKG_ACCEPT_LIC_2, *pResMgr));
167 	OUString s3 = String(ResId(RID_STR_UNOPKG_ACCEPT_LIC_3, *pResMgr));
168 	OUString s4 = String(ResId(RID_STR_UNOPKG_ACCEPT_LIC_4, *pResMgr));
169 	OUString sYES = String(ResId(RID_STR_UNOPKG_ACCEPT_LIC_YES, *pResMgr));
170 	OUString sY = String(ResId(RID_STR_UNOPKG_ACCEPT_LIC_Y, *pResMgr));
171 	OUString sNO = String(ResId(RID_STR_UNOPKG_ACCEPT_LIC_NO, *pResMgr));
172 	OUString sN = String(ResId(RID_STR_UNOPKG_ACCEPT_LIC_N, *pResMgr));
173 
174     OUString sNewLine(RTL_CONSTASCII_USTRINGPARAM("\n"));
175 
176     dp_misc::writeConsole(sNewLine + sNewLine + s1 + sNewLine + sNewLine);
177     dp_misc::writeConsole(sLicense + sNewLine + sNewLine);
178     dp_misc::writeConsole(s2 + sNewLine);
179     dp_misc::writeConsole(s3);
180 
181 	//the user may enter "yes" or "no", we compare in a case insensitive way
182 	Reference< css::i18n::XCollator > xCollator(
183 		m_xComponentContext->getServiceManager()
184 			->createInstanceWithContext(
185 				OUSTR("com.sun.star.i18n.Collator"),m_xComponentContext),
186 			UNO_QUERY_THROW	);
187 	xCollator->loadDefaultCollator(OfficeLocale::get(),
188 		css::i18n::CollatorOptions::CollatorOptions_IGNORE_CASE);
189 
190 	do
191 	{
192         OUString sAnswer = dp_misc::readConsole();
193 		if (xCollator->compareString(sAnswer, sYES) == 0
194 			|| xCollator->compareString(sAnswer, sY) == 0)
195 		{
196 			accept = true;
197 			break;
198 		}
199 		else if(xCollator->compareString(sAnswer, sNO) == 0
200 			|| xCollator->compareString(sAnswer, sN) == 0)
201 		{
202 			decline = true;
203 			break;
204 		}
205 		else
206 		{
207             dp_misc::writeConsole(sNewLine + sNewLine + s4 + sNewLine);
208 		}
209 	}
210 	while(true);
211 }
212 
213 // XCommandEnvironment
214 //______________________________________________________________________________
215 Reference< task::XInteractionHandler >
216 CommandEnvironmentImpl::getInteractionHandler() throw (RuntimeException)
217 {
218     return this;
219 }
220 
221 //______________________________________________________________________________
222 Reference< XProgressHandler > CommandEnvironmentImpl::getProgressHandler()
223     throw (RuntimeException)
224 {
225     return this;
226 }
227 
228 // XInteractionHandler
229 //______________________________________________________________________________
230 void CommandEnvironmentImpl::handle(
231     Reference<task::XInteractionRequest> const & xRequest )
232     throw (RuntimeException)
233 {
234     Any request( xRequest->getRequest() );
235     OSL_ASSERT( request.getValueTypeClass() == TypeClass_EXCEPTION );
236     dp_misc::TRACE(OUSTR("[unopkg_cmdenv.cxx] incoming request:\n")
237         + ::comphelper::anyToString(request) + OUSTR("\n\n"));
238 
239     // selections:
240     bool approve = false;
241     bool abort = false;
242 
243     lang::WrappedTargetException wtExc;
244 	deployment::LicenseException licExc;
245     deployment::InstallException instExc;
246     deployment::PlatformException platExc;
247     deployment::VersionException verExc;
248 
249 
250     bool bLicenseException = false;
251     if (request >>= wtExc) {
252         // ignore intermediate errors of legacy packages, i.e.
253         // former pkgchk behaviour:
254         const Reference<deployment::XPackage> xPackage(
255             wtExc.Context, UNO_QUERY );
256         OSL_ASSERT( xPackage.is() );
257         if (xPackage.is()) {
258             const Reference<deployment::XPackageTypeInfo> xPackageType(
259                 xPackage->getPackageType() );
260             OSL_ASSERT( xPackageType.is() );
261             if (xPackageType.is()) {
262                 approve = (xPackage->isBundle() &&
263                            xPackageType->getMediaType().matchAsciiL(
264                                RTL_CONSTASCII_STRINGPARAM(
265                                    "application/"
266                                    "vnd.sun.star.legacy-package-bundle") ));
267             }
268         }
269         abort = !approve;
270         if (abort) {
271             // notify cause as error:
272             request = wtExc.TargetException;
273         }
274         else {
275             // handable deployment error signalled, e.g.
276             // bundle item registration failed, notify as warning:
277             update_( wtExc.TargetException );
278         }
279     }
280 	else if (request >>= licExc)
281 	{
282         printLicense(licExc.ExtensionName, licExc.Text, approve, abort);
283 	}
284    	else if (request >>= instExc)
285 	{
286 		//Only if the unopgk was started with gui + extension then we user is asked.
287         //In console mode there is no asking.
288 		approve = true;
289 	}
290     else if (request >>= platExc)
291     {
292         String sMsg(ResId(RID_STR_UNSUPPORTED_PLATFORM, *dp_gui::DeploymentGuiResMgr::get()));
293         sMsg.SearchAndReplaceAllAscii("%Name", platExc.package->getDisplayName());
294         dp_misc::writeConsole(OUSTR("\n") + sMsg + OUSTR("\n\n"));
295         approve = true;
296     }
297     else {
298         deployment::VersionException nc_exc;
299         if (request >>= nc_exc) {
300             approve = m_option_force_overwrite ||
301                 (::dp_misc::compareVersions(nc_exc.NewVersion, nc_exc.Deployed->getVersion())
302                  == ::dp_misc::GREATER);
303             abort = !approve;
304         }
305         else
306             return; // unknown request => no selection at all
307     }
308 
309     //In case of a user declining a license abort is true but this is intended,
310     //therefore no logging
311     if (abort && m_option_verbose && !bLicenseException)
312     {
313         OUString msg = ::comphelper::anyToString(request);
314         dp_misc::writeConsoleError(
315             OUSTR("\nERROR: ") + msg + OUSTR("\n"));
316     }
317 
318     // select:
319     Sequence< Reference<task::XInteractionContinuation> > conts(
320         xRequest->getContinuations() );
321     Reference<task::XInteractionContinuation> const * pConts =
322         conts.getConstArray();
323     sal_Int32 len = conts.getLength();
324     for ( sal_Int32 pos = 0; pos < len; ++pos )
325     {
326         if (approve) {
327             Reference<task::XInteractionApprove> xInteractionApprove(
328                 pConts[ pos ], UNO_QUERY );
329             if (xInteractionApprove.is()) {
330                 xInteractionApprove->select();
331                 break;
332             }
333         }
334         else if (abort) {
335             Reference<task::XInteractionAbort> xInteractionAbort(
336                 pConts[ pos ], UNO_QUERY );
337             if (xInteractionAbort.is()) {
338                 xInteractionAbort->select();
339                 break;
340             }
341         }
342     }
343 }
344 
345 // XProgressHandler
346 //______________________________________________________________________________
347 void CommandEnvironmentImpl::push( Any const & Status )
348     throw (RuntimeException)
349 {
350     update_( Status );
351     OSL_ASSERT( m_logLevel >= 0 );
352     ++m_logLevel;
353     if (m_xLogFile.is())
354         m_xLogFile->push( Status );
355 }
356 
357 //______________________________________________________________________________
358 void CommandEnvironmentImpl::update_( Any const & Status )
359     throw (RuntimeException)
360 {
361     if (! Status.hasValue())
362         return;
363     bool bUseErr = false;
364     OUString msg;
365     if (Status >>= msg) {
366         if (! m_option_verbose)
367             return;
368     }
369     else {
370         ::rtl::OUStringBuffer buf;
371         buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("WARNING: ") );
372         deployment::DeploymentException dp_exc;
373         if (Status >>= dp_exc) {
374             buf.append( dp_exc.Message );
375             buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(", Cause: ") );
376             buf.append( ::comphelper::anyToString(dp_exc.Cause) );
377         }
378         else {
379             buf.append( ::comphelper::anyToString(Status) );
380         }
381         msg = buf.makeStringAndClear();
382         bUseErr = true;
383     }
384     OSL_ASSERT( m_logLevel >= 0 );
385     for ( sal_Int32 n = 0; n < m_logLevel; ++n )
386     {
387         if (bUseErr)
388             dp_misc::writeConsoleError(" ");
389         else
390             dp_misc::writeConsole(" ");
391     }
392 
393     if (bUseErr)
394         dp_misc::writeConsoleError(msg + OUSTR("\n"));
395     else
396         dp_misc::writeConsole(msg + OUSTR("\n"));
397 }
398 
399 //______________________________________________________________________________
400 void CommandEnvironmentImpl::update( Any const & Status )
401     throw (RuntimeException)
402 {
403     update_( Status );
404     if (m_xLogFile.is())
405         m_xLogFile->update( Status );
406 }
407 
408 //______________________________________________________________________________
409 void CommandEnvironmentImpl::pop() throw (RuntimeException)
410 {
411     OSL_ASSERT( m_logLevel > 0 );
412     --m_logLevel;
413     if (m_xLogFile.is())
414         m_xLogFile->pop();
415 }
416 
417 
418 } // anon namespace
419 
420 namespace unopkg {
421 
422 //==============================================================================
423 Reference< XCommandEnvironment > createCmdEnv(
424     Reference< XComponentContext > const & xContext,
425     OUString const & logFile,
426     bool option_force_overwrite,
427     bool option_verbose)
428 {
429     return new CommandEnvironmentImpl(
430         xContext, logFile, option_force_overwrite, option_verbose);
431 }
432 } // unopkg
433 
434