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