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