1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: unopkg_app.cxx,v $
10 * $Revision: 1.14.58.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
34 #include "unopkg_main.h"
35 #include "unopkg_shared.h"
36 #include "dp_identifier.hxx"
38 #include "tools/extendapplicationenvironment.hxx"
39 #include "rtl/ustrbuf.hxx"
40 #include "rtl/uri.hxx"
41 #include "osl/thread.h"
42 #include "osl/process.h"
43 #include "osl/conditn.hxx"
44 #include "cppuhelper/implbase1.hxx"
45 #include "cppuhelper/exc_hlp.hxx"
46 #include "comphelper/anytostring.hxx"
47 #include "com/sun/star/deployment/thePackageManagerFactory.hpp"
48 #include "com/sun/star/deployment/ui/PackageManagerDialog.hpp"
49 #include "com/sun/star/ui/dialogs/XExecutableDialog.hpp"
50 #include "com/sun/star/lang/DisposedException.hpp"
51 #include "boost/scoped_array.hpp"
52 #include "com/sun/star/ui/dialogs/XDialogClosedListener.hpp"
53 #include "com/sun/star/bridge/XBridgeFactory.hpp"
58 using namespace ::com::sun::star
;
59 using namespace ::com::sun::star::uno
;
60 using namespace ::unopkg
;
61 using ::rtl::OUString
;
62 namespace css
= ::com::sun::star
;
65 //------------------------------------------------------------------------------
66 const char s_usingText
[] =
68 "using: " APP_NAME
" add <options> extension-path...\n"
69 " " APP_NAME
" remove <options> extension-identifier...\n"
70 " " APP_NAME
" list <options> extension-identifier...\n"
71 " " APP_NAME
" reinstall <options>\n"
77 " add add extension\n"
78 " remove remove extensions by identifier\n"
79 " reinstall expert feature: reinstall all deployed extensions\n"
80 " list list information about deployed extensions\n"
81 " gui raise Extension Manager Graphical User Interface (GUI)\n"
84 " -h, --help this help\n"
85 " -V, --version version information\n"
86 " -v, --verbose verbose output to stdout\n"
87 " -f, --force force overwriting existing extensions\n"
89 " -l, --link attempt to link to instead of copying extensions\n"
91 " --log-file <file> custom log file; default: <cache-dir>/log.txt\n"
92 " --shared expert feature: operate on shared installation\n"
93 " deployment context;\n"
94 " run only when no concurrent Office\n"
95 " process(es) are running!\n"
96 " --deployment-context expert feature: explicit deployment context\n"
99 "To learn more about the Extension Manager and extensions, see:\n"
100 "http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/Extensions/Extensions\n\n";
102 //------------------------------------------------------------------------------
103 const OptionInfo s_option_infos
[] = {
104 { RTL_CONSTASCII_STRINGPARAM("help"), 'h', false },
105 { RTL_CONSTASCII_STRINGPARAM("version"), 'V', false },
106 { RTL_CONSTASCII_STRINGPARAM("verbose"), 'v', false },
107 { RTL_CONSTASCII_STRINGPARAM("force"), 'f', false },
109 { RTL_CONSTASCII_STRINGPARAM("link"), 'l', false },
111 { RTL_CONSTASCII_STRINGPARAM("log-file"), '\0', true },
112 { RTL_CONSTASCII_STRINGPARAM("shared"), '\0', false },
113 { RTL_CONSTASCII_STRINGPARAM("deployment-context"), '\0', true },
114 { RTL_CONSTASCII_STRINGPARAM("bundled"), '\0', false},
116 { 0, 0, '\0', false }
119 class DialogClosedListenerImpl
:
120 public ::cppu::WeakImplHelper1
< ui::dialogs::XDialogClosedListener
>
122 osl::Condition
& m_rDialogClosedCondition
;
125 DialogClosedListenerImpl( osl::Condition
& rDialogClosedCondition
)
126 : m_rDialogClosedCondition( rDialogClosedCondition
) {}
128 // XEventListener (base of XDialogClosedListener)
129 virtual void SAL_CALL
disposing( lang::EventObject
const & Source
)
130 throw (RuntimeException
);
132 // XDialogClosedListener
133 virtual void SAL_CALL
dialogClosed(
134 ui::dialogs::DialogClosedEvent
const & aEvent
)
135 throw (RuntimeException
);
138 // XEventListener (base of XDialogClosedListener)
139 void DialogClosedListenerImpl::disposing( lang::EventObject
const & )
140 throw (RuntimeException
)
145 // XDialogClosedListener
146 void DialogClosedListenerImpl::dialogClosed(
147 ui::dialogs::DialogClosedEvent
const & )
148 throw (RuntimeException
)
150 m_rDialogClosedCondition
.set();
153 // If a package had been installed with a pre OOo 2.2, it could not normally be
154 // found via its identifier; similarly (and for ease of use), a package
155 // installed with OOo 2.2 or later could not normally be found via its file
157 Reference
<deployment::XPackage
> findPackage(
158 Reference
<deployment::XPackageManager
> const & manager
,
159 Reference
<ucb::XCommandEnvironment
> const & environment
,
160 OUString
const & idOrFileName
)
162 Sequence
< Reference
<deployment::XPackage
> > ps(
163 manager
->getDeployedPackages(
164 Reference
<task::XAbortChannel
>(), environment
) );
165 for ( sal_Int32 i
= 0; i
< ps
.getLength(); ++i
)
166 if ( dp_misc::getIdentifier( ps
[i
] ) == idOrFileName
)
168 for ( sal_Int32 i
= 0; i
< ps
.getLength(); ++i
)
169 if ( ps
[i
]->getName() == idOrFileName
)
171 return Reference
<deployment::XPackage
>();
177 //workaround for some reason the bridge threads which communicate with the uno.exe
178 //process are not releases on time
179 void disposeBridges(Reference
<css::uno::XComponentContext
> ctx
)
184 Reference
<css::bridge::XBridgeFactory
> bridgeFac(
185 ctx
->getServiceManager()->createInstanceWithContext(
186 OUSTR("com.sun.star.bridge.BridgeFactory"), ctx
),
191 const Sequence
< Reference
<css::bridge::XBridge
> >seqBridges
= bridgeFac
->getExistingBridges();
192 for (sal_Int32 i
= 0; i
< seqBridges
.getLength(); i
++)
194 Reference
<css::lang::XComponent
> comp(seqBridges
[i
], UNO_QUERY
);
200 catch (css::lang::DisposedException
& )
208 //##############################################################################
209 extern "C" int unopkg_main()
211 tools::extendApplicationEnvironment();
212 DisposeGuard disposeGuard
;
213 bool bNoOtherErrorMsg
= false;
215 bool option_shared
= false;
216 bool option_force
= false;
217 bool option_link
= false;
218 bool option_verbose
= false;
219 bool option_bundled
= false;
220 bool subcmd_add
= false;
221 bool subcmd_gui
= false;
223 OUString deploymentContext
;
225 ::std::vector
<OUString
> cmdPackages
;
227 OptionInfo
const * info_shared
= getOptionInfo(
228 s_option_infos
, OUSTR("shared") );
229 OptionInfo
const * info_force
= getOptionInfo(
230 s_option_infos
, OUSTR("force") );
231 OptionInfo
const * info_link
= getOptionInfo(
232 s_option_infos
, OUSTR("link") );
233 OptionInfo
const * info_verbose
= getOptionInfo(
234 s_option_infos
, OUSTR("verbose") );
235 OptionInfo
const * info_log
= getOptionInfo(
236 s_option_infos
, OUSTR("log-file") );
237 OptionInfo
const * info_context
= getOptionInfo(
238 s_option_infos
, OUSTR("deployment-context") );
239 OptionInfo
const * info_help
= getOptionInfo(
240 s_option_infos
, OUSTR("help") );
241 OptionInfo
const * info_version
= getOptionInfo(
242 s_option_infos
, OUSTR("version") );
243 OptionInfo
const * info_bundled
= getOptionInfo(
244 s_option_infos
, OUSTR("bundled") );
246 Reference
<XComponentContext
> xComponentContext
;
247 Reference
<XComponentContext
> xLocalComponentContext
;
251 sal_uInt32 nCount
= osl_getCommandArgCount();
252 if (nCount
== 0 || isOption( info_help
, &nPos
))
254 dp_misc::writeConsole(s_usingText
);
257 else if (isOption( info_version
, &nPos
)) {
258 dp_misc::writeConsole("\n"APP_NAME
" Version 3.0\n");
261 //consume all bootstrap variables which may occur before the subcommannd
262 while(isBootstrapVariable(&nPos
));
266 //get the sub command
267 osl_getCommandArg( nPos
, &subCommand
.pData
);
269 subCommand
= subCommand
.trim();
270 subcmd_add
= subCommand
.equalsAsciiL(
271 RTL_CONSTASCII_STRINGPARAM("add") );
272 subcmd_gui
= subCommand
.equalsAsciiL(
273 RTL_CONSTASCII_STRINGPARAM("gui") );
275 // sun-command options and packages:
276 while (nPos
< nCount
)
278 if (readArgument( &cmdArg
, info_log
, &nPos
)) {
279 logFile
= makeAbsoluteFileUrl(
280 cmdArg
.trim(), getProcessWorkingDir() );
282 else if (!readOption( &option_verbose
, info_verbose
, &nPos
) &&
283 !readOption( &option_shared
, info_shared
, &nPos
) &&
284 !readOption( &option_force
, info_force
, &nPos
) &&
285 !readOption( &option_bundled
, info_bundled
, &nPos
) &&
286 !readOption( &option_link
, info_link
, &nPos
) &&
287 !readArgument( &deploymentContext
, info_context
, &nPos
) &&
288 !isBootstrapVariable(&nPos
))
290 osl_getCommandArg( nPos
, &cmdArg
.pData
);
292 cmdArg
= cmdArg
.trim();
293 if (cmdArg
.getLength() > 0)
295 if (cmdArg
[ 0 ] == '-')
298 dp_misc::writeConsoleError(
299 OUSTR("\nERROR: unexpected option ") +
302 OUSTR(" Use " APP_NAME
" ") +
303 toString(info_help
) +
304 OUSTR(" to print all options.\n"));
310 cmdPackages
.push_back(
311 subcmd_add
|| subcmd_gui
312 ? makeAbsoluteFileUrl(
313 cmdArg
, getProcessWorkingDir() )
320 //make sure the bundled option was provided together with shared
321 if (option_bundled
&& !option_shared
)
323 dp_misc::writeConsoleError(
324 "\nERROR: option --bundled can only be used together with --shared!");
329 xComponentContext
= getUNO(
330 disposeGuard
, option_verbose
, option_shared
, subcmd_gui
,
331 xLocalComponentContext
);
333 if (deploymentContext
.getLength() == 0) {
334 deploymentContext
= option_shared
? OUSTR("shared") : OUSTR("user");
338 if (deploymentContext
.equalsAsciiL(
339 RTL_CONSTASCII_STRINGPARAM("shared") )) {
340 option_shared
= true;
342 else if (option_shared
) {
343 dp_misc::writeConsoleError(
344 OUSTR("WARNING: explicit context given! ") +
345 OUSTR("Ignoring option ") +
346 toString( info_shared
) +
351 Reference
<deployment::XPackageManagerFactory
> xPackageManagerFactory(
352 deployment::thePackageManagerFactory::get( xComponentContext
) );
353 Reference
<deployment::XPackageManager
> xPackageManager(
354 xPackageManagerFactory
->getPackageManager( deploymentContext
) );
356 Reference
< ::com::sun::star::ucb::XCommandEnvironment
> xCmdEnv(
357 createCmdEnv( xComponentContext
, logFile
,
358 option_force
, option_link
, option_verbose
, option_bundled
) );
361 subCommand
.equalsAsciiL(
362 RTL_CONSTASCII_STRINGPARAM("remove") ))
364 for ( ::std::size_t pos
= 0; pos
< cmdPackages
.size(); ++pos
)
366 OUString
const & cmdPackage
= cmdPackages
[ pos
];
369 Reference
<deployment::XPackage
> xPackage(
370 xPackageManager
->addPackage(
371 cmdPackage
, OUString() /* to be detected */,
372 Reference
<task::XAbortChannel
>(), xCmdEnv
) );
373 OSL_ASSERT( xPackage
.is() );
379 xPackageManager
->removePackage(
380 cmdPackage
, cmdPackage
,
381 Reference
<task::XAbortChannel
>(), xCmdEnv
);
383 catch (lang::IllegalArgumentException
&)
385 Reference
<deployment::XPackage
> p(
387 xPackageManager
, xCmdEnv
, cmdPackage
) );
388 //Todo. temporary preventing exception in bundled case.
389 //In case of a bundled extension, remove would be called as a result of
390 //uninstalling a rpm. Then we do not want to show an error when the
391 //extension does not exist, because the package will be uninstalled anyway
392 //and the error would only confuse people.
393 if ( !p
.is() && !option_bundled
)
396 xPackageManager
->removePackage(
397 ::dp_misc::getIdentifier(p
), p
->getName(),
398 Reference
<task::XAbortChannel
>(), xCmdEnv
);
403 else if (subCommand
.equalsAsciiL(
404 RTL_CONSTASCII_STRINGPARAM("reinstall") ))
406 xPackageManager
->reinstallDeployedPackages(
407 Reference
<task::XAbortChannel
>(), xCmdEnv
);
409 else if (subCommand
.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("list") ))
411 Sequence
< Reference
<deployment::XPackage
> > packages
;
412 if (cmdPackages
.empty())
414 packages
= xPackageManager
->getDeployedPackages(
415 Reference
<task::XAbortChannel
>(), xCmdEnv
);
416 dp_misc::writeConsole(
417 OUSTR("all deployed ") + deploymentContext
+ OUSTR(" packages:\n"));
421 packages
.realloc( cmdPackages
.size() );
422 for ( ::std::size_t pos
= 0; pos
< cmdPackages
.size(); ++pos
)
425 packages
[ pos
] = xPackageManager
->getDeployedPackage(
426 cmdPackages
[ pos
], cmdPackages
[ pos
], xCmdEnv
);
428 catch (lang::IllegalArgumentException
&)
430 packages
[ pos
] = findPackage(
431 xPackageManager
, xCmdEnv
, cmdPackages
[ pos
] );
432 if ( !packages
[ pos
].is() )
436 printf_packages( packages
, xCmdEnv
);
438 else if (subCommand
.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("gui") ))
440 Reference
<ui::dialogs::XAsynchronousExecutableDialog
> xDialog(
441 deployment::ui::PackageManagerDialog::createAndInstall(
443 cmdPackages
.size() > 0 ? cmdPackages
[0] : OUString() ));
445 osl::Condition dialogEnded
;
448 Reference
< ui::dialogs::XDialogClosedListener
> xListener(
449 new DialogClosedListenerImpl( dialogEnded
) );
451 xDialog
->startExecuteModal(xListener
);
456 dp_misc::writeConsoleError(
457 OUSTR("\nERROR: unknown sub-command ") +
460 OUSTR(" Use " APP_NAME
" ") +
461 toString(info_help
) +
462 OUSTR(" to print all options.\n"));
467 dp_misc::writeConsole(OUSTR("\n"APP_NAME
" done.\n"));
468 //Force to release all bridges which connect us to the child processes
469 disposeBridges(xLocalComponentContext
);
472 catch (ucb::CommandFailedException
&e
)
474 dp_misc::writeConsoleError(e
.Message
+ OUSTR("\n"));
475 bNoOtherErrorMsg
= true;
477 catch (ucb::CommandAbortedException
&)
479 dp_misc::writeConsoleError("\n"APP_NAME
" aborted!\n");
481 catch (deployment::DeploymentException
& exc
)
486 cause
= ::comphelper::anyToString(exc
.Cause
);
490 css::uno::Exception e
;
495 dp_misc::writeConsoleError(
496 OUSTR("\nERROR: ") + exc
.Message
+ OUSTR("\n"));
497 if (cause
.getLength())
498 dp_misc::writeConsoleError(
499 OUSTR(" Cause: ") + cause
+ OUSTR("\n"));
501 catch (LockFileException
& e
)
504 dp_misc::writeConsoleError(e
.Message
);
505 bNoOtherErrorMsg
= true;
507 catch (::com::sun::star::uno::Exception
& e
) {
508 Any
exc( ::cppu::getCaughtException() );
510 dp_misc::writeConsoleError(
512 OUString(option_verbose
? e
.Message
+ OUSTR("\nException details: \n") +
513 ::comphelper::anyToString(exc
) : e
.Message
) +
516 if (!bNoOtherErrorMsg
)
517 dp_misc::writeConsoleError("\n"APP_NAME
" failed.\n");
518 disposeBridges(xLocalComponentContext
);