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::__anonff2c77720111::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::__anonff2c77720111::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