diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /jvmfwk/plugins/sunmajor/pluginlib | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'jvmfwk/plugins/sunmajor/pluginlib')
-rw-r--r-- | jvmfwk/plugins/sunmajor/pluginlib/JREProperties.java | 62 | ||||
-rw-r--r-- | jvmfwk/plugins/sunmajor/pluginlib/diagnostics.h | 35 | ||||
-rw-r--r-- | jvmfwk/plugins/sunmajor/pluginlib/otherjre.cxx | 115 | ||||
-rw-r--r-- | jvmfwk/plugins/sunmajor/pluginlib/otherjre.hxx | 43 | ||||
-rw-r--r-- | jvmfwk/plugins/sunmajor/pluginlib/sunjavaplugin.cxx | 900 | ||||
-rw-r--r-- | jvmfwk/plugins/sunmajor/pluginlib/sunjre.cxx | 116 | ||||
-rw-r--r-- | jvmfwk/plugins/sunmajor/pluginlib/sunjre.hxx | 42 | ||||
-rw-r--r-- | jvmfwk/plugins/sunmajor/pluginlib/sunversion.cxx | 303 | ||||
-rw-r--r-- | jvmfwk/plugins/sunmajor/pluginlib/sunversion.hxx | 115 | ||||
-rw-r--r-- | jvmfwk/plugins/sunmajor/pluginlib/util.cxx | 1281 | ||||
-rw-r--r-- | jvmfwk/plugins/sunmajor/pluginlib/util.hxx | 116 | ||||
-rw-r--r-- | jvmfwk/plugins/sunmajor/pluginlib/util_cocoa.hxx | 14 | ||||
-rw-r--r-- | jvmfwk/plugins/sunmajor/pluginlib/util_cocoa.mm | 109 | ||||
-rw-r--r-- | jvmfwk/plugins/sunmajor/pluginlib/vendorbase.cxx | 197 | ||||
-rw-r--r-- | jvmfwk/plugins/sunmajor/pluginlib/vendorlist.cxx | 52 | ||||
-rw-r--r-- | jvmfwk/plugins/sunmajor/pluginlib/vendorlist.hxx | 49 |
16 files changed, 3549 insertions, 0 deletions
diff --git a/jvmfwk/plugins/sunmajor/pluginlib/JREProperties.java b/jvmfwk/plugins/sunmajor/pluginlib/JREProperties.java new file mode 100644 index 000000000..0d3503af3 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/JREProperties.java @@ -0,0 +1,62 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import java.util.*; + +/** This class prints out the system properties. + + We cannot print the strings directly because of encoding issues. Since + about 1.3.1 one can start java with the option -Dfile.encoding=UTF-8, but + unfortunately this works only with later update - versions (for example, + 1.3.1_07). Therefore we use this scheme. The property string has this form: + name=value + + Every character is cast to an integer which value is printed, followed by a + space. If all characters of the string are printed, then a new line is printed. +*/ +public class JREProperties +{ + public static void main(String[] args) + { + try + { + Properties p = System.getProperties(); + Enumeration e = p.propertyNames(); + while (e.hasMoreElements()) { + String sProp = (String) e.nextElement(); + String sCompleteProp = sProp + "=" + p.getProperty(sProp); + char[] arChars = new char[sCompleteProp.length()]; + sCompleteProp.getChars(0, sCompleteProp.length(), arChars, 0); + for (int c = 0; c < arChars.length; c++) { + System.out.print(String.valueOf((int) arChars[c])); + System.out.print(" "); + } + System.out.print("\n"); + } + } + catch(Exception e) + { + System.err.println(e); + } + + System.exit(0); + } + + + +} diff --git a/jvmfwk/plugins/sunmajor/pluginlib/diagnostics.h b/jvmfwk/plugins/sunmajor/pluginlib/diagnostics.h new file mode 100644 index 000000000..599c40438 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/diagnostics.h @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_DIAGNOSTICS_H +#define INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_DIAGNOSTICS_H + +#include <sal/config.h> + +#include <sal/log.hxx> + +#define JFW_ENSURE(c, m) SAL_WARN_IF(!(c), "jfw", m) + +#define JFW_TRACE0(m) SAL_INFO("jfw.level1", m) + +#define JFW_TRACE2(m) SAL_INFO("jfw.level2", m) + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/otherjre.cxx b/jvmfwk/plugins/sunmajor/pluginlib/otherjre.cxx new file mode 100644 index 000000000..528af9498 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/otherjre.cxx @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include "otherjre.hxx" + +using ::rtl::Reference; +namespace jfw_plugin +{ + +Reference<VendorBase> OtherInfo::createInstance() +{ + return new OtherInfo; +} + + +char const* const* OtherInfo::getJavaExePaths(int * size) +{ + static char const * ar[] = { +#if defined(_WIN32) + "bin/java.exe", + "jre/bin/java.exe" +#elif defined UNX + "bin/java", + "jre/bin/java" +#endif + }; + *size = SAL_N_ELEMENTS (ar); + return ar; +} + +char const* const* OtherInfo::getRuntimePaths(int * size) +{ + static char const* ar[]= { +#if defined(_WIN32) + "/bin/client/jvm.dll", + "/bin/hotspot/jvm.dll", + "/bin/classic/jvm.dll", + "/bin/jrockit/jvm.dll", + // Needed by IBM Semeru Runtime, which is an OpenJDK (so should actually use SunInfo) with a + // java.vendor of "IBM Corporation", so using OtherInfo here (cf. gVendorMap in + // jvmfwk/plugins/sunmajor/pluginlib/vendorlist.cxx): + "/bin/server/jvm.dll" +#elif defined UNX +#ifdef MACOSX + "/../../../../../Frameworks/JavaVM.framework/JavaVM" //as of 1.6.0_22 +#else + "/lib/" JFW_PLUGIN_ARCH "/client/libjvm.so", // for Blackdown PPC + "/lib/" JFW_PLUGIN_ARCH "/server/libjvm.so", // for Blackdown AMD64 + "/lib/" JFW_PLUGIN_ARCH "/classic/libjvm.so", // for Blackdown PPC + "/lib/" JFW_PLUGIN_ARCH "/jrockit/libjvm.so", // for Java of BEA Systems + "/bin/classic/libjvm.so", // fallback for older for IBM Java + "/jre/bin/classic/libjvm.so" // fallback for older for IBM Java +#endif +#endif + + }; + *size = SAL_N_ELEMENTS(ar); + return ar; +} + +char const* const* OtherInfo::getLibraryPaths(int* size) +{ + +#if defined(UNX) && !defined(MACOSX) + //mac version does not have a ld library path anymore + static char const * ar[] = { + "/bin", + "/jre/bin", + "/bin/classic", + "/jre/bin/classic", + "/lib/" JFW_PLUGIN_ARCH "/client", + "/lib/" JFW_PLUGIN_ARCH "/server", + "/lib/" JFW_PLUGIN_ARCH "/classic", + "/lib/" JFW_PLUGIN_ARCH "/jrockit", + "/lib/" JFW_PLUGIN_ARCH "/native_threads", + ("/lib/" JFW_PLUGIN_ARCH) + }; + + *size = SAL_N_ELEMENTS(ar); + return ar; +#else + *size = 0; + return nullptr; +#endif +} + +int OtherInfo::compareVersions(const OUString& /*sSecond*/) const +{ + //Need to provide an own algorithm for comparing version. + //Because this function returns always 0, which means the version of + //this JRE and the provided version "sSecond" are equal, one cannot put + //any excludeVersion entries in the javavendors.xml file. + return 0; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/otherjre.hxx b/jvmfwk/plugins/sunmajor/pluginlib/otherjre.hxx new file mode 100644 index 000000000..f722234ef --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/otherjre.hxx @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_OTHERJRE_HXX +#define INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_OTHERJRE_HXX + +#include <vendorbase.hxx> + +namespace jfw_plugin +{ +/* Do not forget to put this class in the vendor map in vendorlist.cxx + */ +class OtherInfo : public VendorBase +{ +public: + static char const* const* getJavaExePaths(int* size); + + static rtl::Reference<VendorBase> createInstance(); + + virtual char const* const* getRuntimePaths(int* size) override; + virtual char const* const* getLibraryPaths(int* size) override; + virtual int compareVersions(const OUString& sSecond) const override; +}; +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/sunjavaplugin.cxx b/jvmfwk/plugins/sunmajor/pluginlib/sunjavaplugin.cxx new file mode 100644 index 000000000..03c38c1de --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/sunjavaplugin.cxx @@ -0,0 +1,900 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#ifdef _WIN32 +# include <sys/stat.h> +# if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include <windows.h> +#endif + +#ifdef ANDROID +# include <dlfcn.h> +#endif + +#include <string.h> + +#include <cstddef> +#include <cassert> +#include <memory> +#include <string_view> +#include <utility> +#include <vector> + +#include <config_options.h> +#include <osl/diagnose.h> +#include <rtl/ustring.hxx> +#include <rtl/ustrbuf.hxx> +#include <osl/module.hxx> +#include <osl/mutex.hxx> +#include <osl/process.h> +#include <osl/thread.hxx> +#include <osl/file.hxx> +#include <sal/log.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include <setjmp.h> +#include <signal.h> + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-attributes" +#endif +#include <jni.h> +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +#include <rtl/byteseq.hxx> +#include <fwkbase.hxx> +#include <elements.hxx> +#include <vendorbase.hxx> +#include <vendorplugin.hxx> +#include <jvmfwk/framework.hxx> +#include "util.hxx" +#include "sunversion.hxx" +#include "diagnostics.h" + +#if defined MACOSX && defined __x86_64__ +#include "util_cocoa.hxx" +#endif + +#ifdef ANDROID +#include <osl/detail/android-bootstrap.h> +#else +#if !ENABLE_RUNTIME_OPTIMIZATIONS +#define FORCE_INTERPRETED 1 +#elif defined HAVE_VALGRIND_HEADERS +#include <valgrind/valgrind.h> +#define FORCE_INTERPRETED RUNNING_ON_VALGRIND +#else +#define FORCE_INTERPRETED 0 +#endif +#endif + +#if defined LINUX && (defined X86 || defined X86_64) +#include <sys/resource.h> +#endif + +using namespace osl; +using namespace jfw_plugin; + + +namespace { + +#if defined(UNX) && !defined(ANDROID) +OString getPluginJarPath( + std::u16string_view sVendor, + std::u16string_view sLocation, + std::u16string_view sVersion) +{ + OString ret; + OUString sName1("javaplugin.jar"); + OUString sName2("plugin.jar"); + OUString sPath; + if ( sVendor == u"Sun Microsystems Inc." ) + { + SunVersion ver142("1.4.2-ea"); + SunVersion ver150("1.5.0-ea"); + SunVersion ver(sVersion); + OSL_ASSERT(ver142 && ver150 && ver); + + OUString sName; + if (ver < ver142) + { + sName = sName1; + } + else if (ver < ver150) + {//this will cause ea, beta etc. to have plugin.jar in path. + //but this does not harm. 1.5.0-beta < 1.5.0 + sName = sName2; + } + if (!sName.isEmpty()) + { + sName = OUString::Concat(sLocation) + "/lib/" + sName; + OSL_VERIFY( + osl_getSystemPathFromFileURL(sName.pData, & sPath.pData) + == osl_File_E_None); + } + } + else + { + OUString sName(OUString::Concat(sLocation) + "/lib/" + sName1); + OUString sPath1; + OUString sPath2; + if (osl_getSystemPathFromFileURL(sName.pData, & sPath1.pData) + == osl_File_E_None) + { + sName = OUString::Concat(sLocation) + "/lib/" + sName2; + if (osl_getSystemPathFromFileURL(sName.pData, & sPath2.pData) + == osl_File_E_None) + { + sPath = sPath1 + OUStringChar(SAL_PATHSEPARATOR) + sPath2; + } + } + OSL_ASSERT(!sPath.isEmpty()); + } + ret = OUStringToOString(sPath, osl_getThreadTextEncoding()); + + return ret; +} +#endif // UNX + + +std::unique_ptr<JavaInfo> createJavaInfo( + const rtl::Reference<VendorBase> & info) +{ + OUStringBuffer buf(1024); + buf.append(info->getRuntimeLibrary()); + if (!info->getLibraryPath().isEmpty()) + { + buf.append("\n"); + buf.append(info->getLibraryPath()); + buf.append("\n"); + } + OUString sVendorData = buf.makeStringAndClear(); + return std::unique_ptr<JavaInfo>( + new JavaInfo{ + info->getVendor(), info->getHome(), info->getVersion(), + sal_uInt64(info->needsRestart() ? JFW_REQUIRE_NEEDRESTART : 0), + rtl::ByteSequence( + reinterpret_cast<sal_Int8*>(sVendorData.pData->buffer), + sVendorData.getLength() * sizeof(sal_Unicode))}); +} + +OUString getRuntimeLib(const rtl::ByteSequence & data) +{ + const sal_Unicode* chars = reinterpret_cast<sal_Unicode const *>(data.getConstArray()); + sal_Int32 len = data.getLength(); + OUString sData(chars, len / 2); + //the runtime lib is on the first line + sal_Int32 index = 0; + OUString aToken = sData.getToken( 0, '\n', index); + + return aToken; +} + +jmp_buf jmp_jvm_abort; +sig_atomic_t g_bInGetJavaVM = 0; + +extern "C" void JNICALL abort_handler() +{ + // If we are within JNI_CreateJavaVM then we jump back into getJavaVM + if( g_bInGetJavaVM != 0 ) + { + SAL_WARN("jfw", "JavaVM: JNI_CreateJavaVM called os::abort(), caught by abort_handler"); + longjmp( jmp_jvm_abort, 0); + } +} + +typedef jint JNICALL JNI_CreateVM_Type(JavaVM **, JNIEnv **, void *); + +#ifndef ANDROID +int createJvm( + JNI_CreateVM_Type * pCreateJavaVM, JavaVM ** pJavaVM, JNIEnv ** ppEnv, JavaVMInitArgs * vm_args) +{ + /* We set a global flag which is used by the abort handler in order to + determine whether it is should use longjmp to get back into this function. + That is, the abort handler determines if it is on the same stack as this function + and then jumps back into this function. + */ + g_bInGetJavaVM = 1; + jint err; + memset( jmp_jvm_abort, 0, sizeof(jmp_jvm_abort)); + /* If the setjmp return value is not "0" then this point was reached by a longjmp in the + abort_handler, which was called indirectly by JNI_CreateVM. + */ + if( setjmp( jmp_jvm_abort ) == 0) + { + //returns negative number on failure + err= pCreateJavaVM(pJavaVM, ppEnv, vm_args); + g_bInGetJavaVM = 0; + } + else + // set err to a positive number, so as or recognize that an abort (longjmp) + //occurred + err= 1; + return err; +} +#endif + +/** helper function to check Java version requirements + + This function checks if the Java version of the given VendorBase + meets the given Java version requirements. + + @param aVendorInfo + [in] the object to be inspected whether it meets the version requirements + @param sMinVersion + [in] represents the minimum version of a JRE. The string can be empty. + @param sMaxVersion + [in] represents the maximum version of a JRE. The string can be empty. + @param arExcludeList + [in] contains a list of "bad" versions. JREs which have one of these + versions must not be returned by this function. + + @return + javaPluginError::NONE the function ran successfully and the version requirements are met + javaPluginError::FailedVersion at least one of the version requirements (minVersion, + maxVersion, excludeVersions) was violated + javaPluginError::WrongVersionFormat the version strings in + <code>sMinVersion,sMaxVersion,arExcludeList</code> are not recognized as valid + version strings. + + */ +javaPluginError checkJavaVersionRequirements( + rtl::Reference<VendorBase> const & aVendorInfo, + OUString const& sMinVersion, + OUString const& sMaxVersion, + std::vector<OUString> const & arExcludeList) +{ + if (!aVendorInfo->isValidArch()) + { + return javaPluginError::WrongArch; + } + if (!sMinVersion.isEmpty()) + { + try + { + if (aVendorInfo->compareVersions(sMinVersion) < 0) + return javaPluginError::FailedVersion; + } + catch (MalformedVersionException&) + { + //The minVersion was not recognized as valid for this vendor. + JFW_ENSURE( + false, + "[Java framework]sunjavaplugin does not know version: " + + sMinVersion + " for vendor: " + aVendorInfo->getVendor() + + " .Check minimum Version." ); + return javaPluginError::WrongVersionFormat; + } + } + + if (!sMaxVersion.isEmpty()) + { + try + { + if (aVendorInfo->compareVersions(sMaxVersion) > 0) + return javaPluginError::FailedVersion; + } + catch (MalformedVersionException&) + { + //The maxVersion was not recognized as valid for this vendor. + JFW_ENSURE( + false, + "[Java framework]sunjavaplugin does not know version: " + + sMaxVersion + " for vendor: " + aVendorInfo->getVendor() + + " .Check maximum Version." ); + return javaPluginError::WrongVersionFormat; + } + } + + for (auto const & sExVer: arExcludeList) { + try + { + if (aVendorInfo->compareVersions(sExVer) == 0) + return javaPluginError::FailedVersion; + } + catch (MalformedVersionException&) + { + //The excluded version was not recognized as valid for this vendor. + JFW_ENSURE( + false, + "[Java framework]sunjavaplugin does not know version: " + + sExVer + " for vendor: " + aVendorInfo->getVendor() + + " .Check excluded versions." ); + return javaPluginError::WrongVersionFormat; + } + } + + return javaPluginError::NONE; +} + +} + +javaPluginError jfw_plugin_getAllJavaInfos( + bool checkJavaHomeAndPath, + jfw::VendorSettings const & vendorSettings, + std::vector<std::unique_ptr<JavaInfo>>* parJavaInfo, + std::vector<rtl::Reference<jfw_plugin::VendorBase>> & infos) +{ + assert(parJavaInfo); + + //Find all JREs + std::vector<rtl::Reference<VendorBase> > vecInfos = + addAllJREInfos(checkJavaHomeAndPath, infos); + std::vector<rtl::Reference<VendorBase> > vecVerifiedInfos; + + for (auto const& vecInfo : vecInfos) + { + auto const versionInfo = vendorSettings.getVersionInformation(vecInfo->getVendor()); + javaPluginError err = checkJavaVersionRequirements( + vecInfo, versionInfo.sMinVersion, versionInfo.sMaxVersion, versionInfo.vecExcludeVersions); + + if (err == javaPluginError::FailedVersion || err == javaPluginError::WrongArch) + continue; + else if (err == javaPluginError::WrongVersionFormat) + return err; + + vecVerifiedInfos.push_back(vecInfo); + } + //Now vecVerifiedInfos contains all those JREs which meet the version requirements + //Transfer them into the array that is passed out. + parJavaInfo->clear(); + for (auto const& vecVerifiedInfo : vecVerifiedInfos) + { + parJavaInfo->push_back(createJavaInfo(vecVerifiedInfo)); + } + + return javaPluginError::NONE; +} + +javaPluginError jfw_plugin_getJavaInfoByPath( + OUString const& sPath, + jfw::VendorSettings const & vendorSettings, + std::unique_ptr<JavaInfo> * ppInfo) +{ + assert(ppInfo != nullptr); + OSL_ASSERT(!sPath.isEmpty()); + if (sPath.isEmpty()) + return javaPluginError::InvalidArg; + + rtl::Reference<VendorBase> aVendorInfo = getJREInfoByPath(sPath); + if (!aVendorInfo.is()) + return javaPluginError::NoJre; + + //Check if the detected JRE matches the version requirements + javaPluginError errorcode = javaPluginError::NONE; + auto const versionInfo = vendorSettings.getVersionInformation(aVendorInfo->getVendor()); + errorcode = checkJavaVersionRequirements( + aVendorInfo, versionInfo.sMinVersion, versionInfo.sMaxVersion, versionInfo.vecExcludeVersions); + + if (errorcode == javaPluginError::NONE) + *ppInfo = createJavaInfo(aVendorInfo); + + return errorcode; +} + +javaPluginError jfw_plugin_getJavaInfoFromJavaHome( + jfw::VendorSettings const & vendorSettings, + std::unique_ptr<JavaInfo> * ppInfo, + std::vector<rtl::Reference<VendorBase>> & infos) +{ + assert(ppInfo); + + std::vector<rtl::Reference<VendorBase>> infoJavaHome; + addJavaInfoFromJavaHome(infos, infoJavaHome); + + if (infoJavaHome.empty()) + return javaPluginError::NoJre; + assert(infoJavaHome.size() == 1); + + //Check if the detected JRE matches the version requirements + auto const versionInfo = vendorSettings.getVersionInformation(infoJavaHome[0]->getVendor()); + if (checkJavaVersionRequirements( + infoJavaHome[0], + versionInfo.sMinVersion, + versionInfo.sMaxVersion, + versionInfo.vecExcludeVersions) + == javaPluginError::NONE) + { + *ppInfo = createJavaInfo(infoJavaHome[0]); + return javaPluginError::NONE; + } + + return javaPluginError::NoJre; +} + +javaPluginError jfw_plugin_getJavaInfosFromPath( + jfw::VendorSettings const & vendorSettings, + std::vector<std::unique_ptr<JavaInfo>> & javaInfosFromPath, + std::vector<rtl::Reference<jfw_plugin::VendorBase>> & infos) +{ + // find JREs from PATH + std::vector<rtl::Reference<VendorBase>> vecInfosFromPath; + addJavaInfosFromPath(infos, vecInfosFromPath); + + std::vector<std::unique_ptr<JavaInfo>> vecVerifiedInfos; + + // copy infos of JREs that meet version requirements to vecVerifiedInfos + for (auto const& infosFromPath : vecInfosFromPath) + { + auto const versionInfo = vendorSettings.getVersionInformation(infosFromPath->getVendor()); + if (checkJavaVersionRequirements( + infosFromPath, + versionInfo.sMinVersion, + versionInfo.sMaxVersion, + versionInfo.vecExcludeVersions) + == javaPluginError::NONE) + { + vecVerifiedInfos.push_back(createJavaInfo(infosFromPath)); + } + } + + if (vecVerifiedInfos.empty()) + return javaPluginError::NoJre; + + javaInfosFromPath = std::move(vecVerifiedInfos); + + return javaPluginError::NONE; +} + + +#if defined(_WIN32) + +// Load msvcr71.dll using an explicit full path from where it is +// present as bundled with the JRE. In case it is not found where we +// think it should be, do nothing, and just let the implicit loading +// that happens when loading the JVM take care of it. + +static void load_msvcr(std::u16string_view jvm_dll, std::u16string_view msvcr) +{ + // First check if msvcr71.dll is in the same folder as jvm.dll. It + // normally isn't, at least up to 1.6.0_22, but who knows if it + // might be in the future. + std::size_t slash = jvm_dll.rfind('\\'); + + if (slash == std::u16string_view::npos) + { + // Huh, weird path to jvm.dll. Oh well. + SAL_WARN("jfw", "JVM pathname <" << OUString(jvm_dll) << "> w/o backslash"); + return; + } + + if (LoadLibraryW( + o3tl::toW(OUString(OUString::Concat(jvm_dll.substr(0, slash+1)) + msvcr).getStr()))) + return; + + // Then check if msvcr71.dll is in the parent folder of where + // jvm.dll is. That is currently (1.6.0_22) as far as I know the + // normal case. + slash = jvm_dll.rfind('\\', slash); + + if (slash == std::u16string_view::npos) + return; + + (void)LoadLibraryW( + o3tl::toW(OUString(OUString::Concat(jvm_dll.substr(0, slash+1)) + msvcr).getStr())); +} + +// Check if the jvm DLL imports msvcr71.dll, and in that case try +// loading it explicitly. In case something goes wrong, do nothing, +// and just let the implicit loading try to take care of it. +static void do_msvcr_magic(OUString const &jvm_dll) +{ + struct stat st; + + OUString Module; + osl::FileBase::RC nError = osl::FileBase::getSystemPathFromFileURL( + jvm_dll, Module); + + if ( osl::FileBase::E_None != nError ) + { + SAL_WARN( + "jfw", "getSystemPathFromFileURL(" << jvm_dll << "): " << +nError); + return; + } + + FILE *f = _wfopen(o3tl::toW(Module.getStr()), L"rb"); + + if (!f) + { + SAL_WARN("jfw", "_wfopen(" << Module << ") failed"); + return; + } + + if (fstat(fileno(f), &st) == -1) + { + SAL_WARN("jfw", "fstat(" << Module << ") failed"); + fclose(f); + return; + } + + PIMAGE_DOS_HEADER dos_hdr = static_cast<PIMAGE_DOS_HEADER>(malloc(st.st_size)); + assert(dos_hdr); + if (fread(dos_hdr, st.st_size, 1, f) != 1 || + memcmp(dos_hdr, "MZ", 2) != 0 || + dos_hdr->e_lfanew < 0 || + dos_hdr->e_lfanew > static_cast<LONG>(st.st_size - sizeof(IMAGE_NT_HEADERS))) + { + SAL_WARN("jfw", "analyzing <" << Module << "> failed"); + free(dos_hdr); + fclose(f); + return; + } + + fclose(f); + + IMAGE_NT_HEADERS *nt_hdr = reinterpret_cast<IMAGE_NT_HEADERS *>(reinterpret_cast<char *>(dos_hdr) + dos_hdr->e_lfanew); + + DWORD importsVA = nt_hdr->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; + // first determine Virtual-to-File-address mapping for the section + // that contains the import directory + IMAGE_SECTION_HEADER *sections = IMAGE_FIRST_SECTION(nt_hdr); + ptrdiff_t VAtoPhys = -1; + for (int i = 0; i < nt_hdr->FileHeader.NumberOfSections; ++i) + { + if (sections->VirtualAddress <= importsVA && + importsVA < sections->VirtualAddress + sections->SizeOfRawData) + { + VAtoPhys = static_cast<size_t>(sections->PointerToRawData) - static_cast<size_t>(sections->VirtualAddress); + break; + } + ++sections; + } + if (-1 == VAtoPhys) // not found? + { + SAL_WARN("jfw", "analyzing <" << Module << "> failed"); + free(dos_hdr); + return; + } + IMAGE_IMPORT_DESCRIPTOR *imports = + reinterpret_cast<IMAGE_IMPORT_DESCRIPTOR *>(reinterpret_cast<char *>(dos_hdr) + importsVA + VAtoPhys); + + while (imports <= reinterpret_cast<IMAGE_IMPORT_DESCRIPTOR *>(reinterpret_cast<char *>(dos_hdr) + st.st_size - sizeof (IMAGE_IMPORT_DESCRIPTOR)) && + imports->Name != 0 && + imports->Name + VAtoPhys < static_cast<DWORD>(st.st_size)) + { + static std::u16string_view msvcrts[] = + { + u"msvcr71.dll", + u"msvcr100.dll" + }; + char const* importName = reinterpret_cast<char *>(dos_hdr) + imports->Name + VAtoPhys; + sal_Int32 importNameLen = rtl_str_getLength(importName); + for (size_t i = 0; i < SAL_N_ELEMENTS(msvcrts); ++i) + { + if (0 == rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths( + msvcrts[i].data(), msvcrts[i].size(), + importName, importNameLen)) + { + load_msvcr(Module, msvcrts[i]); + free(dos_hdr); + return; + } + } + imports++; + } + + free(dos_hdr); +} + +#endif + +/** starts a Java Virtual Machine. + <p> + The function shall ensure, that the VM does not abort the process + during instantiation. + </p> + */ +javaPluginError jfw_plugin_startJavaVirtualMachine( + const JavaInfo *pInfo, + const JavaVMOption* arOptions, + sal_Int32 cOptions, + JavaVM ** ppVm, + JNIEnv ** ppEnv) +{ + static osl::Mutex aPluginMutex; + assert(pInfo != nullptr); + assert(ppVm != nullptr); + assert(ppEnv != nullptr); + osl::MutexGuard guard(aPluginMutex); + javaPluginError errorcode = javaPluginError::NONE; +#ifdef MACOSX + rtl::Reference<VendorBase> aVendorInfo = getJREInfoByPath( pInfo->sLocation ); + if ( !aVendorInfo.is() || aVendorInfo->compareVersions( pInfo->sVersion ) < 0 ) + return javaPluginError::VmCreationFailed; +#endif + OUString sRuntimeLib = getRuntimeLib(pInfo->arVendorData); +#if defined MACOSX && defined __x86_64__ + if ( !JvmfwkUtil_isLoadableJVM( sRuntimeLib ) ) + return javaPluginError::VmCreationFailed; +#endif + JFW_TRACE2("Using Java runtime library: " << sRuntimeLib); + +#ifndef ANDROID + // On linux we load jvm with RTLD_GLOBAL. This is necessary for debugging, because + // libjdwp.so need a symbol (fork1) from libjvm which it only gets if the jvm is loaded + // with RTLD_GLOBAL. On Solaris libjdwp.so is correctly linked with libjvm.so + osl::Module moduleRt; +#if defined(LINUX) + if (!moduleRt.load(sRuntimeLib, SAL_LOADMODULE_GLOBAL | SAL_LOADMODULE_NOW)) +#elif defined MACOSX + // Must be SAL_LOADMODULE_GLOBAL when e.g. specifying a + // -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000 option to + // JDK 1.8.0_121 at least, as JNI_CreateJavaVM -> Threads::create_vm -> + // JvmtiExport::post_vm_initialized -> cbEarlyVMInit -> initialize -> + // util_initialize -> sun.misc.VMSupport.getAgentProperties -> + // Java_sun_misc_VMSupport_initAgentProperties -> + // JDK_FindJvmEntry("JVM_INitAgentProperties") -> + // dlsym(RTLD_DEFAULT, "JVM_INitAgentProperties"): + if (!moduleRt.load(sRuntimeLib, SAL_LOADMODULE_GLOBAL)) +#else +#if defined(_WIN32) + do_msvcr_magic(sRuntimeLib); +#endif + if (!moduleRt.load(sRuntimeLib)) +#endif + { + JFW_ENSURE(false, + "[Java framework]sunjavaplugin" SAL_DLLEXTENSION + " could not load Java runtime library: \n" + + sRuntimeLib + "\n"); + JFW_TRACE0("Could not load Java runtime library: " << sRuntimeLib); + return javaPluginError::VmCreationFailed; + } + +#if defined UNX && !defined MACOSX + //Setting the JAVA_HOME is needed for awt + OUString sPathLocation; + osl::FileBase::getSystemPathFromFileURL(pInfo->sLocation, sPathLocation); + osl_setEnvironment(OUString("JAVA_HOME").pData, sPathLocation.pData); +#endif + + OUString sSymbolCreateJava("JNI_CreateJavaVM"); + + JNI_CreateVM_Type * pCreateJavaVM = + reinterpret_cast<JNI_CreateVM_Type *>(moduleRt.getFunctionSymbol(sSymbolCreateJava)); + if (!pCreateJavaVM) + { + OSL_ASSERT(false); + OString sLib = OUStringToOString( + sRuntimeLib, osl_getThreadTextEncoding()); + OString sSymbol = OUStringToOString( + sSymbolCreateJava, osl_getThreadTextEncoding()); + SAL_WARN("jfw", "Java runtime library: " << sLib << " does not export symbol " << sSymbol); + return javaPluginError::VmCreationFailed; + } + moduleRt.release(); + + // Valgrind typically emits many false errors when executing JIT'ed JVM + // code, so force the JVM into interpreted mode: + bool addForceInterpreted = FORCE_INTERPRETED > 0; + + // Some testing with Java 1.4 showed that JavaVMOption.optionString has to + // be encoded with the system encoding (i.e., osl_getThreadTextEncoding): + JavaVMInitArgs vm_args; + + struct Option { + Option(OString const & theOptionString, void * theExtraInfo): + optionString(theOptionString), extraInfo(theExtraInfo) + {} + + OString optionString; + void * extraInfo; + }; + std::vector<Option> options; + + // We set an abort handler which is called when the VM calls _exit during + // JNI_CreateJavaVM. This happens when the LD_LIBRARY_PATH does not contain + // all some directories of the Java installation. This is necessary for + // all versions below 1.5.1 + options.emplace_back("abort", reinterpret_cast<void*>(abort_handler)); + bool hasStackSize = false; +#ifdef UNX + // Until java 1.5 we need to put a plugin.jar or javaplugin.jar (<1.4.2) + // in the class path in order to have applet support: + OString sAddPath = getPluginJarPath(pInfo->sVendor, pInfo->sLocation,pInfo->sVersion); +#endif + for (int i = 0; i < cOptions; i++) + { + OString opt(arOptions[i].optionString); +#ifdef UNX + if (opt.startsWith("-Djava.class.path=")) + { + if (!sAddPath.isEmpty()) + { + opt += OStringChar(SAL_PATHSEPARATOR) + sAddPath; + sAddPath.clear(); + } + } +#endif + if (opt == "-Xint") { + addForceInterpreted = false; + } + if (opt.startsWith("-Xss")) { + hasStackSize = true; + } + options.emplace_back(opt, arOptions[i].extraInfo); + } + if (addForceInterpreted) { + options.emplace_back("-Xint", nullptr); + } + if (!hasStackSize) { +#if defined LINUX && (defined X86 || defined X86_64) + // At least OpenJDK 1.8.0's os::workaround_expand_exec_shield_cs_limit + // (hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp) can mmap an rwx + // page into the area that the main stack can grow down to according to + // "ulimit -s", as os::init_2's (hotspot/src/os/linux/vm/os_linux.cpp) + // call to + // + // Linux::capture_initial_stack(JavaThread::stack_size_at_create()); + // + // caps _initial_thread_stack_size at threadStackSizeInBytes ,i.e., + // -Xss, which appears to default to only 327680, whereas "ulimit -s" + // defaults to 8192 * 1024 at least on Fedora 20; so attempt to pass in + // a useful -Xss argument: + rlimit l; + if (getrlimit(RLIMIT_STACK, &l) == 0) { + if (l.rlim_cur == RLIM_INFINITY) { + SAL_INFO("jfw", "RLIMIT_STACK RLIM_INFINITY -> 8192K"); + l.rlim_cur = 8192 * 1024; + } else if (l.rlim_cur > 512 * 1024 * 1024) { + SAL_INFO( + "jfw", "huge RLIMIT_STACK " << l.rlim_cur << " -> 8192K"); + l.rlim_cur = 8192 * 1024; + } + options.emplace_back("-Xss" + OString::number(l.rlim_cur), nullptr); + } else { + int e = errno; + SAL_WARN("jfw", "getrlimit(RLIMIT_STACK) failed with errno " << e); + } +#endif + } +#ifdef UNX + if (!sAddPath.isEmpty()) { + options.emplace_back("-Djava.class.path=" + sAddPath, nullptr); + } +#endif + + std::unique_ptr<JavaVMOption[]> sarOptions(new JavaVMOption[options.size()]); + for (std::vector<Option>::size_type i = 0; i != options.size(); ++i) { + SAL_INFO( + "jfw", + "VM option \"" << options[i].optionString << "\" " + << options[i].extraInfo); + sarOptions[i].optionString = const_cast<char *>( + options[i].optionString.getStr()); + sarOptions[i].extraInfo = options[i].extraInfo; + } + +#ifdef MACOSX + vm_args.version= JNI_VERSION_1_4; // issue 88987 +#else + vm_args.version= JNI_VERSION_1_2; +#endif + vm_args.options= sarOptions.get(); + vm_args.nOptions= options.size(); //TODO overflow + vm_args.ignoreUnrecognized= JNI_TRUE; + + JavaVM * pJavaVM = nullptr; + jint err = createJvm(pCreateJavaVM, &pJavaVM, ppEnv, &vm_args); + + if(err != 0) + { + if( err < 0) + { + SAL_WARN("jfw", "Can not create Java Virtual Machine, " << err); + errorcode = javaPluginError::VmCreationFailed; + } + else if( err > 0) + { + SAL_WARN("jfw", "Can not create JavaVirtualMachine, abort handler was called"); + errorcode = javaPluginError::VmCreationFailed; + } + } + else + { + *ppVm = pJavaVM; + JFW_TRACE2("JVM created"); + } +#else + (void) arOptions; + (void) cOptions; + (void) ppEnv; + // On Android we always have a Java VM as we only expect this code + // to be run in an Android app anyway. + *ppVm = lo_get_javavm(); +#endif + + return errorcode; +} + +javaPluginError jfw_plugin_existJRE(const JavaInfo *pInfo, bool *exist) +{ + assert(pInfo != nullptr); + assert(exist != nullptr); + + javaPluginError ret = javaPluginError::NONE; + OUString sLocation(pInfo->sLocation); + + if (sLocation.isEmpty()) + return javaPluginError::InvalidArg; + ::osl::DirectoryItem item; + ::osl::File::RC rc_item = ::osl::DirectoryItem::get(sLocation, item); + if (::osl::File::E_None == rc_item) + { + *exist = true; + } + else if (::osl::File::E_NOENT == rc_item) + { + *exist = false; + } + else + { + ret = javaPluginError::Error; + } + //We can have the situation that the JavaVM runtime library is not + //contained within JAVA_HOME. Then the check for JAVA_HOME would return + //true although the runtime library may not be loadable. + //Or the JAVA_HOME directory of a deinstalled JRE left behind. + if (ret == javaPluginError::NONE && *exist) + { + OUString sRuntimeLib = getRuntimeLib(pInfo->arVendorData); + JFW_TRACE2("Checking existence of Java runtime library"); + + ::osl::DirectoryItem itemRt; + ::osl::File::RC rc_itemRt = ::osl::DirectoryItem::get(sRuntimeLib, itemRt); + if (::osl::File::E_None == rc_itemRt) + { + *exist = true; + JFW_TRACE2("Java runtime library exist: " << sRuntimeLib); + + // Check version + rtl::Reference<VendorBase> aVendorInfo = getJREInfoByPath(sLocation); + if (!aVendorInfo.is()) + { + *exist = false; + JFW_TRACE2("JRE or supported vendor not accessible at location: " << sLocation); + } + else if(pInfo->sVersion!=aVendorInfo->getVersion()) + { + *exist = false; + JFW_TRACE2("Mismatch between version number in libreoffice settings and installed JRE: " << pInfo->sVersion <<" != " << aVendorInfo->getVersion()); + } + } + else if (::osl::File::E_NOENT == rc_itemRt) + { + *exist = false; + JFW_TRACE2("Java runtime library does not exist: " << sRuntimeLib); + } + else + { + ret = javaPluginError::Error; + JFW_TRACE2("Error while looking for Java runtime library: " << sRuntimeLib); + } + } + return ret; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/sunjre.cxx b/jvmfwk/plugins/sunmajor/pluginlib/sunjre.cxx new file mode 100644 index 000000000..a0f8cbee1 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/sunjre.cxx @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include "sunjre.hxx" +#include "sunversion.hxx" +#include "diagnostics.h" + +namespace jfw_plugin +{ + +rtl::Reference<VendorBase> SunInfo::createInstance() +{ + return new SunInfo; +} + +char const* const* SunInfo::getJavaExePaths(int * size) +{ + static char const * ar[] = { +#if defined(_WIN32) + "java.exe", + "bin/java.exe", + "jre/bin/java.exe" +#elif defined UNX + "java", + "bin/java", + "jre/bin/java" +#endif + }; + *size = SAL_N_ELEMENTS(ar); + return ar; +} + +char const* const* SunInfo::getRuntimePaths(int * size) +{ + static char const* ar[]= { +#if defined(_WIN32) + "/bin/client/jvm.dll", + "/bin/hotspot/jvm.dll", + "/bin/classic/jvm.dll", + "/bin/jrockit/jvm.dll", + // The 64-bit JRE has the jvm in bin/server + "/bin/server/jvm.dll" +#elif defined MACOSX + // Oracle Java 7, under /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home + "/lib/server/libjvm.dylib", + "/lib/jli/libjli.dylib" +#elif defined UNX + "/lib/" JFW_PLUGIN_ARCH "/client/libjvm.so", + "/lib/" JFW_PLUGIN_ARCH "/server/libjvm.so", + "/lib/" JFW_PLUGIN_ARCH "/classic/libjvm.so", + "/lib/" JFW_PLUGIN_ARCH "/jrockit/libjvm.so", + "/lib/server/libjvm.so" +#endif + }; + *size = SAL_N_ELEMENTS(ar); + return ar; +} + +char const* const* SunInfo::getLibraryPaths(int* size) +{ +#if defined UNX && !defined MACOSX + static char const * ar[] = { + "/lib/" JFW_PLUGIN_ARCH "/client", + "/lib/" JFW_PLUGIN_ARCH "/server", + "/lib/" JFW_PLUGIN_ARCH "/native_threads", + ("/lib/" JFW_PLUGIN_ARCH) + }; + *size = SAL_N_ELEMENTS(ar); + return ar; +#else + *size = 0; + return nullptr; +#endif +} + +int SunInfo::compareVersions(const OUString& sSecond) const +{ + OUString sFirst = getVersion(); + + SunVersion version1(sFirst); + JFW_ENSURE(version1, "[Java framework] sunjavaplugin" SAL_DLLEXTENSION + " does not know the version: " + + sFirst + " as valid for a SUN/Oracle JRE."); + SunVersion version2(sSecond); + if ( ! version2) + throw MalformedVersionException(); + + if (version1 == version2) + return 0; + if (version1 > version2) + return 1; + else + return -1; +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/sunjre.hxx b/jvmfwk/plugins/sunmajor/pluginlib/sunjre.hxx new file mode 100644 index 000000000..773f40080 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/sunjre.hxx @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_SUNJRE_HXX +#define INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_SUNJRE_HXX + +#include <vendorbase.hxx> + +namespace jfw_plugin +{ +class SunInfo : public VendorBase +{ +public: + static char const* const* getJavaExePaths(int* size); + + static rtl::Reference<VendorBase> createInstance(); + + virtual char const* const* getRuntimePaths(int* size) override; + virtual char const* const* getLibraryPaths(int* size) override; + + virtual int compareVersions(const OUString& sSecond) const override; +}; +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/sunversion.cxx b/jvmfwk/plugins/sunmajor/pluginlib/sunversion.cxx new file mode 100644 index 000000000..16a1e14f3 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/sunversion.cxx @@ -0,0 +1,303 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include "sunversion.hxx" +#include <osl/thread.h> +#include <rtl/character.hxx> +#include <rtl/ustring.hxx> +#include <string.h> +namespace jfw_plugin { //stoc_javadetect + + +SunVersion::SunVersion(std::u16string_view usVer): + m_nUpdateSpecial(0), m_preRelease(Rel_NONE) +{ + OString sVersion= OUStringToOString(usVer, osl_getThreadTextEncoding()); + m_bValid = init(sVersion.getStr()); +} +SunVersion::SunVersion(const char * szVer): + m_nUpdateSpecial(0), m_preRelease(Rel_NONE) +{ + m_bValid = init(szVer); +} + + +/**Format major.minor.maintenance_update + */ +bool SunVersion::init(const char *szVersion) +{ + if (!szVersion || szVersion[0] == '\0') + return false; + + //first get the major,minor,maintenance + const char * pLast = szVersion; + const char * pCur = szVersion; + //pEnd point to the position after the last character + const char * pEnd = szVersion + strlen(szVersion); + // 0 = major, 1 = minor, 2 = maintenance, 3 = update + int nPart = 0; + // position within part beginning with 0 + int nPartPos = 0; + char buf[128]; + + //char must me a number 0 - 999 and no leading + while (true) + { + if (pCur < pEnd && rtl::isAsciiDigit(static_cast<unsigned char>(*pCur))) + { + pCur ++; + nPartPos ++; + } + //if correct separator then form integer + else if ( + (nPartPos != 0) // prevents: ".4.1", "..1", part must start with digit + && ( + //separators after maintenance (1.4.1_01, 1.4.1-beta, or 1.4.1) + (pCur == pEnd || *pCur == '_' || *pCur == '-') + || + //separators between major-minor and minor-maintenance (or fourth segment) + (nPart < 3 && *pCur == '.') ) + && ( + //prevent 1.4.0. 1.4.0- + pCur + 1 != pEnd + || rtl::isAsciiDigit(static_cast<unsigned char>(*pCur))) ) + { + bool afterMaint = pCur == pEnd || *pCur == '_' || *pCur == '-'; + + int len = pCur - pLast; + if (len >= 127) + return false; + + strncpy(buf, pLast, len); + buf[len] = 0; + pCur ++; + pLast = pCur; + + m_arVersionParts[nPart] = atoi(buf); + + if (afterMaint) + nPart = 2; + nPart ++; + nPartPos = 0; + if (nPart == 3) + break; + + //check next character + if (! ( (pCur < pEnd) + && ( (nPart < 3) + && rtl::isAsciiDigit( + static_cast<unsigned char>(*pCur))))) + return false; + } + else + { + return false; + } + } + if (pCur >= pEnd) + return true; + //We have now 1.4.1. This can be followed by _01 (or a fourth segment .1), -beta, etc. + // _01 (update) According to docu must not be followed by any other + //characters, but on Solaris 9 we have a 1.4.1_01a!! + if (* (pCur - 1) == '_' || *(pCur - 1) == '.') + {// _01, _02 + // update is the last part _01, _01a, part 0 is the digits parts and 1 the trailing alpha + while (true) + { + if (pCur <= pEnd) + { + if ( ! rtl::isAsciiDigit(static_cast<unsigned char>(*pCur))) + { + //1.8.0_102-, 1.8.0_01a, + size_t len = pCur - pLast; + if (len > sizeof(buf) - 1) + return false; + //we've got the update: 01, 02 etc + strncpy(buf, pLast, len); + buf[len] = 0; + m_arVersionParts[nPart] = atoi(buf); + if (pCur == pEnd) + { + break; + } + if (*pCur == 'a' && (pCur + 1) == pEnd) + { + //check if it s followed by a simple "a" (not specified) + m_nUpdateSpecial = *pCur; + break; + } + else if (*pCur == '-' && pCur < pEnd) + { + //check 1.5.0_01-ea + PreRelease pr = getPreRelease(++pCur); + if (pr == Rel_NONE) + return false; + //just ignore -ea because its no official release + break; + } + else + { + return false; + } + } + if (pCur < pEnd) + pCur ++; + else + break; + } + } + } + // 1.4.1-ea + else if (*(pCur - 1) == '-') + { + m_preRelease = getPreRelease(pCur); + if (m_preRelease == Rel_NONE) + return false; +#if defined(FREEBSD) + if (m_preRelease == Rel_FreeBSD) + { + pCur++; //eliminate 'p' + if (pCur < pEnd + && rtl::isAsciiDigit(static_cast<unsigned char>(*pCur))) + pCur ++; + int len = pCur - pLast -1; //eliminate 'p' + if (len >= 127) + return false; + strncpy(buf, (pLast+1), len); //eliminate 'p' + buf[len] = 0; + m_nUpdateSpecial = atoi(buf)+100; //hack for FBSD #i56953# + return true; + } +#endif + } + else + { + return false; + } + return true; +} + +SunVersion::PreRelease SunVersion::getPreRelease(const char *szRelease) +{ + if (szRelease == nullptr) + return Rel_NONE; + if( ! strcmp(szRelease,"internal")) + return Rel_INTERNAL; + else if( ! strcmp(szRelease,"ea")) + return Rel_EA; + else if( ! strcmp(szRelease,"ea1")) + return Rel_EA1; + else if( ! strcmp(szRelease,"ea2")) + return Rel_EA2; + else if( ! strcmp(szRelease,"ea3")) + return Rel_EA3; + else if ( ! strcmp(szRelease,"beta")) + return Rel_BETA; + else if ( ! strcmp(szRelease,"beta1")) + return Rel_BETA1; + else if ( ! strcmp(szRelease,"beta2")) + return Rel_BETA2; + else if ( ! strcmp(szRelease,"beta3")) + return Rel_BETA3; + else if (! strcmp(szRelease, "rc")) + return Rel_RC; + else if (! strcmp(szRelease, "rc1")) + return Rel_RC1; + else if (! strcmp(szRelease, "rc2")) + return Rel_RC2; + else if (! strcmp(szRelease, "rc3")) + return Rel_RC3; +#if defined (FREEBSD) + else if (! strncmp(szRelease, "p", 1)) + return Rel_FreeBSD; +#endif + else + return Rel_NONE; +} + +/* Examples: + a) 1.0 < 1.1 + b) 1.0 < 1.0.0 + c) 1.0 < 1.0_00 + + returns false if both values are equal +*/ +bool SunVersion::operator > (const SunVersion& ver) const +{ + if( &ver == this) + return false; + + //compare major.minor.maintenance + for( int i= 0; i < 4; i ++) + { + // 1.4 > 1.3 + if(m_arVersionParts[i] > ver.m_arVersionParts[i]) + { + return true; + } + else if (m_arVersionParts[i] < ver.m_arVersionParts[i]) + { + return false; + } + } + //major.minor.maintenance_update are equal. Test for a trailing char + if (m_nUpdateSpecial > ver.m_nUpdateSpecial) + { + return true; + } + + //Until here the versions are equal + //compare pre -release values + if ((m_preRelease == Rel_NONE && ver.m_preRelease == Rel_NONE) + || + (m_preRelease != Rel_NONE && ver.m_preRelease == Rel_NONE)) + return false; + else if (m_preRelease == Rel_NONE && ver.m_preRelease != Rel_NONE) + return true; + else if (m_preRelease > ver.m_preRelease) + return true; + + return false; +} + +bool SunVersion::operator < (const SunVersion& ver) const +{ + return (! operator > (ver)) && (! operator == (ver)); +} + +bool SunVersion::operator == (const SunVersion& ver) const +{ + bool bRet= true; + for(int i= 0; i < 4; i++) + { + if( m_arVersionParts[i] != ver.m_arVersionParts[i]) + { + bRet= false; + break; + } + } + bRet = m_nUpdateSpecial == ver.m_nUpdateSpecial && bRet; + bRet = m_preRelease == ver.m_preRelease && bRet; + return bRet; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/sunversion.hxx b/jvmfwk/plugins/sunmajor/pluginlib/sunversion.hxx new file mode 100644 index 000000000..58173b74f --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/sunversion.hxx @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_SUNVERSION_HXX +#define INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_SUNVERSION_HXX + +#include <sal/config.h> + +#include <string_view> + +namespace jfw_plugin +{ +/* SunVersion is used to compare java versions based on a string, as taken + from the registry. The strings look like "1.3", "1.3.1", "1.3.1_02" etc. + Versions such as "1.4.1_01a" are allowed although this is not specified. + 1.4.1_01 < 1.4.1_01a < 1.4.1_01b < 1.4.1_02 + Pre - release versions, such as 1.4.1-ea, 1.4.1-beta, 1.4.1-rc are recognized, + but are treated as minor to release versions: + 1.4.0 > 1.4.2-beta + Pre releases relate this way + 1.4.1-ea < 1.4.1-beta < 1.4.1-rc1 + + This class supports also a FreeBSD Java. This is currently necessary because + it also has the vendor string "Sun Microsystems Inc.". + + An object acts as holder for the version string. That string may be present + even if the version could not be parsed. Then the version may not be compatible + to a SUN Java version. + + An invalid object, that is, operator bool returns false, will always be + the lower version in a comparison. If two invalid objects are compared + then they are considered equal. + + To test if the version is ok, that is this object can be compared to others, + use the bool conversion operator. + */ +class SunVersion final +{ + enum PreRelease + { + Rel_NONE, + Rel_INTERNAL, + Rel_EA, + Rel_EA1, + Rel_EA2, + Rel_EA3, + Rel_BETA, + Rel_BETA1, + Rel_BETA2, + Rel_BETA3, + Rel_RC, + Rel_RC1, + Rel_RC2, + Rel_RC3 +#if defined(FREEBSD) + , + Rel_FreeBSD +#endif + }; + + //contains major,minor,micro,update + int m_arVersionParts[4] = {}; + // The update can be followed by a char, e.g. 1.4.1_01a + char m_nUpdateSpecial; + + PreRelease m_preRelease; + +public: + explicit SunVersion(const char* szVer); + explicit SunVersion(std::u16string_view usVer); + + /** + Pre-release versions are taken into account. + 1.5.0-beta > 1.5.0-ea > 1.4.2 + */ + bool operator>(const SunVersion& ver) const; + bool operator<(const SunVersion& ver) const; + bool operator==(const SunVersion& ver) const; + + /** Test if the version is compatible tu SUN's versioning scheme + */ + operator bool() { return m_bValid; } + +private: + bool init(const char* szVer); + + bool m_bValid; + + /* Determines if a string constitutes a pre release. For example, if + "ea" is passed then Rel_EA is returned. If the string is no pre release + then Rel_NONE is returned. + */ + static PreRelease getPreRelease(const char* szRel); +}; +} + +#endif // INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_SUNVERSION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/util.cxx b/jvmfwk/plugins/sunmajor/pluginlib/util.cxx new file mode 100644 index 000000000..6eb96d731 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/util.cxx @@ -0,0 +1,1281 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_folders.h> + +#include "util.hxx" + +#include <osl/process.h> +#include <osl/file.hxx> +#include <osl/module.hxx> +#include <osl/diagnose.h> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <salhelper/linkhelper.hxx> +#include <salhelper/thread.hxx> +#include <o3tl/string_view.hxx> +#include <memory> +#include <utility> +#include <algorithm> +#include <map> +#include <string_view> + +#if defined(_WIN32) +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#endif +#include <string.h> + +#include "sunjre.hxx" +#include "vendorlist.hxx" +#include "diagnostics.h" +#if defined MACOSX && defined __x86_64__ +#include "util_cocoa.hxx" +#endif + +using namespace osl; + +using ::rtl::Reference; + +#ifdef _WIN32 +#define HKEY_SUN_JRE L"Software\\JavaSoft\\Java Runtime Environment" +#define HKEY_SUN_SDK L"Software\\JavaSoft\\Java Development Kit" +#endif + +#if defined( UNX ) && !defined( MACOSX ) +namespace { +char const *g_arJavaNames[] = { + "", + "j2re", + "j2se", + "j2sdk", + "jdk", + "jre", + "java" +}; + +/* These are directory names which could contain multiple java installations. + */ +char const *g_arCollectDirs[] = { + "", +#ifndef JVM_ONE_PATH_CHECK + "j2re/", + "j2se/", + "j2sdk/", + "jdk/", + "jre/", + "java/", +#endif + "jvm/" +}; + +/* These are directories in which a java installation is + looked for. +*/ +char const *g_arSearchPaths[] = { +#ifndef JVM_ONE_PATH_CHECK + "", + "usr/", + "usr/local/", +#ifdef X86_64 + "usr/lib64/", +#endif + "usr/lib/", + "usr/bin/" +#else + JVM_ONE_PATH_CHECK +#endif +}; +} +#endif // UNX && !MACOSX + +namespace jfw_plugin +{ +#if defined(_WIN32) +static bool getSDKInfoFromRegistry(std::vector<OUString> & vecHome); +static bool getJREInfoFromRegistry(std::vector<OUString>& vecJavaHome); +#endif + +static bool decodeOutput(std::string_view s, OUString* out); + + +namespace +{ + +bool addJREInfo( + rtl::Reference<VendorBase> const & info, + std::vector<rtl::Reference<VendorBase>> & infos) +{ + if (std::none_of(infos.begin(), infos.end(), InfoFindSame(info->getHome()))) { + infos.push_back(info); + return true; + } else { + return false; + } +} + +bool getAndAddJREInfoByPath( + const OUString& path, + std::vector<rtl::Reference<VendorBase> > & allInfos, + std::vector<rtl::Reference<VendorBase> > & addedInfos) +{ + rtl::Reference<VendorBase> aInfo = getJREInfoByPath(path); + if (aInfo.is()) { + if (addJREInfo(aInfo, allInfos)) { + addedInfos.push_back(aInfo); + } + return true; + } else { + return false; + } +} + +} + +namespace { + +class FileHandleGuard +{ +public: + explicit FileHandleGuard(oslFileHandle & rHandle): + m_rHandle(rHandle) {} + + inline ~FileHandleGuard(); + + FileHandleGuard(const FileHandleGuard&) = delete; + FileHandleGuard& operator=(const FileHandleGuard&) = delete; + + oslFileHandle & getHandle() { return m_rHandle; } + +private: + oslFileHandle & m_rHandle; +}; + +} + +inline FileHandleGuard::~FileHandleGuard() +{ + if (m_rHandle != nullptr) + { + if (osl_closeFile(m_rHandle) != osl_File_E_None) + { + OSL_FAIL("unexpected situation"); + } + } +} + +namespace { + +class FileHandleReader +{ +public: + enum Result + { + RESULT_OK, + RESULT_EOF, + RESULT_ERROR + }; + + explicit FileHandleReader(oslFileHandle & rHandle): + m_aGuard(rHandle), m_nSize(0), m_nIndex(0), m_bLf(false) {} + + Result readLine(OString * pLine); + +private: + enum { BUFFER_SIZE = 1024 }; + + char m_aBuffer[BUFFER_SIZE]; + FileHandleGuard m_aGuard; + int m_nSize; + int m_nIndex; + bool m_bLf; +}; + +} + +FileHandleReader::Result +FileHandleReader::readLine(OString * pLine) +{ + OSL_ENSURE(pLine, "specification violation"); + + for (bool bEof = true;; bEof = false) + { + if (m_nIndex == m_nSize) + { + sal_uInt64 nRead = 0; + switch (osl_readFile( + m_aGuard.getHandle(), m_aBuffer, sizeof(m_aBuffer), &nRead)) + { + case osl_File_E_PIPE: //HACK! for windows + nRead = 0; + [[fallthrough]]; + case osl_File_E_None: + if (nRead == 0) + { + m_bLf = false; + return bEof ? RESULT_EOF : RESULT_OK; + } + m_nIndex = 0; + m_nSize = static_cast< int >(nRead); + break; + case osl_File_E_INTR: + continue; + + default: + return RESULT_ERROR; + } + } + + if (m_bLf && m_aBuffer[m_nIndex] == 0x0A) + ++m_nIndex; + m_bLf = false; + + int nStart = m_nIndex; + while (m_nIndex != m_nSize) + switch (m_aBuffer[m_nIndex++]) + { + case 0x0D: + m_bLf = true; + [[fallthrough]]; + case 0x0A: + *pLine += std::string_view(m_aBuffer + nStart, + m_nIndex - 1 - nStart); + //TODO! check for overflow, and not very efficient + return RESULT_OK; + } + + *pLine += std::string_view(m_aBuffer + nStart, m_nIndex - nStart); + //TODO! check for overflow, and not very efficient + } +} + +namespace { + +class AsynchReader: public salhelper::Thread +{ + size_t m_nDataSize; + std::unique_ptr<char[]> m_arData; + + FileHandleGuard m_aGuard; + + virtual ~AsynchReader() override {} + + void execute() override; +public: + + explicit AsynchReader(oslFileHandle & rHandle); + + /** only call this function after this thread has finished. + + That is, call join on this instance and then call getData. + + */ + OString getData(); +}; + +} + +AsynchReader::AsynchReader(oslFileHandle & rHandle): + Thread("jvmfwkAsyncReader"), m_nDataSize(0), + m_aGuard(rHandle) +{ +} + +OString AsynchReader::getData() +{ + return OString(m_arData.get(), m_nDataSize); +} + +void AsynchReader::execute() +{ + const sal_uInt64 BUFFER_SIZE = 4096; + char aBuffer[BUFFER_SIZE]; + while (true) + { + sal_uInt64 nRead; + //the function blocks until something could be read or the pipe closed. + switch (osl_readFile( + m_aGuard.getHandle(), aBuffer, BUFFER_SIZE, &nRead)) + { + case osl_File_E_PIPE: //HACK! for windows + nRead = 0; + [[fallthrough]]; + case osl_File_E_None: + break; + default: + return; + } + + if (nRead == 0) + { + break; + } + else if (nRead <= BUFFER_SIZE) + { + //Save the data we have in m_arData into a temporary array + std::unique_ptr<char[]> arTmp( new char[m_nDataSize]); + if (m_nDataSize != 0) { + memcpy(arTmp.get(), m_arData.get(), m_nDataSize); + } + //Enlarge m_arData to hold the newly read data + m_arData.reset(new char[static_cast<size_t>(m_nDataSize + nRead)]); + //Copy back the data that was already in m_arData + memcpy(m_arData.get(), arTmp.get(), m_nDataSize); + //Add the newly read data to m_arData + memcpy(m_arData.get() + m_nDataSize, aBuffer, static_cast<size_t>(nRead)); + m_nDataSize += static_cast<size_t>(nRead); + } + } +} + +bool getJavaProps(const OUString & exePath, +#ifdef JVM_ONE_PATH_CHECK + const OUString & homePath, +#endif + std::vector<std::pair<OUString, OUString> >& props, + bool * bProcessRun) +{ + bool ret = false; + + OSL_ASSERT(!exePath.isEmpty()); + OUString usStartDir; + //We need to set the CLASSPATH in case the office is started from + //a different directory. The JREProperties.class is expected to reside + //next to the plugin, except on macOS where it is in ../Resources/java relative + //to the plugin. + OUString sThisLib; + if (!osl_getModuleURLFromAddress(reinterpret_cast<void *>(&getJavaProps), + & sThisLib.pData)) + { + return false; + } + sThisLib = getDirFromFile(sThisLib); + OUString sClassPath; + if (osl_getSystemPathFromFileURL(sThisLib.pData, & sClassPath.pData) + != osl_File_E_None) + { + return false; + } + +#ifdef MACOSX +#if defined __x86_64__ + if (!JvmfwkUtil_isLoadableJVM(exePath)) + return false; +#endif + if (sClassPath.endsWith("/")) + sClassPath += "../Resources/java/"; + else + sClassPath += "/../Resources/java"; +#endif + + //prepare the arguments + sal_Int32 const cArgs = 3; + OUString arg1 = "-classpath";// + sClassPath; + OUString arg2 = sClassPath; + OUString arg3("JREProperties"); + rtl_uString *args[cArgs] = {arg1.pData, arg2.pData, arg3.pData}; + + oslProcess javaProcess= nullptr; + oslFileHandle fileOut= nullptr; + oslFileHandle fileErr= nullptr; + + FileHandleReader stdoutReader(fileOut); + rtl::Reference< AsynchReader > stderrReader(new AsynchReader(fileErr)); + + JFW_TRACE2("Executing: " + exePath); + oslProcessError procErr = + osl_executeProcess_WithRedirectedIO( exePath.pData,//usExe.pData, + args, + cArgs, //sal_uInt32 nArguments, + osl_Process_HIDDEN, //oslProcessOption Options, + nullptr, //oslSecurity Security, + usStartDir.pData,//usStartDir.pData,//usWorkDir.pData, //rtl_uString *strWorkDir, + nullptr, //rtl_uString *strEnvironment[], + 0, // sal_uInt32 nEnvironmentVars, + &javaProcess, //oslProcess *pProcess, + nullptr,//oslFileHandle *pChildInputWrite, + &fileOut,//oslFileHandle *pChildOutputRead, + &fileErr);//oslFileHandle *pChildErrorRead); + + if( procErr != osl_Process_E_None) + { + JFW_TRACE2("Execution failed"); + *bProcessRun = false; + SAL_WARN("jfw", + "osl_executeProcess failed (" << ret << "): \"" << exePath << "\""); + return ret; + } + else + { + JFW_TRACE2("Java executed successfully"); + *bProcessRun = true; + } + + //Start asynchronous reading (different thread) of error stream + stderrReader->launch(); + + //Use this thread to read output stream + FileHandleReader::Result rs = FileHandleReader::RESULT_OK; + JFW_TRACE2("Properties found:"); + while (true) + { + OString aLine; + rs = stdoutReader.readLine( & aLine); + if (rs != FileHandleReader::RESULT_OK) + break; + OUString sLine; + if (!decodeOutput(aLine, &sLine)) + continue; + JFW_TRACE2(" \"" << sLine << "\""); + sLine = sLine.trim(); + if (sLine.isEmpty()) + continue; + //The JREProperties class writes key value pairs, separated by '=' + sal_Int32 index = sLine.indexOf('='); + OSL_ASSERT(index != -1); + OUString sKey = sLine.copy(0, index); + OUString sVal = sLine.copy(index + 1); + +#ifdef JVM_ONE_PATH_CHECK + //replace absolute path by linux distro link + OUString sHomeProperty("java.home"); + if(sHomeProperty.equals(sKey)) + { + sVal = homePath + "/jre"; + } +#endif + + props.emplace_back(sKey, sVal); + } + + if (rs != FileHandleReader::RESULT_ERROR && !props.empty()) + ret = true; + + //process error stream data + stderrReader->join(); + JFW_TRACE2("Java wrote to stderr:\" " + << stderrReader->getData() << " \""); + + TimeValue waitMax= {5 ,0}; + procErr = osl_joinProcessWithTimeout(javaProcess, &waitMax); + OSL_ASSERT(procErr == osl_Process_E_None); + osl_freeProcessHandle(javaProcess); + return ret; +} + +/* converts the properties printed by JREProperties.class into + readable strings. The strings are encoded as integer values separated + by spaces. + */ +bool decodeOutput(std::string_view s, OUString* out) +{ + OSL_ASSERT(out != nullptr); + OUStringBuffer buff(512); + sal_Int32 nIndex = 0; + do + { + std::string_view aToken = o3tl::getToken(s, 0, ' ', nIndex ); + if (!aToken.empty()) + { + for (size_t i = 0; i < aToken.size(); ++i) + { + if (aToken[i] < '0' || aToken[i] > '9') + return false; + } + sal_Unicode value = static_cast<sal_Unicode>(o3tl::toInt32(aToken)); + buff.append(value); + } + } while (nIndex >= 0); + + *out = buff.makeStringAndClear(); + return true; +} + + +#if defined(_WIN32) + +static bool getJavaInfoFromRegistry(const wchar_t* szRegKey, + std::vector<OUString>& vecJavaHome) +{ + HKEY hRoot; + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_ENUMERATE_SUB_KEYS, &hRoot) + == ERROR_SUCCESS) + { + DWORD dwIndex = 0; + const DWORD BUFFSIZE = 1024; + wchar_t bufVersion[BUFFSIZE]; + FILETIME fileTime; + DWORD nNameLen = sizeof(bufVersion); + + // Iterate over all subkeys of HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment + while (RegEnumKeyExW(hRoot, dwIndex, bufVersion, &nNameLen, nullptr, nullptr, nullptr, &fileTime) != ERROR_NO_MORE_ITEMS) + { + HKEY hKey; + // Open a Java Runtime Environment sub key, e.g. "1.4.0" + if (RegOpenKeyExW(hRoot, bufVersion, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) + { + DWORD dwType; + DWORD dwTmpPathLen= 0; + // Get the path to the JavaHome every JRE entry + // Find out how long the string for JavaHome is and allocate memory to hold the path + if( RegQueryValueExW(hKey, L"JavaHome", nullptr, &dwType, nullptr, &dwTmpPathLen)== ERROR_SUCCESS) + { + unsigned char* szTmpPath= static_cast<unsigned char *>(malloc(dwTmpPathLen+sizeof(sal_Unicode))); + // According to https://msdn.microsoft.com/en-us/ms724911, the application should ensure + // that the string is properly terminated before using it + for (DWORD i = 0; i < sizeof(sal_Unicode); ++i) + szTmpPath[dwTmpPathLen + i] = 0; + // Get the path for the runtime lib + if(RegQueryValueExW(hKey, L"JavaHome", nullptr, &dwType, szTmpPath, &dwTmpPathLen) == ERROR_SUCCESS) + { + // There can be several version entries referring with the same JavaHome,e.g 1.4 and 1.4.1 + OUString usHome(reinterpret_cast<sal_Unicode*>(szTmpPath)); + // check if there is already an entry with the same JavaHomeruntime lib + // if so, we use the one with the more accurate version + OUString usHomeUrl; + if (osl_getFileURLFromSystemPath(usHome.pData, & usHomeUrl.pData) == + osl_File_E_None) + { + bool bAppend= true; + //iterate over the vector with java home strings + for (auto const& javaHome : vecJavaHome) + { + if(usHomeUrl.equals(javaHome)) + { + bAppend= false; + break; + } + } + // Save the home dir + if(bAppend) + { + vecJavaHome.push_back(usHomeUrl); + } + } + } + free( szTmpPath); + RegCloseKey(hKey); + } + } + dwIndex ++; + nNameLen = BUFFSIZE; + } + RegCloseKey(hRoot); + } + return true; +} + + +bool getSDKInfoFromRegistry(std::vector<OUString> & vecHome) +{ + return getJavaInfoFromRegistry(HKEY_SUN_SDK, vecHome); +} + +bool getJREInfoFromRegistry(std::vector<OUString>& vecJavaHome) +{ + return getJavaInfoFromRegistry(HKEY_SUN_JRE, vecJavaHome); +} + +static void addJavaInfoFromWinReg( + std::vector<rtl::Reference<VendorBase> > & allInfos, + std::vector<rtl::Reference<VendorBase> > & addedInfos) +{ + // Get Java s from registry + std::vector<OUString> vecJavaHome; + if(getSDKInfoFromRegistry(vecJavaHome)) + { + // create impl objects + for (auto const& javaHome : vecJavaHome) + { + getAndAddJREInfoByPath(javaHome, allInfos, addedInfos); + } + } + + vecJavaHome.clear(); + if(getJREInfoFromRegistry(vecJavaHome)) + { + for (auto const& javaHome : vecJavaHome) + { + getAndAddJREInfoByPath(javaHome, allInfos, addedInfos); + } + } + + vecJavaHome.clear(); + if (getJavaInfoFromRegistry(L"Software\\JavaSoft\\JDK", vecJavaHome)) { + for (auto const & javaHome: vecJavaHome) { + getAndAddJREInfoByPath(javaHome, allInfos, addedInfos); + } + } + + vecJavaHome.clear(); + if (getJavaInfoFromRegistry(L"Software\\JavaSoft\\JRE", vecJavaHome)) { + for (auto const & javaHome: vecJavaHome) { + getAndAddJREInfoByPath(javaHome, allInfos, addedInfos); + } + } +} + +#endif // _WIN32 + +void bubbleSortVersion(std::vector<rtl::Reference<VendorBase> >& vec) +{ + if(vec.empty()) + return; + int size= vec.size() - 1; + int cIter= 0; + // sort for version + for(int i= 0; i < size; i++) + { + for(int j= size; j > 0 + cIter; j--) + { + rtl::Reference<VendorBase>& cur= vec.at(j); + rtl::Reference<VendorBase>& next= vec.at(j-1); + + int nCmp = 0; + // comparing invalid SunVersion s is possible, they will be less than a + // valid version + + //check if version of current is recognized, by comparing it with itself + try + { + (void)cur->compareVersions(cur->getVersion()); + } + catch (MalformedVersionException &) + { + nCmp = -1; // current < next + } + //The version of cur is valid, now compare with the second version + if (nCmp == 0) + { + try + { + nCmp = cur->compareVersions(next->getVersion()); + } + catch (MalformedVersionException & ) + { + //The second version is invalid, therefore it regards less. + nCmp = 1; + } + } + if(nCmp == 1) // cur > next + { + std::swap(cur, next); + } + } + ++cIter; + } +} + + +void addJREInfoFromBinPath( + const OUString& path, std::vector<rtl::Reference<VendorBase>> & allInfos, + std::vector<rtl::Reference<VendorBase>> & addedInfos) +{ + // file:///c:/jre/bin + //map: jre/bin/java.exe + + for ( sal_Int32 pos = 0; + gVendorMap[pos].sVendorName != nullptr; ++pos ) + { + std::vector<OUString> vecPaths; + getJavaExePaths_func pFunc = gVendorMap[pos].getJavaFunc; + + int size = 0; + char const* const* arExePaths = (*pFunc)(&size); + vecPaths = getVectorFromCharArray(arExePaths, size); + + //make sure argument path does not end with '/' + OUString sBinPath = path; + if (path.endsWith("/")) + sBinPath = path.copy(0, path.getLength() - 1); + + for (auto const& looppath : vecPaths) + { + //the map contains e.g. jre/bin/java.exe + //get the directory where the executable is contained + OUString sHome; + sal_Int32 index = looppath.lastIndexOf('/'); + if (index == -1) + { + //map contained only : "java.exe, then the argument + //path is already the home directory + sHome = sBinPath; + } + else + { + // jre/bin/jre -> jre/bin + OUString sMapPath = looppath.copy(0, index); + index = sBinPath.lastIndexOf(sMapPath); + if (index != -1 + && (index + sMapPath.getLength() == sBinPath.getLength()) + && sBinPath[index - 1] == '/') + { + sHome = sBinPath.copy(index - 1); + } + } + if (!sHome.isEmpty() + && getAndAddJREInfoByPath(path, allInfos, addedInfos)) + { + return; + } + } + } +} + +std::vector<Reference<VendorBase> > addAllJREInfos( + bool checkJavaHomeAndPath, + std::vector<rtl::Reference<VendorBase>> & allInfos) +{ + std::vector<Reference<VendorBase> > addedInfos; + +#if defined(_WIN32) + // Get Javas from the registry + addJavaInfoFromWinReg(allInfos, addedInfos); +#endif // _WIN32 + + if (checkJavaHomeAndPath) { + addJavaInfoFromJavaHome(allInfos, addedInfos); + //this function should be called after addJavaInfosDirScan. + //Otherwise in SDKs Java may be started twice + addJavaInfosFromPath(allInfos, addedInfos); + } + +#ifdef UNX + addJavaInfosDirScan(allInfos, addedInfos); +#endif + + bubbleSortVersion(addedInfos); + return addedInfos; +} + + +std::vector<OUString> getVectorFromCharArray(char const * const * ar, int size) +{ + std::vector<OUString> vec; + for( int i = 0; i < size; i++) + { + OUString s(ar[i], strlen(ar[i]), RTL_TEXTENCODING_UTF8); + vec.push_back(s); + } + return vec; +} + +/** Checks if the path is a directory. Links are resolved. + In case of an error the returned string has the length 0. + Otherwise the returned string is the "resolved" file URL. + */ +static OUString resolveDirPath(const OUString & path) +{ + OUString ret; + salhelper::LinkResolver aResolver(osl_FileStatus_Mask_Type | + osl_FileStatus_Mask_FileURL); + if (aResolver.fetchFileStatus(path) == osl::FileBase::E_None) + { + //check if this is a directory + if (aResolver.m_aStatus.getFileType() == FileStatus::Directory) + { +#ifndef JVM_ONE_PATH_CHECK + ret = aResolver.m_aStatus.getFileURL(); +#else + ret = path; +#endif + } + } + return ret; +} +/** Checks if the path is a file. If it is a link to a file than + it is resolved. + */ +static OUString resolveFilePath(const OUString & path) +{ + OUString ret; + salhelper::LinkResolver aResolver(osl_FileStatus_Mask_Type | + osl_FileStatus_Mask_FileURL); + if (aResolver.fetchFileStatus(path) == osl::FileBase::E_None) + { + //check if this is a file + if (aResolver.m_aStatus.getFileType() == FileStatus::Regular) + { +#ifndef JVM_ONE_PATH_CHECK + ret = aResolver.m_aStatus.getFileURL(); +#else + ret = path; +#endif + } + } + return ret; +} + +rtl::Reference<VendorBase> getJREInfoByPath( + const OUString& path) +{ + rtl::Reference<VendorBase> ret; + static std::vector<OUString> vecBadPaths; + + static std::map<OUString, rtl::Reference<VendorBase> > mapJREs; + OUString sFilePath; + std::vector<std::pair<OUString, OUString> > props; + + OUString sResolvedDir = resolveDirPath(path); + // If this path is invalid then there is no chance to find a JRE here + if (sResolvedDir.isEmpty()) + { + return nullptr; + } + + //check if the directory path is good, that is a JRE was already recognized. + //Then we need not detect it again + //For example, a sun JDK contains <jdk>/bin/java and <jdk>/jre/bin/java. + //When <jdk>/bin/java has been found then we need not find <jdk>/jre/bin/java. + //Otherwise we would execute java two times for every JDK found. + auto entry2 = find_if(mapJREs.cbegin(), mapJREs.cend(), + SameOrSubDirJREMap(sResolvedDir)); + if (entry2 != mapJREs.end()) + { + JFW_TRACE2("JRE found again (detected before): " << sResolvedDir); + return entry2->second; + } + + for ( sal_Int32 pos = 0; + gVendorMap[pos].sVendorName != nullptr; ++pos ) + { + std::vector<OUString> vecPaths; + getJavaExePaths_func pFunc = gVendorMap[pos].getJavaFunc; + + int size = 0; + char const* const* arExePaths = (*pFunc)(&size); + vecPaths = getVectorFromCharArray(arExePaths, size); + + bool bBreak = false; + for (auto const& looppath : vecPaths) + { + //if the path is a link, then resolve it + //check if the executable exists at all + + //path can be only "file:///". Then do not append a '/' + //sizeof counts the terminating 0 + OUString sFullPath; + if (path.getLength() == sizeof("file:///") - 1) + sFullPath = sResolvedDir + looppath; + else + sFullPath = sResolvedDir + "/" + looppath; + + sFilePath = resolveFilePath(sFullPath); + + if (sFilePath.isEmpty()) + { + //The file path (to java exe) is not valid + auto ifull = find(vecBadPaths.cbegin(), vecBadPaths.cend(), sFullPath); + if (ifull == vecBadPaths.cend()) + { + vecBadPaths.push_back(sFullPath); + } + continue; + } + + auto ifile = find(vecBadPaths.cbegin(), vecBadPaths.cend(), sFilePath); + if (ifile != vecBadPaths.cend()) + { + continue; + } + + auto entry = mapJREs.find(sFilePath); + if (entry != mapJREs.end()) + { + JFW_TRACE2("JRE found again (detected before): " << sFilePath); + + return entry->second; + } + + bool bProcessRun= false; + if (!getJavaProps(sFilePath, +#ifdef JVM_ONE_PATH_CHECK + sResolvedDir, +#endif + props, & bProcessRun)) + { + //The java executable could not be run or the system properties + //could not be retrieved. We can assume that this java is corrupt. + vecBadPaths.push_back(sFilePath); + //If there was a java executable, that could be run but we did not get + //the system properties, then we also assume that the whole Java installation + //does not work. In a jdk there are two executables. One in jdk/bin and the other + //in jdk/jre/bin. We do not search any further, because we assume that if one java + //does not work then the other does not work as well. This saves us to run java + //again which is quite costly. + if (bProcessRun) + { + // 1.3.1 special treatment: jdk/bin/java and /jdk/jre/bin/java are links to + //a script, named .java_wrapper. The script starts jdk/bin/sparc/native_threads/java + //or jdk/jre/bin/sparc/native_threads/java. The script uses the name with which it was + //invoked to build the path to the executable. It we start the script directly as .java_wrapper + //then it tries to start a jdk/.../native_threads/.java_wrapper. Therefore the link, which + //is named java, must be used to start the script. + getJavaProps(sFullPath, +#ifdef JVM_ONE_PATH_CHECK + sResolvedDir, +#endif + props, & bProcessRun); + // Either we found a working 1.3.1 + // Or the java is broken. In both cases we stop searching under this "root" directory + bBreak = true; + break; + } + //sFilePath is no working java executable. We continue with another possible + //path. + else + { + continue; + } + } + //sFilePath is a java and we could get the system properties. We proceed with this + //java. + else + { + bBreak = true; + break; + } + } + if (bBreak) + break; + } + + if (props.empty()) + { + return rtl::Reference<VendorBase>(); + } + + //find java.vendor property + OUString sVendorName; + + for (auto const& prop : props) + { + if (prop.first == "java.vendor") + { + sVendorName = prop.second; + break; + } + } + + auto knownVendor = false; + if (!sVendorName.isEmpty()) + { + //find the creator func for the respective vendor name + for ( sal_Int32 c = 0; + gVendorMap[c].sVendorName != nullptr; ++c ) + { + OUString sNameMap(gVendorMap[c].sVendorName, strlen(gVendorMap[c].sVendorName), + RTL_TEXTENCODING_ASCII_US); + if (sNameMap == sVendorName) + { + ret = createInstance(gVendorMap[c].createFunc, props); + knownVendor = true; + break; + } + } + } + // For unknown vendors, try SunInfo as fallback: + if (!knownVendor) + { + ret = createInstance(SunInfo::createInstance, props); + } + if (!ret.is()) + { + vecBadPaths.push_back(sFilePath); + } + else + { + JFW_TRACE2("Found JRE: " << sResolvedDir << " at: " << path); + + mapJREs.emplace(sResolvedDir, ret); + mapJREs.emplace(sFilePath, ret); + } + + return ret; +} + +Reference<VendorBase> createInstance(createInstance_func pFunc, + const std::vector<std::pair<OUString, OUString> >& properties) +{ + + Reference<VendorBase> aBase = (*pFunc)(); + if (aBase.is()) + { + if (!aBase->initialize(properties)) + aBase = nullptr; + } + return aBase; +} + +inline OUString getDirFromFile(std::u16string_view usFilePath) +{ + size_t index = usFilePath.rfind('/'); + return OUString(usFilePath.substr(0, index)); +} + +void addJavaInfosFromPath( + std::vector<rtl::Reference<VendorBase>> & allInfos, + std::vector<rtl::Reference<VendorBase>> & addedInfos) +{ +#if !defined JVM_ONE_PATH_CHECK +// Get Java from PATH environment variable + char *szPath= getenv("PATH"); + if(!szPath) + return; + + OUString usAllPath(szPath, strlen(szPath), osl_getThreadTextEncoding()); + sal_Int32 nIndex = 0; + do + { + OUString usToken( usAllPath.getToken( 0, SAL_PATHSEPARATOR, nIndex ) ); + OUString usTokenUrl; + if(File::getFileURLFromSystemPath(usToken, usTokenUrl) == File::E_None) + { + if(!usTokenUrl.isEmpty()) + { + OUString usBin; + if(usTokenUrl == ".") + { + OUString usWorkDirUrl; + if(osl_Process_E_None == osl_getProcessWorkingDir(&usWorkDirUrl.pData)) + usBin= usWorkDirUrl; + } + else if(usTokenUrl == "..") + { + OUString usWorkDir; + if(osl_Process_E_None == osl_getProcessWorkingDir(&usWorkDir.pData)) + usBin= getDirFromFile(usWorkDir); + } + else + { + usBin = usTokenUrl; + } + if(!usBin.isEmpty()) + { + addJREInfoFromBinPath(usBin, allInfos, addedInfos); + } + } + } + } + while ( nIndex >= 0 ); +#endif +} + + +void addJavaInfoFromJavaHome( + std::vector<rtl::Reference<VendorBase>> & allInfos, + std::vector<rtl::Reference<VendorBase>> & addedInfos) +{ +#if !defined JVM_ONE_PATH_CHECK + // Get Java from JAVA_HOME environment + + // Note that on macOS is it not normal at all to have a JAVA_HOME environment + // variable. We set it in our build environment for build-time programs, though, + // so it is set when running unit tests that involve Java functionality. (Which affects + // at least CppunitTest_dbaccess_dialog_save, too, and not only the JunitTest ones.) + char *szJavaHome= getenv("JAVA_HOME"); + if(szJavaHome) + { + OUString sHome(szJavaHome, strlen(szJavaHome), osl_getThreadTextEncoding()); + OUString sHomeUrl; + if(File::getFileURLFromSystemPath(sHome, sHomeUrl) == File::E_None) + { + getAndAddJREInfoByPath(sHomeUrl, allInfos, addedInfos); + } + } +#endif +} + +bool makeDriveLetterSame(OUString * fileURL) +{ + bool ret = false; + DirectoryItem item; + if (DirectoryItem::get(*fileURL, item) == File::E_None) + { + FileStatus status(osl_FileStatus_Mask_FileURL); + if (item.getFileStatus(status) == File::E_None) + { + *fileURL = status.getFileURL(); + ret = true; + } + } + return ret; +} + +#ifdef UNX +#ifdef __sun + +void addJavaInfosDirScan( + std::vector<rtl::Reference<VendorBase>> & allInfos, + std::vector<rtl::Reference<VendorBase>> & addedInfos) +{ + JFW_TRACE2("Checking /usr/jdk/latest"); + getAndAddJREInfoByPath("file:////usr/jdk/latest", allInfos, addedInfos); +} + +#else +void addJavaInfosDirScan( + std::vector<rtl::Reference<VendorBase>> & allInfos, + std::vector<rtl::Reference<VendorBase>> & addedInfos) +{ +#ifdef MACOSX + // Ignore all but Oracle's JDK as loading Apple's Java and Oracle's JRE + // will cause macOS's JavaVM framework to display a dialog and invoke + // exit() when loaded via JNI on macOS 10.10 + Directory aDir("file:///Library/Java/JavaVirtualMachines"); + if (aDir.open() == File::E_None) + { + DirectoryItem aItem; + while (aDir.getNextItem(aItem) == File::E_None) + { + FileStatus aStatus(osl_FileStatus_Mask_FileURL); + if (aItem.getFileStatus(aStatus) == File::E_None) + { + OUString aItemURL( aStatus.getFileURL() ); + if (aItemURL.getLength()) + { + aItemURL += "/Contents/Home"; + if (DirectoryItem::get(aItemURL, aItem) == File::E_None) + getAndAddJREInfoByPath(aItemURL, allInfos, addedInfos); + } + } + } + aDir.close(); + } +#else // MACOSX + OUString excMessage = "[Java framework] sunjavaplugin: " + "Error in function addJavaInfosDirScan in util.cxx."; + int cJavaNames= SAL_N_ELEMENTS(g_arJavaNames); + std::unique_ptr<OUString[]> sarJavaNames(new OUString[cJavaNames]); + OUString *arNames = sarJavaNames.get(); + for(int i= 0; i < cJavaNames; i++) + arNames[i] = OUString(g_arJavaNames[i], strlen(g_arJavaNames[i]), + RTL_TEXTENCODING_UTF8); + + int cSearchPaths= SAL_N_ELEMENTS(g_arSearchPaths); + std::unique_ptr<OUString[]> sarPathNames(new OUString[cSearchPaths]); + OUString *arPaths = sarPathNames.get(); + for(int c = 0; c < cSearchPaths; c++) + arPaths[c] = OUString(g_arSearchPaths[c], strlen(g_arSearchPaths[c]), + RTL_TEXTENCODING_UTF8); + + int cCollectDirs = SAL_N_ELEMENTS(g_arCollectDirs); + std::unique_ptr<OUString[]> sarCollectDirs(new OUString[cCollectDirs]); + OUString *arCollectDirs = sarCollectDirs.get(); + for(int d = 0; d < cCollectDirs; d++) + arCollectDirs[d] = OUString(g_arCollectDirs[d], strlen(g_arCollectDirs[d]), + RTL_TEXTENCODING_UTF8); + + + for( int ii = 0; ii < cSearchPaths; ii ++) + { + OUString usDir1("file:///" + arPaths[ii]); + DirectoryItem item; + if(DirectoryItem::get(usDir1, item) == File::E_None) + { + for(int j= 0; j < cCollectDirs; j++) + { + OUString usDir2(usDir1 + arCollectDirs[j]); + // prevent that we scan the whole /usr, /usr/lib, etc directories + if (!arCollectDirs[j].isEmpty()) + { + //usr/java/xxx + //Examining every subdirectory + Directory aCollectionDir(usDir2); + + Directory::RC openErr = aCollectionDir.open(); + switch (openErr) + { + case File::E_None: + break; + case File::E_NOENT: + case File::E_NOTDIR: + continue; + case File::E_ACCES: + JFW_TRACE2("Could not read directory " << usDir2 << " because of missing access rights"); + continue; + default: + JFW_TRACE2("Could not read directory " << usDir2 << ". Osl file error: " << openErr); + continue; + } + + DirectoryItem curIt; + File::RC errNext = File::E_None; + while( (errNext = aCollectionDir.getNextItem(curIt)) == File::E_None) + { + FileStatus aStatus(osl_FileStatus_Mask_FileURL); + File::RC errStatus = File::E_None; + if ((errStatus = curIt.getFileStatus(aStatus)) != File::E_None) + { + JFW_TRACE2(excMessage + "getFileStatus failed with error " << errStatus); + continue; + } + JFW_TRACE2("Checking if directory: " << aStatus.getFileURL() << " is a Java"); + + getAndAddJREInfoByPath( + aStatus.getFileURL(), allInfos, addedInfos); + } + + JFW_ENSURE(errNext == File::E_None || errNext == File::E_NOENT, + "[Java framework] sunjavaplugin: " + "Error while iterating over contents of " + + usDir2 + ". Osl file error: " + + OUString::number(openErr)); + } + else + { + //usr/java + //When we look directly into a dir like /usr, /usr/lib, etc. then we only + //look for certain java directories, such as jre, jdk, etc. We do not want + //to examine the whole directory because of performance reasons. + DirectoryItem item2; + if(DirectoryItem::get(usDir2, item2) == File::E_None) + { + for( int k= 0; k < cJavaNames; k++) + { + // /usr/java/j2re1.4.0 + OUString usDir3(usDir2 + arNames[k]); + + DirectoryItem item3; + if(DirectoryItem::get(usDir3, item3) == File::E_None) + { + //remove trailing '/' + sal_Int32 islash = usDir3.lastIndexOf('/'); + if (islash == usDir3.getLength() - 1 + && (islash + > RTL_CONSTASCII_LENGTH("file://"))) + usDir3 = usDir3.copy(0, islash); + getAndAddJREInfoByPath( + usDir3, allInfos, addedInfos); + } + } + } + } + } + } + } +#endif // MACOSX +} +#endif // ifdef __sun +#endif // ifdef UNX +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/util.hxx b/jvmfwk/plugins/sunmajor/pluginlib/util.hxx new file mode 100644 index 000000000..9ba005583 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/util.hxx @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_UTIL_HXX +#define INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_UTIL_HXX + +#include <rtl/ustring.hxx> +#include <vector> +#include <vendorbase.hxx> + +namespace jfw_plugin +{ + +std::vector<OUString> getVectorFromCharArray(char const * const * ar, int size); + +/* The function uses the relative paths, such as "bin/java.exe" and the provided + path to derive the home directory. The home directory is then used as + argument to getJREInfoByPath. For example usBinDir is + file:///c:/j2sdk/jre/bin then file:///c:/j2sdk/jre would be derived. + */ +void addJREInfoFromBinPath( + const OUString& path, + std::vector<rtl::Reference<VendorBase>> & allInfos, + std::vector<rtl::Reference<VendorBase>> & addedInfos); +inline OUString getDirFromFile(std::u16string_view usFilePath); +void addJavaInfosFromPath( + std::vector<rtl::Reference<VendorBase>> & allInfos, + std::vector<rtl::Reference<VendorBase>> & addedInfos); + +/* Returns a VendorBase object if JAVA_HOME environment variable points + to a JRE. + */ +void addJavaInfoFromJavaHome( + std::vector<rtl::Reference<VendorBase>> & allInfos, + std::vector<rtl::Reference<VendorBase>> & addedInfos); + +void addJavaInfosDirScan( + std::vector<rtl::Reference<VendorBase>> & allInfos, + std::vector<rtl::Reference<VendorBase>> & addedInfos); + +bool makeDriveLetterSame(OUString * fileURL); + + +/* for std::find_if + Used to find a JavaInfo::Impl object in a std::vector<Impl*> which has a member usJavaHome + as the specified string in the constructor. +*/ +struct InfoFindSame +{ + OUString sJava; + explicit InfoFindSame(const OUString& sJavaHome):sJava(sJavaHome){} + + bool operator () (const rtl::Reference<VendorBase> & aVendorInfo) + { + return aVendorInfo->getHome() == sJava; + } +}; + +struct SameOrSubDirJREMap +{ + OUString s1; + explicit SameOrSubDirJREMap(const OUString& s):s1(s){ + } + + bool operator () (const std::pair<const OUString, rtl::Reference<VendorBase> > & s2) + { + if (s1 == s2.first) + return true; + OUString sSub = s2.first + "/"; + if (s1.match(sSub)) + return true; + return false; + } +}; + + +/* Creates a VendorBase object if a JRE could be found at the specified path. + + This depends if there is a JRE at all and if it is from a vendor that + is supported by this plugin. + */ +rtl::Reference<VendorBase> getJREInfoByPath(const OUString& path); + +std::vector<rtl::Reference<VendorBase> > addAllJREInfos( + bool checkJavaHomeAndPath, std::vector<rtl::Reference<VendorBase>> & infos); + +bool getJavaProps( + const OUString & exePath, +#ifdef JVM_ONE_PATH_CHECK + const OUString & homePath, +#endif + std::vector<std::pair<OUString, OUString> >& props, + bool * bProcessRun); + +void bubbleSortVersion(std::vector<rtl::Reference<VendorBase> >& vec); + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/util_cocoa.hxx b/jvmfwk/plugins/sunmajor/pluginlib/util_cocoa.hxx new file mode 100644 index 000000000..ae43cee91 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/util_cocoa.hxx @@ -0,0 +1,14 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_UTIL_COCOA_HXX +#define INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_UTIL_COCOA_HXX + +#include <sal/config.h> + +#include <rtl/ustring.hxx> + +bool JvmfwkUtil_isLoadableJVM(OUString const& aURL); + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/util_cocoa.mm b/jvmfwk/plugins/sunmajor/pluginlib/util_cocoa.mm new file mode 100644 index 000000000..8c745f8d9 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/util_cocoa.mm @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include <rtl/ustring.hxx> + +#include <premac.h> +#import <Foundation/Foundation.h> +#include <postmac.h> + +#import "util_cocoa.hxx" + +using namespace rtl; + +bool JvmfwkUtil_isLoadableJVM( OUString const & aURL ) +{ + bool bRet = false; + + if ( aURL.getLength() ) + { + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + NSString *pString = [NSString stringWithCharacters:reinterpret_cast<unichar const *>(aURL.getStr()) length:aURL.getLength()]; + if ( pString ) + { + // Ignore all but Oracle's JDK as loading Apple's Java and Oracle's + // JRE will cause macOS's JavaVM framework to display a dialog and + // invoke exit() when loaded via JNI on macOS 10.10 + NSURL *pURL = [NSURL URLWithString:pString]; + if ( pURL ) + pURL = [pURL filePathURL]; + if ( pURL ) + pURL = [pURL URLByStandardizingPath]; + if ( pURL ) + pURL = [pURL URLByResolvingSymlinksInPath]; + + while ( pURL ) + { + // Check if this is a valid bundle + NSNumber *pDir = nil; + NSURL *pContentsURL = [pURL URLByAppendingPathComponent:@"Contents"]; + if ( pContentsURL && [pContentsURL getResourceValue:&pDir forKey:NSURLIsDirectoryKey error:nil] && pDir && [pDir boolValue] ) + { + NSBundle *pBundle = [NSBundle bundleWithURL:pURL]; + if ( pBundle ) + { + // Make sure that this bundle's Info.plist has the + // proper JVM keys to supports loading via JNI. If + // this bundle is a valid JVM and these keys + // are missing, loading the JVM will cause macOS's + // JavaVM framework to display a dialog and invoke + // exit() when loaded via JNI on macOS 10.10. + NSDictionary *pInfo = [pBundle infoDictionary]; + if ( pInfo ) + { + NSDictionary *pJavaVM = [pInfo objectForKey:@"JavaVM"]; + if ( pJavaVM && [pJavaVM isKindOfClass:[NSDictionary class]] ) + { + NSArray *pJVMCapabilities = [pJavaVM objectForKey:@"JVMCapabilities"]; + if ( pJVMCapabilities ) + { + if ( [pJVMCapabilities indexOfObjectIdenticalTo:@"JNI"] == NSNotFound ) + { + if ( [pJVMCapabilities isKindOfClass:[NSMutableArray class]] ) + { + [static_cast<NSMutableArray *>(pJVMCapabilities) addObject:@"JNI"]; + bRet = true; + } + else if ( [pJavaVM isKindOfClass:[NSMutableDictionary class]] ) + { + NSMutableArray *pNewJVMCapabilities = [NSMutableArray arrayWithCapacity:[pJVMCapabilities count] + 1]; + if ( pNewJVMCapabilities ) + { + [pNewJVMCapabilities addObject:@"JNI"]; + [static_cast<NSMutableDictionary *>(pJavaVM) setObject:pNewJVMCapabilities forKey:@"JVMCapabilities"]; + bRet = true; + } + } + } + else + { + bRet = true; + } + } + } + } + } + } + + NSURL *pOldURL = pURL; + pURL = [pURL URLByDeletingLastPathComponent]; + if ( pURL ) + { + pURL = [pURL URLByStandardizingPath]; + if ( pURL ) + { + pURL = [pURL URLByResolvingSymlinksInPath]; + if ( pURL && [pURL isEqual:pOldURL] ) + pURL = nil; + } + } + } + } + + [pPool release]; + } + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/vendorbase.cxx b/jvmfwk/plugins/sunmajor/pluginlib/vendorbase.cxx new file mode 100644 index 000000000..f4cf14a9a --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/vendorbase.cxx @@ -0,0 +1,197 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <osl/file.hxx> + +#include <vendorbase.hxx> +#include "util.hxx" + +using namespace osl; + + +namespace jfw_plugin +{ + +MalformedVersionException::~MalformedVersionException() = default; + +VendorBase::VendorBase() +{ +} + +bool VendorBase::initialize(const std::vector<std::pair<OUString, OUString> >& props) +{ + //get java.vendor, java.version, java.home + //from system properties + + bool bVersion = false; + bool bVendor = false; + bool bHome = false; + bool bArch = false; + + for (auto const& prop : props) + { + if(! bVendor && prop.first == "java.vendor") + { + m_sVendor = prop.second; + bVendor = true; + } + else if (!bVersion && prop.first == "java.version") + { + m_sVersion = prop.second; + bVersion = true; + } + else if (!bHome && prop.first == "java.home") + { +#ifndef JVM_ONE_PATH_CHECK + OUString fileURL; + if (osl_getFileURLFromSystemPath(prop.second.pData,& fileURL.pData) == + osl_File_E_None) + { + //make sure that the drive letter have all the same case + //otherwise file:///c:/jre and file:///C:/jre produce two + //different objects!!! + if (makeDriveLetterSame( & fileURL)) + { + m_sHome = fileURL; + bHome = true; + } + } +#else + m_sHome = prop.second; + bHome = true; +#endif + } + else if (!bArch && prop.first == "os.arch") + { + m_sArch = prop.second; + bArch = true; + } + if (bVendor && bVersion && bHome && bArch) { + break; + } + } + if (!bVersion || !bVendor || !bHome || !bArch) + return false; + + // init m_sRuntimeLibrary + OSL_ASSERT(!m_sHome.isEmpty()); + //call virtual function to get the possible paths to the runtime library. + + int size = 0; + char const* const* arRtPaths = getRuntimePaths( & size); + std::vector<OUString> libpaths = getVectorFromCharArray(arRtPaths, size); + + bool bRt = false; + for (auto const& libpath : libpaths) + { + //Construct an absolute path to the possible runtime + OUString usRt= m_sHome + libpath; + DirectoryItem item; + if(DirectoryItem::get(usRt, item) == File::E_None) + { + //found runtime lib + m_sRuntimeLibrary = usRt; + bRt = true; + break; + } + } + if (!bRt) + return false; + + // init m_sLD_LIBRARY_PATH + OSL_ASSERT(!m_sHome.isEmpty()); + size = 0; + char const * const * arLDPaths = getLibraryPaths( & size); + std::vector<OUString> ld_paths = getVectorFromCharArray(arLDPaths, size); + + bool bLdPath = true; + int c = 0; + for (auto const& ld_path : ld_paths) + { + OUString usAbsUrl= m_sHome + ld_path; + // convert to system path + OUString usSysPath; + if(File::getSystemPathFromFileURL(usAbsUrl, usSysPath) == File::E_None) + { + + if(c > 0) + m_sLD_LIBRARY_PATH+= OUStringChar(SAL_PATHSEPARATOR); + m_sLD_LIBRARY_PATH+= usSysPath; + } + else + { + bLdPath = false; + break; + } + ++c; + } + return bLdPath; +} + +const OUString & VendorBase::getVendor() const +{ + return m_sVendor; +} +const OUString & VendorBase::getVersion() const +{ + return m_sVersion; +} + +const OUString & VendorBase::getHome() const +{ + return m_sHome; +} + +const OUString & VendorBase::getLibraryPath() const +{ + return m_sLD_LIBRARY_PATH; +} + +const OUString & VendorBase::getRuntimeLibrary() const +{ + return m_sRuntimeLibrary; +} + +bool VendorBase::isValidArch() const +{ + // Warning: These values come from the "os.arch" property. + // It is not defined what the exact values are. + // Oracle JRE 8 has "x86" and "amd64", the others were found at http://lopica.sourceforge.net/os.html . + // There might still be missing some options; we need to extend the check once we find out. +#if defined _WIN64 || (defined MACOSX && defined __x86_64__) + return m_sArch == "amd64" || m_sArch == "x86_64"; +#elif defined _WIN32 + return m_sArch == "x86" || m_sArch == "i386" || m_sArch == "i686"; +#elif defined MACOSX && defined __aarch64__ + return m_sArch == "aarch64"; +#else + (void)this; + return true; +#endif +} + +bool VendorBase::needsRestart() const +{ + return !getLibraryPath().isEmpty(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/vendorlist.cxx b/jvmfwk/plugins/sunmajor/pluginlib/vendorlist.cxx new file mode 100644 index 000000000..96cc05167 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/vendorlist.cxx @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include "vendorlist.hxx" +#include "sunjre.hxx" +#include "otherjre.hxx" + +namespace jfw_plugin +{ + +/* Note: The vendor strings must be UTF-8. For example, if + the string contains an umlaut then it must be expressed + by "\xXX\xXX" + */ +VendorSupportMapEntry const gVendorMap[] ={ +// For macOS, don't bother with implementations that aren't relevant (or have never existed) +#ifdef MACOSX + VENDOR_MAP_ENTRY<OtherInfo>("Apple Inc."), + VENDOR_MAP_ENTRY<OtherInfo>("Apple Computer, Inc."), +#endif + VENDOR_MAP_ENTRY<SunInfo>("Sun Microsystems Inc."), + VENDOR_MAP_ENTRY<SunInfo>("Oracle Corporation"), + VENDOR_MAP_ENTRY<SunInfo>("Amazon.com Inc."), + VENDOR_MAP_ENTRY<SunInfo>("Azul Systems, Inc."), +#ifndef MACOSX + VENDOR_MAP_ENTRY<OtherInfo>("IBM Corporation"), + VENDOR_MAP_ENTRY<OtherInfo>("Blackdown Java-Linux Team"), + VENDOR_MAP_ENTRY<OtherInfo>("BEA Systems, Inc."), + VENDOR_MAP_ENTRY<OtherInfo>("The FreeBSD Foundation"), +#endif + {nullptr, nullptr, nullptr} }; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/vendorlist.hxx b/jvmfwk/plugins/sunmajor/pluginlib/vendorlist.hxx new file mode 100644 index 000000000..e10958d24 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/vendorlist.hxx @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_VENDORLIST_HXX +#define INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_VENDORLIST_HXX + +#include <rtl/ref.hxx> + +namespace jfw_plugin { class VendorBase; } + +namespace jfw_plugin +{ + +typedef char const * const * (* getJavaExePaths_func)(int*); +typedef rtl::Reference<VendorBase> (* createInstance_func) (); + +struct VendorSupportMapEntry +{ + char const * sVendorName; + getJavaExePaths_func getJavaFunc; + createInstance_func createFunc; +}; + +extern VendorSupportMapEntry const gVendorMap[]; + +template<typename y> constexpr VendorSupportMapEntry VENDOR_MAP_ENTRY(char const * x) { + return {x, & y::getJavaExePaths, & y::createInstance}; +} + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |