Line data Source code
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 <config_folders.h>
21 :
22 : #include "deployment.hrc"
23 : #include "unopkg_shared.h"
24 : #include "dp_identifier.hxx"
25 : #include "../../deployment/gui/dp_gui.hrc"
26 : #include "lockfile.hxx"
27 : #include <vcl/svapp.hxx>
28 : #include <vcl/msgbox.hxx>
29 : #include <rtl/bootstrap.hxx>
30 : #include <rtl/strbuf.hxx>
31 : #include <rtl/ustrbuf.hxx>
32 : #include <osl/process.h>
33 : #include <osl/file.hxx>
34 : #include <osl/thread.hxx>
35 : #include <tools/getprocessworkingdir.hxx>
36 : #include <comphelper/processfactory.hxx>
37 : #include <unotools/configmgr.hxx>
38 : #include <com/sun/star/lang/XMultiServiceFactory.hpp>
39 : #include <com/sun/star/ucb/UniversalContentBroker.hpp>
40 : #include <cppuhelper/bootstrap.hxx>
41 : #include <comphelper/sequence.hxx>
42 : #include <stdio.h>
43 :
44 : using namespace ::com::sun::star;
45 : using namespace ::com::sun::star::uno;
46 : using namespace ::com::sun::star::ucb;
47 :
48 : namespace unopkg {
49 :
50 0 : OUString toString( OptionInfo const * info )
51 : {
52 : assert(info != 0);
53 0 : OUStringBuffer buf;
54 0 : buf.appendAscii("--");
55 0 : buf.appendAscii(info->m_name);
56 0 : if (info->m_short_option != '\0')
57 : {
58 0 : buf.appendAscii(" (short -" );
59 0 : buf.append(info->m_short_option );
60 0 : buf.appendAscii(")");
61 : }
62 0 : if (info->m_has_argument)
63 0 : buf.appendAscii(" <argument>" );
64 0 : return buf.makeStringAndClear();
65 : }
66 :
67 :
68 0 : OptionInfo const * getOptionInfo(
69 : OptionInfo const * list,
70 : OUString const & opt, sal_Unicode copt )
71 : {
72 0 : for ( ; list->m_name != 0; ++list )
73 : {
74 0 : OptionInfo const & option_info = *list;
75 0 : if (!opt.isEmpty())
76 : {
77 0 : if (opt.equalsAsciiL(
78 0 : option_info.m_name, option_info.m_name_length ) &&
79 0 : (copt == '\0' || copt == option_info.m_short_option))
80 : {
81 0 : return &option_info;
82 : }
83 : }
84 : else
85 : {
86 : OSL_ASSERT( copt != '\0' );
87 0 : if (copt == option_info.m_short_option)
88 : {
89 0 : return &option_info;
90 : }
91 : }
92 : }
93 : OSL_FAIL( OUStringToOString(
94 : opt, osl_getThreadTextEncoding() ).getStr() );
95 0 : return 0;
96 : }
97 :
98 :
99 0 : bool isOption( OptionInfo const * option_info, sal_uInt32 * pIndex )
100 : {
101 : assert(option_info != 0);
102 0 : if (osl_getCommandArgCount() <= *pIndex)
103 0 : return false;
104 :
105 0 : OUString arg;
106 0 : osl_getCommandArg( *pIndex, &arg.pData );
107 0 : sal_Int32 len = arg.getLength();
108 :
109 0 : if (len < 2 || arg[ 0 ] != '-')
110 0 : return false;
111 :
112 0 : if (len == 2 && arg[ 1 ] == option_info->m_short_option)
113 : {
114 0 : ++(*pIndex);
115 : dp_misc::TRACE(__FILE__ ": identified option \'\'"
116 0 : + OUString( option_info->m_short_option ) + "\n");
117 0 : return true;
118 : }
119 0 : if (arg[ 1 ] == '-' && rtl_ustr_ascii_compare(
120 0 : arg.pData->buffer + 2, option_info->m_name ) == 0)
121 : {
122 0 : ++(*pIndex);
123 : dp_misc::TRACE(__FILE__ ": identified option \'"
124 0 : + OUString::createFromAscii(option_info->m_name) + "\'\n");
125 0 : return true;
126 : }
127 0 : return false;
128 : }
129 :
130 :
131 0 : bool isBootstrapVariable(sal_uInt32 * pIndex)
132 : {
133 : OSL_ASSERT(osl_getCommandArgCount() >= *pIndex);
134 :
135 0 : OUString arg;
136 0 : osl_getCommandArg(*pIndex, &arg.pData);
137 0 : if (arg.match("-env:"))
138 : {
139 0 : ++(*pIndex);
140 0 : return true;
141 : }
142 0 : return false;
143 : }
144 :
145 :
146 0 : bool readArgument(
147 : OUString * pValue, OptionInfo const * option_info, sal_uInt32 * pIndex )
148 : {
149 0 : if (isOption( option_info, pIndex ))
150 : {
151 0 : if (*pIndex < osl_getCommandArgCount())
152 : {
153 : OSL_ASSERT( pValue != 0 );
154 0 : osl_getCommandArg( *pIndex, &pValue->pData );
155 0 : dp_misc::TRACE(OUString( __FILE__) + ": argument value: "
156 0 : + *pValue + "\n");
157 0 : ++(*pIndex);
158 0 : return true;
159 : }
160 0 : --(*pIndex);
161 : }
162 0 : return false;
163 : }
164 :
165 :
166 : namespace {
167 : struct ExecutableDir : public rtl::StaticWithInit<
168 : OUString, ExecutableDir> {
169 0 : const OUString operator () () {
170 0 : OUString path;
171 0 : if (osl_getExecutableFile( &path.pData ) != osl_Process_E_None) {
172 0 : throw RuntimeException("cannot locate executable directory!",0);
173 : }
174 0 : return path.copy( 0, path.lastIndexOf( '/' ) );
175 : }
176 : };
177 : struct ProcessWorkingDir : public rtl::StaticWithInit<
178 : OUString, ProcessWorkingDir> {
179 0 : const OUString operator () () {
180 0 : OUString workingDir;
181 0 : tools::getProcessWorkingDir(workingDir);
182 0 : return workingDir;
183 : }
184 : };
185 : } // anon namespace
186 :
187 :
188 0 : OUString const & getExecutableDir()
189 : {
190 0 : return ExecutableDir::get();
191 : }
192 :
193 :
194 0 : OUString const & getProcessWorkingDir()
195 : {
196 0 : return ProcessWorkingDir::get();
197 : }
198 :
199 :
200 0 : OUString makeAbsoluteFileUrl(
201 : OUString const & sys_path, OUString const & base_url, bool throw_exc )
202 : {
203 : // system path to file url
204 0 : OUString file_url;
205 0 : oslFileError rc = osl_getFileURLFromSystemPath( sys_path.pData, &file_url.pData );
206 0 : if ( rc != osl_File_E_None) {
207 0 : OUString tempPath;
208 0 : if ( osl_getSystemPathFromFileURL( sys_path.pData, &tempPath.pData) == osl_File_E_None )
209 : {
210 0 : file_url = sys_path;
211 : }
212 0 : else if (throw_exc)
213 : {
214 0 : throw RuntimeException("cannot get file url from system path: " +
215 0 : sys_path );
216 0 : }
217 : }
218 :
219 0 : OUString abs;
220 0 : if (osl_getAbsoluteFileURL(
221 0 : base_url.pData, file_url.pData, &abs.pData ) != osl_File_E_None)
222 : {
223 0 : if (throw_exc) {
224 0 : OUStringBuffer buf;
225 0 : buf.appendAscii( "making absolute file url failed: \"" );
226 0 : buf.append( base_url );
227 0 : buf.appendAscii( "\" (base-url) and \"" );
228 0 : buf.append( file_url );
229 0 : buf.appendAscii( "\" (file-url)!" );
230 0 : throw RuntimeException( buf.makeStringAndClear() );
231 : }
232 0 : return OUString();
233 : }
234 0 : return abs[ abs.getLength() -1 ] == '/'
235 0 : ? abs.copy( 0, abs.getLength() -1 ) : abs;
236 : }
237 :
238 :
239 : namespace {
240 :
241 :
242 0 : inline void printf_space( sal_Int32 space )
243 : {
244 0 : while (space--)
245 0 : dp_misc::writeConsole(" ");
246 0 : }
247 :
248 :
249 0 : void printf_line(
250 : OUString const & name, OUString const & value, sal_Int32 level )
251 : {
252 0 : printf_space( level );
253 0 : dp_misc::writeConsole(name + ": " + value + "\n");
254 0 : }
255 :
256 :
257 0 : void printf_package(
258 : Reference<deployment::XPackage> const & xPackage,
259 : Reference<XCommandEnvironment> const & xCmdEnv, sal_Int32 level )
260 : {
261 : beans::Optional< OUString > id(
262 : level == 0
263 : ? beans::Optional< OUString >(
264 : true, dp_misc::getIdentifier( xPackage ) )
265 0 : : xPackage->getIdentifier() );
266 0 : if (id.IsPresent)
267 0 : printf_line( "Identifier", id.Value, level );
268 0 : OUString version(xPackage->getVersion());
269 0 : if (!version.isEmpty())
270 0 : printf_line( "Version", version, level + 1 );
271 0 : printf_line( "URL", xPackage->getURL(), level + 1 );
272 :
273 : beans::Optional< beans::Ambiguous<sal_Bool> > option(
274 0 : xPackage->isRegistered( Reference<task::XAbortChannel>(), xCmdEnv ) );
275 0 : OUString value;
276 0 : if (option.IsPresent) {
277 0 : beans::Ambiguous<sal_Bool> const & reg = option.Value;
278 0 : if (reg.IsAmbiguous)
279 0 : value = "unknown";
280 : else
281 0 : value = reg.Value ? OUString("yes") : OUString("no");
282 : }
283 : else
284 0 : value = "n/a";
285 0 : printf_line( "is registered", value, level + 1 );
286 :
287 : const Reference<deployment::XPackageTypeInfo> xPackageType(
288 0 : xPackage->getPackageType() );
289 : OSL_ASSERT( xPackageType.is() );
290 0 : if (xPackageType.is()) {
291 0 : printf_line( "Media-Type", xPackageType->getMediaType(), level + 1 );
292 : }
293 0 : printf_line( "Description", xPackage->getDescription(), level + 1 );
294 0 : if (xPackage->isBundle()) {
295 : Sequence< Reference<deployment::XPackage> > seq(
296 0 : xPackage->getBundle( Reference<task::XAbortChannel>(), xCmdEnv ) );
297 0 : printf_space( level + 1 );
298 0 : dp_misc::writeConsole("bundled Packages: {\n");
299 0 : ::std::vector<Reference<deployment::XPackage> >vec_bundle;
300 0 : ::comphelper::sequenceToContainer(vec_bundle, seq);
301 : printf_packages( vec_bundle, ::std::vector<bool>(vec_bundle.size()),
302 0 : xCmdEnv, level + 2 );
303 0 : printf_space( level + 1 );
304 0 : dp_misc::writeConsole("}\n");
305 0 : }
306 0 : }
307 :
308 : } // anon namespace
309 :
310 0 : void printf_unaccepted_licenses(
311 : Reference<deployment::XPackage> const & ext)
312 : {
313 : OUString id(
314 0 : dp_misc::getIdentifier(ext) );
315 0 : printf_line( "Identifier", id, 0 );
316 0 : printf_space(1);
317 0 : dp_misc::writeConsole("License not accepted\n\n");
318 0 : }
319 :
320 :
321 0 : void printf_packages(
322 : ::std::vector< Reference<deployment::XPackage> > const & allExtensions,
323 : ::std::vector<bool> const & vecUnaccepted,
324 : Reference<XCommandEnvironment> const & xCmdEnv, sal_Int32 level )
325 : {
326 : OSL_ASSERT(allExtensions.size() == vecUnaccepted.size());
327 :
328 0 : if (allExtensions.empty())
329 : {
330 0 : printf_space( level );
331 0 : dp_misc::writeConsole("<none>\n");
332 : }
333 : else
334 : {
335 : typedef ::std::vector< Reference<deployment::XPackage> >::const_iterator I_EXT;
336 0 : int index = 0;
337 0 : for (I_EXT i = allExtensions.begin(); i != allExtensions.end(); ++i, ++index)
338 : {
339 0 : if (vecUnaccepted[index])
340 0 : printf_unaccepted_licenses(*i);
341 : else
342 0 : printf_package( *i, xCmdEnv, level );
343 0 : dp_misc::writeConsole("\n");
344 : }
345 : }
346 0 : }
347 :
348 :
349 :
350 : namespace {
351 :
352 :
353 0 : Reference<XComponentContext> bootstrapStandAlone()
354 : {
355 : Reference<XComponentContext> xContext =
356 0 : ::cppu::defaultBootstrap_InitialComponentContext();
357 :
358 : Reference<lang::XMultiServiceFactory> xServiceManager(
359 0 : xContext->getServiceManager(), UNO_QUERY_THROW );
360 : // set global process service factory used by unotools config helpers
361 0 : ::comphelper::setProcessServiceFactory( xServiceManager );
362 :
363 : // Initialize the UCB (for backwards compatibility, in case some code still
364 : // uses plain createInstance w/o args directly to obtain an instance):
365 0 : UniversalContentBroker::create( xContext );
366 :
367 0 : return xContext;
368 : }
369 :
370 :
371 0 : Reference<XComponentContext> connectToOffice(
372 : Reference<XComponentContext> const & xLocalComponentContext,
373 : bool verbose )
374 : {
375 0 : Sequence<OUString> args( 3 );
376 0 : args[ 0 ] = "--nologo";
377 0 : args[ 1 ] = "--nodefault";
378 :
379 0 : OUString pipeId( ::dp_misc::generateRandomPipeId() );
380 0 : OUStringBuffer buf;
381 0 : buf.appendAscii( "--accept=pipe,name=" );
382 0 : buf.append( pipeId );
383 0 : buf.appendAscii( ";urp;" );
384 0 : args[ 2 ] = buf.makeStringAndClear();
385 0 : OUString appURL( getExecutableDir() + "/soffice" );
386 :
387 0 : if (verbose)
388 : {
389 : dp_misc::writeConsole(
390 0 : "Raising process: " + appURL +
391 0 : "\nArguments: --nologo --nodefault " + args[2] +
392 0 : "\n");
393 : }
394 :
395 0 : ::dp_misc::raiseProcess( appURL, args );
396 :
397 0 : if (verbose)
398 0 : dp_misc::writeConsole("OK. Connecting...");
399 :
400 : OSL_ASSERT( buf.isEmpty() );
401 0 : buf.appendAscii( "uno:pipe,name=" );
402 0 : buf.append( pipeId );
403 0 : buf.appendAscii( ";urp;StarOffice.ComponentContext" );
404 : Reference<XComponentContext> xRet(
405 : ::dp_misc::resolveUnoURL(
406 : buf.makeStringAndClear(), xLocalComponentContext ),
407 0 : UNO_QUERY_THROW );
408 0 : if (verbose)
409 0 : dp_misc::writeConsole("OK.\n");
410 :
411 0 : return xRet;
412 : }
413 :
414 : } // anon namespace
415 :
416 : /** returns the path to the lock file used by unopkg.
417 : @return the path. An empty string signifies an error.
418 : */
419 0 : OUString getLockFilePath()
420 : {
421 0 : OUString ret;
422 0 : OUString sBootstrap("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}");
423 0 : rtl::Bootstrap::expandMacros(sBootstrap);
424 0 : OUString sAbs;
425 0 : if (::osl::File::E_None == ::osl::File::getAbsoluteFileURL(
426 0 : sBootstrap, ".lock", sAbs))
427 : {
428 0 : if (::osl::File::E_None ==
429 0 : ::osl::File::getSystemPathFromFileURL(sAbs, sBootstrap))
430 : {
431 0 : ret = sBootstrap;
432 : }
433 : }
434 :
435 0 : return ret;
436 : }
437 :
438 0 : Reference<XComponentContext> getUNO(
439 : bool verbose, bool shared, bool bGui,
440 : Reference<XComponentContext> & out_localContext)
441 : {
442 : // do not create any user data (for the root user) in --shared mode:
443 0 : if (shared) {
444 : rtl::Bootstrap::set(
445 : OUString("CFG_CacheUrl"),
446 0 : OUString());
447 : }
448 :
449 : // hold lock during process runtime:
450 0 : static ::desktop::Lockfile s_lockfile( false /* no IPC server */ );
451 0 : Reference<XComponentContext> xComponentContext( bootstrapStandAlone() );
452 0 : out_localContext = xComponentContext;
453 0 : if (::dp_misc::office_is_running()) {
454 : xComponentContext.set(
455 0 : connectToOffice( xComponentContext, verbose ) );
456 : }
457 : else
458 : {
459 0 : if (! s_lockfile.check( 0 ))
460 : {
461 0 : OUString sMsg(ResId(RID_STR_CONCURRENTINSTANCE, *DeploymentResMgr::get()));
462 : //Create this string before we call DeInitVCL, because this will kill
463 : //the ResMgr
464 0 : OUString sError(ResId(RID_STR_UNOPKG_ERROR, *DeploymentResMgr::get()));
465 :
466 0 : sMsg += "\n" + getLockFilePath();
467 :
468 0 : if (bGui)
469 : {
470 : //We show a message box or print to the console that there
471 : //is another instance already running
472 0 : if ( ! InitVCL() )
473 0 : throw RuntimeException( "Cannot initialize VCL!" );
474 : {
475 0 : WarningBox warn(NULL, WB_OK | WB_DEF_OK, sMsg);
476 0 : warn.SetText(utl::ConfigManager::getProductName());
477 0 : warn.SetIcon(0);
478 0 : warn.Execute();
479 : }
480 0 : DeInitVCL();
481 : }
482 :
483 0 : throw LockFileException(sError + sMsg);
484 : }
485 : }
486 :
487 0 : return xComponentContext;
488 : }
489 :
490 0 : }
491 :
492 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|