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