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 <osl/diagnose.h>
23 : #include <osl/thread.h>
24 : #include <osl/process.h>
25 : #include <osl/file.hxx>
26 : #include <rtl/ustrbuf.hxx>
27 :
28 : #include <rtl/uri.hxx>
29 : #include "shellexec.hxx"
30 : #include <com/sun/star/system/SystemShellExecuteFlags.hpp>
31 :
32 : #include <com/sun/star/util/theMacroExpander.hpp>
33 : #include <com/sun/star/uri/XExternalUriReferenceTranslator.hpp>
34 : #include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp>
35 : #include <com/sun/star/uri/UriReferenceFactory.hpp>
36 : #include <cppuhelper/supportsservice.hxx>
37 :
38 : #include "uno/current_context.hxx"
39 :
40 : #include <string.h>
41 : #include <errno.h>
42 : #include <unistd.h>
43 :
44 :
45 : // namespace directives
46 :
47 :
48 : using com::sun::star::system::XSystemShellExecute;
49 : using com::sun::star::system::SystemShellExecuteException;
50 :
51 : using osl::FileBase;
52 :
53 : using namespace ::com::sun::star::uno;
54 : using namespace ::com::sun::star::lang;
55 : using namespace ::com::sun::star::system::SystemShellExecuteFlags;
56 : using namespace cppu;
57 :
58 : #define SHELLEXEC_IMPL_NAME "com.sun.star.comp.system.SystemShellExecute2"
59 :
60 :
61 : // helper functions
62 :
63 :
64 : namespace // private
65 : {
66 0 : Sequence< OUString > SAL_CALL ShellExec_getSupportedServiceNames()
67 : {
68 0 : Sequence< OUString > aRet(1);
69 0 : aRet[0] = "com.sun.star.sys.shell.SystemShellExecute";
70 0 : return aRet;
71 : }
72 : }
73 :
74 0 : void escapeForShell( OStringBuffer & rBuffer, const OString & rURL)
75 : {
76 0 : sal_Int32 nmax = rURL.getLength();
77 0 : for(sal_Int32 n=0; n < nmax; ++n)
78 : {
79 : // escape every non alpha numeric characters (excluding a few "known good") by prepending a '\'
80 0 : sal_Char c = rURL[n];
81 0 : if( ( c < 'A' || c > 'Z' ) && ( c < 'a' || c > 'z' ) && ( c < '0' || c > '9' ) && c != '/' && c != '.' )
82 0 : rBuffer.append( '\\' );
83 :
84 0 : rBuffer.append( c );
85 : }
86 0 : }
87 :
88 :
89 :
90 2 : ShellExec::ShellExec( const Reference< XComponentContext >& xContext ) :
91 : WeakImplHelper2< XSystemShellExecute, XServiceInfo >(),
92 2 : m_xContext(xContext)
93 : {
94 : try {
95 2 : Reference< XCurrentContext > xCurrentContext(getCurrentContext());
96 :
97 2 : if (xCurrentContext.is())
98 : {
99 2 : Any aValue = xCurrentContext->getValueByName(
100 2 : OUString( "system.desktop-environment" ) );
101 :
102 4 : OUString aDesktopEnvironment;
103 2 : if (aValue >>= aDesktopEnvironment)
104 : {
105 2 : m_aDesktopEnvironment = OUStringToOString(aDesktopEnvironment, RTL_TEXTENCODING_ASCII_US);
106 2 : }
107 2 : }
108 0 : } catch (const RuntimeException &) {
109 : }
110 2 : }
111 :
112 :
113 :
114 0 : void SAL_CALL ShellExec::execute( const OUString& aCommand, const OUString& aParameter, sal_Int32 nFlags )
115 : throw (IllegalArgumentException, SystemShellExecuteException, RuntimeException, std::exception)
116 : {
117 0 : OStringBuffer aBuffer, aLaunchBuffer;
118 :
119 : // DESKTOP_LAUNCH, see http://freedesktop.org/pipermail/xdg/2004-August/004489.html
120 0 : static const char *pDesktopLaunch = getenv( "DESKTOP_LAUNCH" );
121 :
122 : // Check whether aCommand contains an absolute URI reference:
123 : css::uno::Reference< css::uri::XUriReference > uri(
124 0 : css::uri::UriReferenceFactory::create(m_xContext)->parse(aCommand));
125 0 : if (uri.is() && uri->isAbsolute())
126 : {
127 : // It seems to be a url ..
128 : // We need to re-encode file urls because osl_getFileURLFromSystemPath converts
129 : // to UTF-8 before encoding non ascii characters, which is not what other apps
130 : // expect.
131 : OUString aURL(
132 : com::sun::star::uri::ExternalUriReferenceTranslator::create(
133 0 : m_xContext)->translateToExternal(aCommand));
134 0 : if ( aURL.isEmpty() && !aCommand.isEmpty() )
135 : {
136 : throw RuntimeException(
137 : OUString("Cannot translate URI reference to external format: ")
138 0 : + aCommand,
139 0 : static_cast< cppu::OWeakObject * >(this));
140 : }
141 :
142 : #ifdef MACOSX
143 : //TODO: Using open(1) with an argument that syntactically is an absolute
144 : // URI reference does not necessarily give expected results:
145 : // 1 If the given URI reference matches a supported scheme (e.g.,
146 : // "mailto:foo"):
147 : // 1.1 If it matches an existing pathname (relative to CWD): Results
148 : // in "mailto:foo?\n[0]\tcancel\n[1]\tOpen the file\tmailto:foo\n[2]\t
149 : // Open the URL\tmailto:foo\n\nWhich did you mean? Cancelled." on
150 : // stderr and SystemShellExecuteException.
151 : // 1.2 If it does not match an exitsting pathname (relative to CWD):
152 : // Results in the corresponding application being opened with the given
153 : // document (e.g., Mail with a New Message).
154 : // 2 If the given URI reference does not match a supported scheme
155 : // (e.g., "foo:bar"):
156 : // 2.1 If it matches an existing pathname (relative to CWD) pointing to
157 : // an executable: Results in execution of that executable.
158 : // 2.2 If it matches an existing pathname (relative to CWD) pointing to
159 : // a non-executable regular file: Results in opening it in TextEdit.
160 : // 2.3 If it matches an existing pathname (relative to CWD) pointing to
161 : // a directory: Results in opening it in Finder.
162 : // 2.4 If it does not match an exitsting pathname (relative to CWD):
163 : // Results in "The file /.../foo:bar does not exits." (where "/..." is
164 : // the CWD) on stderr and SystemShellExecuteException.
165 : aBuffer.append("open --");
166 : #else
167 : // The url launchers are expected to be in the $BRAND_BASE_DIR/LIBO_LIBEXEC_FOLDER
168 : // directory:
169 : com::sun::star::uno::Reference< com::sun::star::util::XMacroExpander >
170 0 : exp = com::sun::star::util::theMacroExpander::get(m_xContext);
171 0 : OUString aProgramURL;
172 : try {
173 0 : aProgramURL = exp->expandMacros(
174 0 : OUString( "$BRAND_BASE_DIR/" LIBO_LIBEXEC_FOLDER "/"));
175 0 : } catch (com::sun::star::lang::IllegalArgumentException &)
176 : {
177 : throw SystemShellExecuteException(
178 : "Could not expand $BRAND_BASE_DIR path",
179 0 : static_cast < XSystemShellExecute * > (this), ENOENT );
180 : }
181 :
182 0 : OUString aProgram;
183 0 : if ( FileBase::E_None != FileBase::getSystemPathFromFileURL(aProgramURL, aProgram))
184 : {
185 : throw SystemShellExecuteException(
186 : "Cound not convert executable path",
187 0 : static_cast < XSystemShellExecute * > (this), ENOENT );
188 : }
189 :
190 0 : OString aTmp = OUStringToOString(aProgram, osl_getThreadTextEncoding());
191 0 : escapeForShell(aBuffer, aTmp);
192 :
193 : #ifdef SOLARIS
194 : if ( m_aDesktopEnvironment.getLength() == 0 )
195 : m_aDesktopEnvironment = OString("GNOME");
196 : #endif
197 :
198 : // Respect the desktop environment - if there is an executable named
199 : // <desktop-environement-is>-open-url, pass the url to this one instead
200 : // of the default "open-url" script.
201 0 : if ( !m_aDesktopEnvironment.isEmpty() )
202 : {
203 0 : OString aDesktopEnvironment(m_aDesktopEnvironment.toAsciiLowerCase());
204 0 : OStringBuffer aCopy(aTmp);
205 :
206 0 : aCopy.append(aDesktopEnvironment + "-open-url");
207 :
208 0 : if ( 0 == access( aCopy.getStr(), X_OK) )
209 : {
210 0 : aBuffer.append(aDesktopEnvironment + "-");
211 0 : }
212 : }
213 :
214 0 : aBuffer.append("open-url");
215 : #endif
216 0 : aBuffer.append(" ");
217 0 : escapeForShell(aBuffer, OUStringToOString(aURL, osl_getThreadTextEncoding()));
218 :
219 0 : if ( pDesktopLaunch && *pDesktopLaunch )
220 : {
221 0 : aLaunchBuffer.append( OString(pDesktopLaunch) + " ");
222 0 : escapeForShell(aLaunchBuffer, OUStringToOString(aURL, osl_getThreadTextEncoding()));
223 0 : }
224 0 : } else if ((nFlags & css::system::SystemShellExecuteFlags::URIS_ONLY) != 0)
225 : {
226 : throw css::lang::IllegalArgumentException(
227 : OUString("XSystemShellExecute.execute URIS_ONLY with non-absolute"
228 : " URI reference ")
229 0 : + aCommand,
230 0 : static_cast< cppu::OWeakObject * >(this), 0);
231 : } else {
232 0 : escapeForShell(aBuffer, OUStringToOString(aCommand, osl_getThreadTextEncoding()));
233 0 : aBuffer.append(" ");
234 0 : if( nFlags != 42 )
235 0 : escapeForShell(aBuffer, OUStringToOString(aParameter, osl_getThreadTextEncoding()));
236 : else
237 0 : aBuffer.append(OUStringToOString(aParameter, osl_getThreadTextEncoding()));
238 : }
239 :
240 : // Prefer DESKTOP_LAUNCH when available
241 0 : if ( !aLaunchBuffer.isEmpty() )
242 : {
243 0 : FILE *pLaunch = popen( aLaunchBuffer.makeStringAndClear().getStr(), "w" );
244 0 : if ( pLaunch != NULL )
245 : {
246 0 : if ( 0 == pclose( pLaunch ) )
247 0 : return;
248 : }
249 : // Failed, do not try DESKTOP_LAUNCH any more
250 0 : pDesktopLaunch = NULL;
251 : }
252 :
253 : OString cmd =
254 : #ifdef LINUX
255 : // avoid blocking (call it in background)
256 0 : "( " + aBuffer.makeStringAndClear() + " ) &";
257 : #else
258 : aBuffer.makeStringAndClear();
259 : #endif
260 0 : FILE *pLaunch = popen(cmd.getStr(), "w");
261 0 : if ( pLaunch != NULL )
262 : {
263 0 : if ( 0 == pclose( pLaunch ) )
264 0 : return;
265 : }
266 :
267 0 : int nerr = errno;
268 0 : throw SystemShellExecuteException(OUString::createFromAscii( strerror( nerr ) ),
269 0 : static_cast < XSystemShellExecute * > (this), nerr );
270 : }
271 :
272 : // XServiceInfo
273 0 : OUString SAL_CALL ShellExec::getImplementationName( )
274 : throw( RuntimeException, std::exception )
275 : {
276 0 : return OUString(SHELLEXEC_IMPL_NAME );
277 : }
278 :
279 : // XServiceInfo
280 0 : sal_Bool SAL_CALL ShellExec::supportsService( const OUString& ServiceName )
281 : throw( RuntimeException, std::exception )
282 : {
283 0 : return cppu::supportsService(this, ServiceName);
284 : }
285 :
286 : // XServiceInfo
287 0 : Sequence< OUString > SAL_CALL ShellExec::getSupportedServiceNames( )
288 : throw( RuntimeException, std::exception )
289 : {
290 0 : return ShellExec_getSupportedServiceNames();
291 : }
292 :
293 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|