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