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.hrc"
28 #include "unopkg_shared.h"
29 #include "dp_identifier.hxx"
30 #include "../../deployment/gui/dp_gui.hrc"
31 #include "../../app/lockfile.hxx"
32 #include "vcl/svapp.hxx"
33 #include "vcl/msgbox.hxx"
34 #include "rtl/bootstrap.hxx"
35 #include "rtl/strbuf.hxx"
36 #include "rtl/ustrbuf.hxx"
37 #include "osl/process.h"
38 #include "osl/file.hxx"
39 #include "osl/thread.hxx"
40 #include "tools/getprocessworkingdir.hxx"
41 #include "ucbhelper/contentbroker.hxx"
42 #include "ucbhelper/configurationkeys.hxx"
43 #include "unotools/processfactory.hxx"
44 #include "unotools/configmgr.hxx"
45 #include "com/sun/star/lang/XMultiServiceFactory.hpp"
46 #include "cppuhelper/bootstrap.hxx"
47 #include "comphelper/sequence.hxx"
48 #include <stdio.h>
49
50 using ::rtl::OUString;
51 using ::rtl::OString;
52 using namespace ::com::sun::star;
53 using namespace ::com::sun::star::uno;
54 using namespace ::com::sun::star::ucb;
55
56 namespace unopkg {
57
58 bool getLockFilePath(OUString & out);
59
toString(OptionInfo const * info)60 ::rtl::OUString toString( OptionInfo const * info )
61 {
62 OSL_ASSERT( info != 0 );
63 ::rtl::OUStringBuffer buf;
64 buf.appendAscii("--");
65 buf.appendAscii(info->m_name);
66 if (info->m_short_option != '\0')
67 {
68 buf.appendAscii(" (short -" );
69 buf.append(info->m_short_option );
70 buf.appendAscii(")");
71 }
72 if (info->m_has_argument)
73 buf.appendAscii(" <argument>" );
74 return buf.makeStringAndClear();
75 }
76
77 //==============================================================================
getOptionInfo(OptionInfo const * list,OUString const & opt,sal_Unicode copt)78 OptionInfo const * getOptionInfo(
79 OptionInfo const * list,
80 OUString const & opt, sal_Unicode copt )
81 {
82 for ( ; list->m_name != 0; ++list )
83 {
84 OptionInfo const & option_info = *list;
85 if (opt.getLength() > 0)
86 {
87 if (opt.equalsAsciiL(
88 option_info.m_name, option_info.m_name_length ) &&
89 (copt == '\0' || copt == option_info.m_short_option))
90 {
91 return &option_info;
92 }
93 }
94 else
95 {
96 OSL_ASSERT( copt != '\0' );
97 if (copt == option_info.m_short_option)
98 {
99 return &option_info;
100 }
101 }
102 }
103 OSL_ENSURE( 0, ::rtl::OUStringToOString(
104 opt, osl_getThreadTextEncoding() ).getStr() );
105 return 0;
106 }
107
108 //==============================================================================
isOption(OptionInfo const * option_info,sal_uInt32 * pIndex)109 bool isOption( OptionInfo const * option_info, sal_uInt32 * pIndex )
110 {
111 OSL_ASSERT( option_info != 0 );
112 if (osl_getCommandArgCount() <= *pIndex)
113 return false;
114
115 OUString arg;
116 osl_getCommandArg( *pIndex, &arg.pData );
117 sal_Int32 len = arg.getLength();
118
119 if (len < 2 || arg[ 0 ] != '-')
120 return false;
121
122 if (len == 2 && arg[ 1 ] == option_info->m_short_option)
123 {
124 ++(*pIndex);
125 dp_misc::TRACE(OUSTR(__FILE__": identified option \'")
126 + OUSTR("\'") + OUString( option_info->m_short_option ) + OUSTR("\n"));
127 return true;
128 }
129 if (arg[ 1 ] == '-' && rtl_ustr_ascii_compare(
130 arg.pData->buffer + 2, option_info->m_name ) == 0)
131 {
132 ++(*pIndex);
133 dp_misc::TRACE(OUSTR( __FILE__": identified option \'")
134 + OUString::createFromAscii(option_info->m_name) + OUSTR("\'\n"));
135 return true;
136 }
137 return false;
138 }
139 //==============================================================================
140
isBootstrapVariable(sal_uInt32 * pIndex)141 bool isBootstrapVariable(sal_uInt32 * pIndex)
142 {
143 OSL_ASSERT(osl_getCommandArgCount() >= *pIndex);
144
145 OUString arg;
146 osl_getCommandArg(*pIndex, &arg.pData);
147 if (arg.matchAsciiL("-env:", 5))
148 {
149 ++(*pIndex);
150 return true;
151 }
152 return false;
153 }
154
155 //==============================================================================
readArgument(OUString * pValue,OptionInfo const * option_info,sal_uInt32 * pIndex)156 bool readArgument(
157 OUString * pValue, OptionInfo const * option_info, sal_uInt32 * pIndex )
158 {
159 if (isOption( option_info, pIndex ))
160 {
161 if (*pIndex < osl_getCommandArgCount())
162 {
163 OSL_ASSERT( pValue != 0 );
164 osl_getCommandArg( *pIndex, &pValue->pData );
165 dp_misc::TRACE(OUSTR( __FILE__": argument value: ")
166 + *pValue + OUSTR("\n"));
167 ++(*pIndex);
168 return true;
169 }
170 --(*pIndex);
171 }
172 return false;
173 }
174
175 //##############################################################################
176
177 namespace {
178 struct ExecutableDir : public rtl::StaticWithInit<
179 const OUString, ExecutableDir> {
operator ()unopkg::__anon5ff1f5c90111::ExecutableDir180 const OUString operator () () {
181 OUString path;
182 if (osl_getExecutableFile( &path.pData ) != osl_Process_E_None) {
183 throw RuntimeException(
184 OUSTR("cannot locate executable directory!"),0 );
185 }
186 return path.copy( 0, path.lastIndexOf( '/' ) );
187 }
188 };
189 struct ProcessWorkingDir : public rtl::StaticWithInit<
190 const OUString, ProcessWorkingDir> {
operator ()unopkg::__anon5ff1f5c90111::ProcessWorkingDir191 const OUString operator () () {
192 OUString workingDir;
193 tools::getProcessWorkingDir(&workingDir);
194 return workingDir;
195 }
196 };
197 } // anon namespace
198
199 //==============================================================================
getExecutableDir()200 OUString const & getExecutableDir()
201 {
202 return ExecutableDir::get();
203 }
204
205 //==============================================================================
getProcessWorkingDir()206 OUString const & getProcessWorkingDir()
207 {
208 return ProcessWorkingDir::get();
209 }
210
211 //==============================================================================
makeAbsoluteFileUrl(OUString const & sys_path,OUString const & base_url,bool throw_exc)212 OUString makeAbsoluteFileUrl(
213 OUString const & sys_path, OUString const & base_url, bool throw_exc )
214 {
215 // system path to file url
216 OUString file_url;
217 oslFileError rc = osl_getFileURLFromSystemPath( sys_path.pData, &file_url.pData );
218 if ( rc != osl_File_E_None) {
219 OUString tempPath;
220 if ( osl_getSystemPathFromFileURL( sys_path.pData, &tempPath.pData) == osl_File_E_None )
221 {
222 file_url = sys_path;
223 }
224 else if (throw_exc)
225 {
226 throw RuntimeException(
227 OUSTR("cannot get file url from system path: ") +
228 sys_path, Reference< XInterface >() );
229 }
230 }
231
232 OUString abs;
233 if (osl_getAbsoluteFileURL(
234 base_url.pData, file_url.pData, &abs.pData ) != osl_File_E_None)
235 {
236 if (throw_exc) {
237 ::rtl::OUStringBuffer buf;
238 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(
239 "making absolute file url failed: \"") );
240 buf.append( base_url );
241 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(
242 "\" (base-url) and \"") );
243 buf.append( file_url );
244 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("\" (file-url)!") );
245 throw RuntimeException(
246 buf.makeStringAndClear(), Reference< XInterface >() );
247 }
248 return OUString();
249 }
250 return abs[ abs.getLength() -1 ] == '/'
251 ? abs.copy( 0, abs.getLength() -1 ) : abs;
252 }
253
254 //##############################################################################
255
256 namespace {
257
258 //------------------------------------------------------------------------------
printf_space(sal_Int32 space)259 inline void printf_space( sal_Int32 space )
260 {
261 while (space--)
262 dp_misc::writeConsole(" ");
263 }
264
265 //------------------------------------------------------------------------------
printf_line(OUString const & name,OUString const & value,sal_Int32 level)266 void printf_line(
267 OUString const & name, OUString const & value, sal_Int32 level )
268 {
269 printf_space( level );
270 dp_misc::writeConsole(name + OUSTR(": ") + value + OUSTR("\n"));
271 }
272
273 //------------------------------------------------------------------------------
printf_package(Reference<deployment::XPackage> const & xPackage,Reference<XCommandEnvironment> const & xCmdEnv,sal_Int32 level)274 void printf_package(
275 Reference<deployment::XPackage> const & xPackage,
276 Reference<XCommandEnvironment> const & xCmdEnv, sal_Int32 level )
277 {
278 beans::Optional< OUString > id(
279 level == 0
280 ? beans::Optional< OUString >(
281 true, dp_misc::getIdentifier( xPackage ) )
282 : xPackage->getIdentifier() );
283 if (id.IsPresent)
284 printf_line( OUSTR("Identifier"), id.Value, level );
285 OUString version(xPackage->getVersion());
286 if (version.getLength() != 0)
287 printf_line( OUSTR("Version"), version, level + 1 );
288 printf_line( OUSTR("URL"), xPackage->getURL(), level + 1 );
289
290 beans::Optional< beans::Ambiguous<sal_Bool> > option(
291 xPackage->isRegistered( Reference<task::XAbortChannel>(), xCmdEnv ) );
292 OUString value;
293 if (option.IsPresent) {
294 beans::Ambiguous<sal_Bool> const & reg = option.Value;
295 if (reg.IsAmbiguous)
296 value = OUSTR("unknown");
297 else
298 value = reg.Value ? OUSTR("yes") : OUSTR("no");
299 }
300 else
301 value = OUSTR("n/a");
302 printf_line( OUSTR("is registered"), value, level + 1 );
303
304 const Reference<deployment::XPackageTypeInfo> xPackageType(
305 xPackage->getPackageType() );
306 OSL_ASSERT( xPackageType.is() );
307 if (xPackageType.is()) {
308 printf_line( OUSTR("Media-Type"),
309 xPackageType->getMediaType(), level + 1 );
310 }
311 printf_line( OUSTR("Description"), xPackage->getDescription(), level + 1 );
312 if (xPackage->isBundle()) {
313 Sequence< Reference<deployment::XPackage> > seq(
314 xPackage->getBundle( Reference<task::XAbortChannel>(), xCmdEnv ) );
315 printf_space( level + 1 );
316 dp_misc::writeConsole("bundled Packages: {\n");
317 ::std::vector<Reference<deployment::XPackage> >vec_bundle;
318 ::comphelper::sequenceToContainer(vec_bundle, seq);
319 printf_packages( vec_bundle, ::std::vector<bool>(vec_bundle.size()),
320 xCmdEnv, level + 2 );
321 printf_space( level + 1 );
322 dp_misc::writeConsole("}\n");
323 }
324 }
325
326 } // anon namespace
327
printf_unaccepted_licenses(Reference<deployment::XPackage> const & ext)328 void printf_unaccepted_licenses(
329 Reference<deployment::XPackage> const & ext)
330 {
331 OUString id(
332 dp_misc::getIdentifier(ext) );
333 printf_line( OUSTR("Identifier"), id, 0 );
334 printf_space(1);
335 dp_misc::writeConsole(OUSTR("License not accepted\n\n"));
336 }
337
338 //==============================================================================
printf_packages(::std::vector<Reference<deployment::XPackage>> const & allExtensions,::std::vector<bool> const & vecUnaccepted,Reference<XCommandEnvironment> const & xCmdEnv,sal_Int32 level)339 void printf_packages(
340 ::std::vector< Reference<deployment::XPackage> > const & allExtensions,
341 ::std::vector<bool> const & vecUnaccepted,
342 Reference<XCommandEnvironment> const & xCmdEnv, sal_Int32 level )
343 {
344 OSL_ASSERT(allExtensions.size() == vecUnaccepted.size());
345
346 if (allExtensions.size() == 0)
347 {
348 printf_space( level );
349 dp_misc::writeConsole("<none>\n");
350 }
351 else
352 {
353 typedef ::std::vector< Reference<deployment::XPackage> >::const_iterator I_EXT;
354 int index = 0;
355 for (I_EXT i = allExtensions.begin(); i != allExtensions.end(); i++, index++)
356 {
357 if (vecUnaccepted[index])
358 printf_unaccepted_licenses(*i);
359 else
360 printf_package( *i, xCmdEnv, level );
361 dp_misc::writeConsole(OUSTR("\n"));
362 }
363 }
364 }
365
366
367 //##############################################################################
368
369 namespace {
370
371 //------------------------------------------------------------------------------
bootstrapStandAlone(DisposeGuard & disposeGuard,bool)372 Reference<XComponentContext> bootstrapStandAlone(
373 DisposeGuard & disposeGuard, bool /*verbose */)
374 {
375 Reference<XComponentContext> xContext =
376 ::cppu::defaultBootstrap_InitialComponentContext();
377
378 // assure disposing of local component context:
379 disposeGuard.reset(
380 Reference<lang::XComponent>( xContext, UNO_QUERY ) );
381
382 Reference<lang::XMultiServiceFactory> xServiceManager(
383 xContext->getServiceManager(), UNO_QUERY_THROW );
384 // set global process service factory used by unotools config helpers
385 ::utl::setProcessServiceFactory( xServiceManager );
386
387 // initialize the ucbhelper ucb,
388 // because the package implementation uses it
389 Sequence<Any> ucb_args( 2 );
390 ucb_args[ 0 ] <<= OUSTR(UCB_CONFIGURATION_KEY1_LOCAL);
391 ucb_args[ 1 ] <<= OUSTR(UCB_CONFIGURATION_KEY2_OFFICE);
392 if (! ::ucbhelper::ContentBroker::initialize( xServiceManager, ucb_args ))
393 throw RuntimeException( OUSTR("cannot initialize UCB!"), 0 );
394
395 disposeGuard.setDeinitUCB();
396 return xContext;
397 }
398
399 //------------------------------------------------------------------------------
connectToOffice(Reference<XComponentContext> const & xLocalComponentContext,bool verbose)400 Reference<XComponentContext> connectToOffice(
401 Reference<XComponentContext> const & xLocalComponentContext,
402 bool verbose )
403 {
404 Sequence<OUString> args( 3 );
405 args[ 0 ] = OUSTR("-nologo");
406 args[ 1 ] = OUSTR("-nodefault");
407
408 OUString pipeId( ::dp_misc::generateRandomPipeId() );
409 ::rtl::OUStringBuffer buf;
410 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("-accept=pipe,name=") );
411 buf.append( pipeId );
412 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(";urp;") );
413 args[ 2 ] = buf.makeStringAndClear();
414 OUString appURL( getExecutableDir() + OUSTR("/soffice") );
415
416 if (verbose)
417 {
418 dp_misc::writeConsole(
419 OUSTR("Raising process: ") +
420 appURL +
421 OUSTR("\nArguments: -nologo -nodefault ") +
422 args[2] +
423 OUSTR("\n"));
424 }
425
426 ::dp_misc::raiseProcess( appURL, args );
427
428 if (verbose)
429 dp_misc::writeConsole("Ok. Connecting...");
430
431 OSL_ASSERT( buf.getLength() == 0 );
432 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("uno:pipe,name=") );
433 buf.append( pipeId );
434 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(
435 ";urp;StarOffice.ComponentContext") );
436 Reference<XComponentContext> xRet(
437 ::dp_misc::resolveUnoURL(
438 buf.makeStringAndClear(), xLocalComponentContext ),
439 UNO_QUERY_THROW );
440 if (verbose)
441 dp_misc::writeConsole("Ok.\n");
442
443 return xRet;
444 }
445
446 } // anon namespace
447
448 /** returns the path to the lock file used by unopkg.
449 @return the path. An empty string signifies an error.
450 */
getLockFilePath()451 OUString getLockFilePath()
452 {
453 OUString ret;
454 OUString sBootstrap(RTL_CONSTASCII_USTRINGPARAM("${$OOO_BASE_DIR/program/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}"));
455 rtl::Bootstrap::expandMacros(sBootstrap);
456 OUString sAbs;
457 if (::osl::File::E_None == ::osl::File::getAbsoluteFileURL(
458 sBootstrap, OUSTR(".lock"), sAbs))
459 {
460 if (::osl::File::E_None ==
461 ::osl::File::getSystemPathFromFileURL(sAbs, sBootstrap))
462 {
463 ret = sBootstrap;
464 }
465 }
466
467 return ret;
468 }
469 //==============================================================================
getUNO(DisposeGuard & disposeGuard,bool verbose,bool shared,bool bGui,Reference<XComponentContext> & out_localContext)470 Reference<XComponentContext> getUNO(
471 DisposeGuard & disposeGuard, bool verbose, bool shared, bool bGui,
472 Reference<XComponentContext> & out_localContext)
473 {
474 // do not create any user data (for the root user) in --shared mode:
475 if (shared) {
476 rtl::Bootstrap::set(
477 rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CFG_CacheUrl")),
478 rtl::OUString());
479 }
480
481 // hold lock during process runtime:
482 static ::desktop::Lockfile s_lockfile( false /* no IPC server */ );
483 Reference<XComponentContext> xComponentContext(
484 bootstrapStandAlone( disposeGuard, verbose ) );
485 out_localContext = xComponentContext;
486 if (::dp_misc::office_is_running()) {
487 xComponentContext.set(
488 connectToOffice( xComponentContext, verbose ) );
489 }
490 else
491 {
492 if (! s_lockfile.check( 0 ))
493 {
494 String sMsg(ResId(RID_STR_CONCURRENTINSTANCE, *DeploymentResMgr::get()));
495 //Create this string before we call DeInitVCL, because this will kill
496 //the ResMgr
497 String sError(ResId(RID_STR_UNOPKG_ERROR, *DeploymentResMgr::get()));
498
499 sMsg = sMsg + OUSTR("\n") + getLockFilePath();
500
501 if (bGui)
502 {
503 //We show a message box or print to the console that there
504 //is another instance already running
505 if ( ! InitVCL( Reference<lang::XMultiServiceFactory>(
506 xComponentContext->getServiceManager(),
507 UNO_QUERY_THROW ) ))
508 throw RuntimeException( OUSTR("Cannot initialize VCL!"),
509 NULL );
510 {
511 WarningBox warn(NULL, WB_OK | WB_DEF_OK, sMsg);
512 warn.SetText(::utl::ConfigManager::GetDirectConfigProperty(
513 ::utl::ConfigManager::PRODUCTNAME).get<OUString>());
514 warn.SetIcon(0);
515 warn.Execute();
516 }
517 DeInitVCL();
518 }
519
520 throw LockFileException(
521 OUSTR("\n") + sError + sMsg + OUSTR("\n"));
522 }
523 }
524
525 return xComponentContext;
526 }
527
528 //Determines if a folder does not contains a folder.
529 //Return false may also mean that the status could not be determined
530 //because some error occurred.
hasNoFolder(OUString const & folderUrl)531 bool hasNoFolder(OUString const & folderUrl)
532 {
533 bool ret = false;
534 OUString url = folderUrl;
535 ::rtl::Bootstrap::expandMacros(url);
536 ::osl::Directory dir(url);
537 osl::File::RC rc = dir.open();
538 if (rc == osl::File::E_None)
539 {
540 bool bFolderExist = false;
541 osl::DirectoryItem i;
542 osl::File::RC rcNext = osl::File::E_None;
543 while ( (rcNext = dir.getNextItem(i)) == osl::File::E_None)
544 {
545 osl::FileStatus stat(FileStatusMask_Type);
546 if (i.getFileStatus(stat) == osl::File::E_None)
547 {
548 if (stat.getFileType() == osl::FileStatus::Directory)
549 {
550 bFolderExist = true;
551 break;
552 }
553 }
554 else
555 {
556 dp_misc::writeConsole(
557 OUSTR("unopkg: Error while investigating ") + url + OUSTR("\n"));
558 break;
559 }
560 i = osl::DirectoryItem();
561 }
562
563 if (rcNext == osl::File::E_NOENT ||
564 rcNext == osl::File::E_None)
565 {
566 if (!bFolderExist)
567 ret = true;
568 }
569 else
570 {
571 dp_misc::writeConsole(
572 OUSTR("unopkg: Error while investigating ") + url + OUSTR("\n"));
573 }
574
575 dir.close();
576 }
577 else
578 {
579 dp_misc::writeConsole(
580 OUSTR("unopkg: Error while investigating ") + url + OUSTR("\n"));
581 }
582 return ret;
583 }
584
removeFolder(OUString const & folderUrl)585 void removeFolder(OUString const & folderUrl)
586 {
587 OUString url = folderUrl;
588 ::rtl::Bootstrap::expandMacros(url);
589 ::osl::Directory dir(url);
590 ::osl::File::RC rc = dir.open();
591 if (rc == osl::File::E_None)
592 {
593 ::osl::DirectoryItem i;
594 ::osl::File::RC rcNext = ::osl::File::E_None;
595 while ( (rcNext = dir.getNextItem(i)) == ::osl::File::E_None)
596 {
597 ::osl::FileStatus stat(FileStatusMask_Type | FileStatusMask_FileURL);
598 if (i.getFileStatus(stat) == ::osl::File::E_None)
599 {
600 ::osl::FileStatus::Type t = stat.getFileType();
601 if (t == ::osl::FileStatus::Directory)
602 {
603 //remove folder
604 removeFolder(stat.getFileURL());
605 }
606 else if (t == ::osl::FileStatus::Regular)
607 {
608 //remove file
609 ::osl::File::remove(stat.getFileURL());
610 }
611 else
612 {
613 OSL_ASSERT(0);
614 }
615 }
616 else
617 {
618 dp_misc::writeConsole(
619 OUSTR("unopkg: Error while investigating ") + url + OUSTR("\n"));
620 break;
621 }
622 i = ::osl::DirectoryItem();
623 }
624 dir.close();
625 ::osl::Directory::remove(url);
626 }
627 else if (rc != osl::File::E_NOENT)
628 {
629 dp_misc::writeConsole(
630 OUSTR("unopkg: Error while removing ") + url + OUSTR("\n"));
631 }
632 }
633
634 }
635