xref: /trunk/main/shell/source/unix/exec/shellexec.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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_shell.hxx"
26 #include <osl/diagnose.h>
27 #include <osl/thread.h>
28 #include <osl/process.h>
29 #include <osl/file.hxx>
30 #include <rtl/ustrbuf.hxx>
31 
32 #ifndef _RTL_URI_H_
33 #include <rtl/uri.hxx>
34 #endif
35 #include "shellexec.hxx"
36 #include <com/sun/star/system/SystemShellExecuteFlags.hpp>
37 
38 #include <com/sun/star/util/XMacroExpander.hpp>
39 #include <com/sun/star/uri/XExternalUriReferenceTranslator.hpp>
40 #include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp>
41 
42 #include "uno/current_context.hxx"
43 
44 #include <string.h>
45 #include <errno.h>
46 #include <unistd.h>
47 #ifdef OS2
48 #include <process.h>
49 #endif
50 
51 //------------------------------------------------------------------------
52 // namespace directives
53 //------------------------------------------------------------------------
54 
55 using com::sun::star::system::XSystemShellExecute;
56 using com::sun::star::system::SystemShellExecuteException;
57 
58 using rtl::OString;
59 using rtl::OUString;
60 using rtl::OStringBuffer;
61 using rtl::OUStringBuffer;
62 using osl::FileBase;
63 
64 using namespace ::com::sun::star::uno;
65 using namespace ::com::sun::star::lang;
66 using namespace ::com::sun::star::system::SystemShellExecuteFlags;
67 using namespace cppu;
68 
69 //------------------------------------------------------------------------
70 // defines
71 //------------------------------------------------------------------------
72 
73 #define SHELLEXEC_IMPL_NAME  "com.sun.star.comp.system.SystemShellExecute2"
74 
75 //------------------------------------------------------------------------
76 // helper functions
77 //------------------------------------------------------------------------
78 
79 namespace // private
80 {
ShellExec_getSupportedServiceNames()81     Sequence< OUString > SAL_CALL ShellExec_getSupportedServiceNames()
82     {
83         Sequence< OUString > aRet(1);
84         aRet[0] = OUString::createFromAscii("com.sun.star.sys.shell.SystemShellExecute");
85         return aRet;
86     }
87 }
88 
escapeForShell(rtl::OStringBuffer & rBuffer,const rtl::OString & rURL)89 void escapeForShell( rtl::OStringBuffer & rBuffer, const rtl::OString & rURL)
90 {
91     sal_Int32 nmax = rURL.getLength();
92     for(sal_Int32 n=0; n < nmax; ++n)
93     {
94         // escape every non alpha numeric characters (excluding a few "known good") by prepending a '\'
95         sal_Char c = rURL[n];
96 #ifndef OS2 // YD shell does not support escaped chars
97         if( ( c < 'A' || c > 'Z' ) && ( c < 'a' || c > 'z' ) && ( c < '0' || c > '9' )  && c != '/' && c != '.' )
98             rBuffer.append( '\\' );
99 #endif
100 
101         rBuffer.append( c );
102     }
103 }
104 
105 //-----------------------------------------------------------------------------------------
106 //
107 //-----------------------------------------------------------------------------------------
108 
ShellExec(const Reference<XComponentContext> & xContext)109 ShellExec::ShellExec( const Reference< XComponentContext >& xContext ) :
110     WeakImplHelper2< XSystemShellExecute, XServiceInfo >(),
111     m_xContext(xContext)
112 {
113     try {
114         Reference< XCurrentContext > xCurrentContext(getCurrentContext());
115 
116         if (xCurrentContext.is())
117         {
118             Any aValue = xCurrentContext->getValueByName(
119                 OUString( RTL_CONSTASCII_USTRINGPARAM( "system.desktop-environment" ) ) );
120 
121             OUString aDesktopEnvironment;
122             if (aValue >>= aDesktopEnvironment)
123             {
124                 m_aDesktopEnvironment = OUStringToOString(aDesktopEnvironment, RTL_TEXTENCODING_ASCII_US);
125             }
126         }
127     } catch (RuntimeException e) {
128     }
129 }
130 
131 //-------------------------------------------------
132 //
133 //-------------------------------------------------
134 
execute(const OUString & aCommand,const OUString & aParameter,sal_Int32 nFlags)135 void SAL_CALL ShellExec::execute( const OUString& aCommand, const OUString& aParameter, sal_Int32 nFlags )
136 {
137     OStringBuffer aBuffer, aLaunchBuffer;
138 
139     // DESKTOP_LAUNCH, see http://freedesktop.org/pipermail/xdg/2004-August/004489.html
140     static const char *pDesktopLaunch = getenv( "DESKTOP_LAUNCH" );
141 
142     // Check whether aCommand contains a document url or not
143     sal_Int32 nIndex = aCommand.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM(":/") ) );
144 
145     if( nIndex > 0 || 0 == aCommand.compareToAscii("mailto:", 7) )
146     {
147         // It seems to be a url ..
148         // We need to re-encode file urls because osl_getFileURLFromSystemPath converts
149         // to UTF-8 before encoding non ascii characters, which is not what other apps
150         // expect.
151         OUString aURL(
152             com::sun::star::uri::ExternalUriReferenceTranslator::create(
153                 m_xContext)->translateToExternal(aCommand));
154         if ( aURL.getLength() == 0 && aCommand.getLength() != 0 )
155         {
156             throw RuntimeException(
157                 (OUString(
158                     RTL_CONSTASCII_USTRINGPARAM(
159                         "Cannot translate URI reference to external format: "))
160                  + aCommand),
161                 static_cast< cppu::OWeakObject * >(this));
162         }
163 
164 #ifdef MACOSX
165         aBuffer.append("open");
166 #else
167         // The url launchers are expected to be in the $OOO_BASE_DIR/program
168         // directory:
169         com::sun::star::uno::Reference< com::sun::star::util::XMacroExpander >
170             exp;
171         if (!(m_xContext->getValueByName(
172                   rtl::OUString(
173                       RTL_CONSTASCII_USTRINGPARAM(
174                           "/singletons/com.sun.star.util.theMacroExpander")))
175               >>= exp)
176             || !exp.is())
177         {
178             throw SystemShellExecuteException(
179                 rtl::OUString(
180                     RTL_CONSTASCII_USTRINGPARAM(
181                         "component context fails to supply singleton"
182                         " com.sun.star.util.theMacroExpander of type"
183                         " com.sun.star.util.XMacroExpander")),
184                 static_cast< XSystemShellExecute * >(this), ENOENT);
185         }
186         OUString aProgramURL;
187         try {
188             aProgramURL = exp->expandMacros(
189                 rtl::OUString(
190                     RTL_CONSTASCII_USTRINGPARAM("$OOO_BASE_DIR/program/")));
191         } catch (com::sun::star::lang::IllegalArgumentException &)
192         {
193             throw SystemShellExecuteException(
194                 OUString(RTL_CONSTASCII_USTRINGPARAM("Could not expand $OOO_BASE_DIR path")),
195                 static_cast < XSystemShellExecute * > (this), ENOENT );
196         }
197 
198         OUString aProgram;
199         if ( FileBase::E_None != FileBase::getSystemPathFromFileURL(aProgramURL, aProgram))
200         {
201             throw SystemShellExecuteException(
202                 OUString(RTL_CONSTASCII_USTRINGPARAM("Cound not convert executable path")),
203                 static_cast < XSystemShellExecute * > (this), ENOENT );
204         }
205 
206 #ifdef OS2
207         OStringBuffer aProg = OUStringToOString(aProgram, osl_getThreadTextEncoding());
208         aProg.append("open-url.exe");
209         OString aUrl = OUStringToOString(aURL, osl_getThreadTextEncoding());
210         if ( -1 == spawnl(P_NOWAIT, aProg.getStr(), aProg.getStr(), aUrl.getStr() , NULL) )
211         {
212             int nerr = errno;
213             throw SystemShellExecuteException(OUString::createFromAscii( strerror( nerr ) ),
214                 static_cast < XSystemShellExecute * > (this), nerr );
215         }
216         return;
217 #endif
218 
219         OString aTmp = OUStringToOString(aProgram, osl_getThreadTextEncoding());
220         escapeForShell(aBuffer, aTmp);
221 
222 #ifdef SOLARIS
223         if ( m_aDesktopEnvironment.getLength() == 0 )
224              m_aDesktopEnvironment = OString("GNOME");
225 #endif
226 
227         // Respect the desktop environment - if there is an executable named
228         // <desktop-environment-is>-open-url, pass the url to this one instead
229         // of the default "open-url" script.
230         if ( m_aDesktopEnvironment.getLength() > 0 )
231         {
232             OString aDesktopEnvironment(m_aDesktopEnvironment.toAsciiLowerCase());
233             OStringBuffer aCopy(aTmp);
234 
235             aCopy.append(aDesktopEnvironment);
236             aCopy.append("-open-url");
237 
238             if ( 0 == access( aCopy.getStr(), X_OK) )
239             {
240                 aBuffer.append(aDesktopEnvironment);
241                 aBuffer.append("-");
242 
243                 /* CDE requires file urls to be decoded */
244                 if ( m_aDesktopEnvironment.equals("CDE") && 0 == aURL.compareToAscii("file://", 7) )
245                 {
246                     aURL = rtl::Uri::decode(aURL, rtl_UriDecodeWithCharset, osl_getThreadTextEncoding());
247                 }
248             }
249         }
250 
251         aBuffer.append("open-url");
252 #endif
253         aBuffer.append(" ");
254         escapeForShell(aBuffer, OUStringToOString(aURL, osl_getThreadTextEncoding()));
255 
256         if ( pDesktopLaunch && *pDesktopLaunch )
257         {
258             aLaunchBuffer.append( pDesktopLaunch );
259             aLaunchBuffer.append(" ");
260             escapeForShell(aLaunchBuffer, OUStringToOString(aURL, osl_getThreadTextEncoding()));
261         }
262     } else {
263         escapeForShell(aBuffer, OUStringToOString(aCommand, osl_getThreadTextEncoding()));
264         aBuffer.append(" ");
265         if( nFlags != 42 )
266             escapeForShell(aBuffer, OUStringToOString(aParameter, osl_getThreadTextEncoding()));
267         else
268             aBuffer.append(OUStringToOString(aParameter, osl_getThreadTextEncoding()));
269     }
270 
271     // Prefer DESKTOP_LAUNCH when available
272     if ( aLaunchBuffer.getLength() > 0 )
273     {
274         FILE *pLaunch = popen( aLaunchBuffer.makeStringAndClear().getStr(), "w" );
275         if ( pLaunch != NULL )
276         {
277             if ( 0 == pclose( pLaunch ) )
278                 return;
279         }
280         // Failed, do not try DESKTOP_LAUNCH any more
281         pDesktopLaunch = NULL;
282     }
283 
284     OString cmd = aBuffer.makeStringAndClear();
285     if ( 0 != pclose(popen(cmd.getStr(), "w")) )
286     {
287         int nerr = errno;
288         throw SystemShellExecuteException(OUString::createFromAscii( strerror( nerr ) ),
289             static_cast < XSystemShellExecute * > (this), nerr );
290     }
291 }
292 
293 
294 // -------------------------------------------------
295 // XServiceInfo
296 // -------------------------------------------------
297 
getImplementationName()298 OUString SAL_CALL ShellExec::getImplementationName(  )
299 {
300     return OUString::createFromAscii( SHELLEXEC_IMPL_NAME );
301 }
302 
303 // -------------------------------------------------
304 //  XServiceInfo
305 // -------------------------------------------------
306 
supportsService(const OUString & ServiceName)307 sal_Bool SAL_CALL ShellExec::supportsService( const OUString& ServiceName )
308 {
309     Sequence < OUString > SupportedServicesNames = ShellExec_getSupportedServiceNames();
310 
311     for ( sal_Int32 n = SupportedServicesNames.getLength(); n--; )
312         if (SupportedServicesNames[n].compareTo(ServiceName) == 0)
313             return sal_True;
314 
315     return sal_False;
316 }
317 
318 // -------------------------------------------------
319 //  XServiceInfo
320 // -------------------------------------------------
321 
getSupportedServiceNames()322 Sequence< OUString > SAL_CALL ShellExec::getSupportedServiceNames(   )
323 {
324     return ShellExec_getSupportedServiceNames();
325 }
326