tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / shell / source / unix / exec / shellexec.cxx
blob5d421f2609fc450bc78c1e71138106f4e511f5d7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <osl/thread.h>
21 #include <osl/file.hxx>
22 #include <rtl/strbuf.hxx>
23 #include <sal/log.hxx>
25 #include "shellexec.hxx"
26 #include <com/sun/star/system/SystemShellExecuteException.hpp>
27 #include <com/sun/star/system/SystemShellExecuteFlags.hpp>
29 #include <com/sun/star/lang/IllegalArgumentException.hpp>
30 #include <com/sun/star/security/AccessControlException.hpp>
31 #include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp>
32 #include <com/sun/star/uri/UriReferenceFactory.hpp>
33 #include <cppuhelper/supportsservice.hxx>
34 #include <comphelper/lok.hxx>
36 #include <string.h>
37 #include <errno.h>
39 #if defined MACOSX
40 #include <sys/stat.h>
41 #endif
43 #ifdef EMSCRIPTEN
44 #include <rtl/uri.hxx>
45 extern void execute_browser(const char* sUrl);
46 #endif
48 using com::sun::star::system::XSystemShellExecute;
49 using com::sun::star::system::SystemShellExecuteException;
51 using namespace ::com::sun::star::uno;
52 using namespace ::com::sun::star::lang;
53 using namespace ::com::sun::star::system::SystemShellExecuteFlags;
54 using namespace cppu;
56 #ifndef EMSCRIPTEN
57 namespace
59 void escapeForShell( OStringBuffer & rBuffer, const OString & rURL)
61 sal_Int32 nmax = rURL.getLength();
62 for(sal_Int32 n=0; n < nmax; ++n)
64 // escape every non alpha numeric characters (excluding a few "known good") by prepending a '\'
65 char c = rURL[n];
66 if( ( c < 'A' || c > 'Z' ) && ( c < 'a' || c > 'z' ) && ( c < '0' || c > '9' ) && c != '/' && c != '.' )
67 rBuffer.append( '\\' );
69 rBuffer.append( c );
73 #endif
75 ShellExec::ShellExec( const Reference< XComponentContext >& xContext ) :
76 m_xContext(xContext)
80 void SAL_CALL ShellExec::execute( const OUString& aCommand, const OUString& aParameter, sal_Int32 nFlags )
82 #ifndef EMSCRIPTEN
83 OStringBuffer aBuffer, aLaunchBuffer;
85 if (comphelper::LibreOfficeKit::isActive())
87 SAL_WARN("shell", "Unusual - shell attempt to launch " << aCommand << " with params " << aParameter << " under lok");
88 return;
91 // DESKTOP_LAUNCH, see http://freedesktop.org/pipermail/xdg/2004-August/004489.html
92 static const char *pDesktopLaunch = getenv( "DESKTOP_LAUNCH" );
94 // Check whether aCommand contains an absolute URI reference:
95 css::uno::Reference< css::uri::XUriReference > uri(
96 css::uri::UriReferenceFactory::create(m_xContext)->parse(aCommand));
97 if (uri.is() && uri->isAbsolute())
99 // It seems to be a URL...
100 // We need to re-encode file urls because osl_getFileURLFromSystemPath converts
101 // to UTF-8 before encoding non ascii characters, which is not what other apps
102 // expect.
103 OUString aURL = css::uri::ExternalUriReferenceTranslator::create(
104 m_xContext)->translateToExternal(aCommand);
105 if ( aURL.isEmpty() && !aCommand.isEmpty() )
107 throw RuntimeException(
108 "Cannot translate URI reference to external format: "
109 + aCommand,
110 getXWeak());
113 #ifdef MACOSX
114 bool dir = false;
115 if (uri->getScheme().equalsIgnoreAsciiCase("file")) {
116 OUString pathname;
117 auto const e1 = osl::FileBase::getSystemPathFromFileURL(aCommand, pathname);
118 if (e1 != osl::FileBase::E_None) {
119 throw css::lang::IllegalArgumentException(
120 ("XSystemShellExecute.execute, getSystemPathFromFileURL <" + aCommand
121 + "> failed with " + OUString::number(e1)),
122 {}, 0);
124 OString pathname8;
125 if (!pathname.convertToString(
126 &pathname8, RTL_TEXTENCODING_UTF8,
127 (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
128 | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
130 throw css::lang::IllegalArgumentException(
131 "XSystemShellExecute.execute, cannot convert \"" + pathname + "\" to UTF-8", {},
134 struct stat st;
135 auto const e2 = lstat(pathname8.getStr(), &st);
136 if (e2 != 0) {
137 auto const e3 = errno;
138 SAL_INFO("shell", "lstat(" << pathname8 << ") failed with errno " << e3);
140 if (e2 != 0) {
141 throw css::lang::IllegalArgumentException(
142 "XSystemShellExecute.execute, cannot process <" + aCommand + ">", {}, 0);
143 } else if (S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode)) {
144 dir = true;
145 } else if ((nFlags & css::system::SystemShellExecuteFlags::URIS_ONLY) != 0
146 && (!S_ISREG(st.st_mode)
147 || (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0))
149 throw css::security::AccessControlException(
150 "XSystemShellExecute.execute, bad <" + aCommand + ">", {}, {});
151 } else if (pathname.endsWithIgnoreAsciiCase(".class")
152 || pathname.endsWithIgnoreAsciiCase(".dmg")
153 || pathname.endsWithIgnoreAsciiCase(".fileloc")
154 || pathname.endsWithIgnoreAsciiCase(".inetloc")
155 || pathname.endsWithIgnoreAsciiCase(".ipa")
156 || pathname.endsWithIgnoreAsciiCase(".jar")
157 || pathname.endsWithIgnoreAsciiCase(".terminal"))
159 dir = true;
163 //TODO: Using open(1) with an argument that syntactically is an absolute
164 // URI reference does not necessarily give expected results:
165 // 1 If the given URI reference matches a supported scheme (e.g.,
166 // "mailto:foo"):
167 // 1.1 If it matches an existing pathname (relative to CWD): Results
168 // in "mailto:foo?\n[0]\tcancel\n[1]\tOpen the file\tmailto:foo\n[2]\t
169 // Open the URL\tmailto:foo\n\nWhich did you mean? Cancelled." on
170 // stderr and SystemShellExecuteException.
171 // 1.2 If it does not match an existing pathname (relative to CWD):
172 // Results in the corresponding application being opened with the given
173 // document (e.g., Mail with a New Message).
174 // 2 If the given URI reference does not match a supported scheme
175 // (e.g., "foo:bar"):
176 // 2.1 If it matches an existing pathname (relative to CWD) pointing to
177 // an executable: Results in execution of that executable.
178 // 2.2 If it matches an existing pathname (relative to CWD) pointing to
179 // a non-executable regular file: Results in opening it in TextEdit.
180 // 2.3 If it matches an existing pathname (relative to CWD) pointing to
181 // a directory: Results in opening it in Finder.
182 // 2.4 If it does not match an existing pathname (relative to CWD):
183 // Results in "The file /.../foo:bar does not exits." (where "/..." is
184 // the CWD) on stderr and SystemShellExecuteException.
185 aBuffer.append("open");
186 if (dir) {
187 aBuffer.append(" -R");
189 aBuffer.append(" --");
190 #else
191 // Just use xdg-open on non-Mac
192 aBuffer.append("xdg-open");
193 #endif
194 aBuffer.append(" ");
195 escapeForShell(aBuffer, OUStringToOString(aURL, osl_getThreadTextEncoding()));
197 if ( pDesktopLaunch && *pDesktopLaunch )
199 aLaunchBuffer.append( pDesktopLaunch + OString::Concat(" "));
200 escapeForShell(aLaunchBuffer, OUStringToOString(aURL, osl_getThreadTextEncoding()));
202 } else if ((nFlags & css::system::SystemShellExecuteFlags::URIS_ONLY) != 0)
204 throw css::lang::IllegalArgumentException(
205 "XSystemShellExecute.execute URIS_ONLY with non-absolute"
206 " URI reference "
207 + aCommand,
208 getXWeak(), 0);
209 } else {
210 #if defined MACOSX
211 auto usingOpen = false;
212 if (OString pathname8;
213 aCommand.convertToString(
214 &pathname8, RTL_TEXTENCODING_UTF8,
215 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR))
217 if (struct stat st; stat(pathname8.getStr(), &st) == 0 && S_ISDIR(st.st_mode)) {
218 usingOpen = true;
219 aBuffer.append("open -a ");
222 #endif
223 escapeForShell(aBuffer, OUStringToOString(aCommand, osl_getThreadTextEncoding()));
224 if (!aParameter.isEmpty()) {
225 aBuffer.append(" ");
226 #if defined MACOSX
227 if (usingOpen) {
228 aBuffer.append("--args ");
230 #endif
231 if( nFlags != 42 )
232 escapeForShell(aBuffer, OUStringToOString(aParameter, osl_getThreadTextEncoding()));
233 else
234 aBuffer.append(OUStringToOString(aParameter, osl_getThreadTextEncoding()));
238 // Prefer DESKTOP_LAUNCH when available
239 if ( !aLaunchBuffer.isEmpty() )
241 FILE *pLaunch = popen( aLaunchBuffer.makeStringAndClear().getStr(), "w" );
242 if ( pLaunch != nullptr )
244 if ( 0 == pclose( pLaunch ) )
245 return;
247 // Failed, do not try DESKTOP_LAUNCH any more
248 pDesktopLaunch = nullptr;
251 OString cmd =
252 #ifdef LINUX
253 // avoid blocking (call it in background)
254 "( " + aBuffer + " ) &";
255 #else
256 aBuffer.makeStringAndClear();
257 #endif
258 FILE *pLaunch = popen(cmd.getStr(), "w");
259 if ( pLaunch != nullptr )
261 if ( 0 == pclose( pLaunch ) )
262 return;
265 int nerr = errno;
266 throw SystemShellExecuteException(OUString::createFromAscii( strerror( nerr ) ),
267 static_cast < XSystemShellExecute * > (this), nerr );
268 #else // EMSCRIPTEN
269 (void)nFlags;
271 css::uno::Reference< css::uri::XUriReference > uri(
272 css::uri::UriReferenceFactory::create(m_xContext)->parse(aCommand));
273 if (!uri.is() || !uri->isAbsolute())
274 throw SystemShellExecuteException("Emscripten can just open absolute URIs.",
275 static_cast<XSystemShellExecute*>(this), 42);
276 if (!aParameter.isEmpty())
277 throw SystemShellExecuteException("Emscripten can't process parameters; encode in URI.",
278 static_cast<XSystemShellExecute*>(this), 42);
280 OUString sEscapedURI(rtl::Uri::encode(aCommand, rtl_UriCharClassUric,
281 rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8));
282 execute_browser(sEscapedURI.toUtf8().getStr());
283 #endif
286 // XServiceInfo
288 OUString SAL_CALL ShellExec::getImplementationName( )
290 return u"com.sun.star.comp.system.SystemShellExecute"_ustr;
293 sal_Bool SAL_CALL ShellExec::supportsService( const OUString& ServiceName )
295 return cppu::supportsService(this, ServiceName);
298 Sequence< OUString > SAL_CALL ShellExec::getSupportedServiceNames( )
300 return { u"com.sun.star.system.SystemShellExecute"_ustr };
303 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
304 shell_ShellExec_get_implementation(
305 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
307 return cppu::acquire(new ShellExec(context));
311 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */