1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_plugin.hxx"
26
27 #include "util.hxx"
28
29 #include "osl/process.h"
30 #include "osl/security.hxx"
31 #include "osl/thread.hxx"
32 #include "osl/file.hxx"
33 #include "osl/module.hxx"
34 #include "rtl/byteseq.hxx"
35 #include "rtl/ustrbuf.hxx"
36 #include "rtl/instance.hxx"
37 #include "boost/scoped_array.hpp"
38 #include "com/sun/star/uno/Sequence.hxx"
39 #include <utility>
40 #include <algorithm>
41 #include <map>
42
43 #if defined WNT
44 #if defined _MSC_VER
45 #pragma warning(push, 1)
46 #endif
47 #include <windows.h>
48 #if defined _MSC_VER
49 #pragma warning(pop)
50 #endif
51 #endif
52 #include <string.h>
53
54 #include "sunjre.hxx"
55 #include "vendorlist.hxx"
56 #include "diagnostics.h"
57 using namespace rtl;
58 using namespace osl;
59 using namespace std;
60
61 #define CHAR_POINTER(oustr) ::rtl::OUStringToOString(oustr,RTL_TEXTENCODING_UTF8).pData->buffer
62 #define OUSTR(x) ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(x) )
63 #ifdef WNT
64 #define HKEY_SUN_JRE L"Software\\JavaSoft\\Java Runtime Environment"
65 #define HKEY_SUN_SDK L"Software\\JavaSoft\\Java Development Kit"
66 #endif
67
68 #ifdef UNX
69 namespace {
70 char const *g_arJavaNames[] = {
71 "",
72 "j2re",
73 "j2se",
74 "j2sdk",
75 "jdk",
76 "jre",
77 "java",
78 "Home",
79 "IBMJava2-ppc-142"
80 };
81 /* These are directory names which could contain multiple java installations.
82 */
83 char const *g_arCollectDirs[] = {
84 "",
85 "j2re/",
86 "j2se/",
87 "j2sdk/",
88 "jdk/",
89 "jre/",
90 "java/",
91 "jvm/"
92 };
93
94 struct JavaSearchPathEntry {
95 int searchImmediateContents; // More thorough, too slow for /usr/bin and /usr/lib
96 char const *path;
97 };
98
99 /* These are directories in which a java installation is
100 looked for.
101 */
102 struct JavaSearchPathEntry g_arSearchPaths[] = {
103 #ifdef MACOSX
104 { 0, "" },
105 // Modern macOS JDK location (Oracle/Temurin/etc., both arm64 and x86_64
106 // install here). Each subdirectory is a JDK whose home is Contents/Home,
107 // so scan the immediate contents. A JDK of the wrong architecture (e.g. a
108 // Rosetta x86_64 JDK on Apple Silicon) is harmlessly skipped when its
109 // libjvm.dylib fails to load into the native-arch process.
110 { 1, "Library/Java/JavaVirtualMachines/" },
111 { 0, "Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin" },
112 { 0, "System/Library/Frameworks/JavaVM.framework/Versions/1.4.2/" },
113 #else
114 { 0, "" },
115 { 1, "usr/" },
116 { 1, "usr/local/" },
117 { 0, "usr/local/IBMJava2-ppc-142" },
118 { 0, "usr/local/j2sdk1.3.1" },
119 #ifdef X86_64
120 { 0, "usr/lib64/" },
121 #endif
122 { 0, "usr/lib/" },
123 { 0, "usr/bin/" }
124 #endif
125 };
126 }
127 #endif // UNX
128
129 namespace jfw_plugin
130 {
131 extern VendorSupportMapEntry gVendorMap[];
132
133 bool getSDKInfoFromRegistry(vector<OUString> & vecHome);
134 bool getJREInfoFromRegistry(vector<OUString>& vecJavaHome);
135 bool decodeOutput(const rtl::OString& s, rtl::OUString* out);
136
137
138
139 namespace
140 {
getLibraryLocation()141 rtl::OUString getLibraryLocation()
142 {
143 rtl::OUString libraryFileUrl;
144 OSL_VERIFY(osl::Module::getUrlFromAddress((void *)(sal_IntPtr)getLibraryLocation, libraryFileUrl));
145 return getDirFromFile(libraryFileUrl);
146 }
147
148 struct InitBootstrap
149 {
operator ()jfw_plugin::__anon1f015b3d0211::InitBootstrap150 rtl::Bootstrap * operator()(const OUString& sIni)
151 {
152 static rtl::Bootstrap aInstance(sIni);
153 return & aInstance;
154
155 }
156 };
157
158 struct InitBootstrapData
159 {
operator ()jfw_plugin::__anon1f015b3d0211::InitBootstrapData160 OUString const & operator()()
161 {
162 // osl::Guard<osl::Mutex> g(osl::GetGlobalMutex());
163 static OUString sIni;
164 rtl::OUStringBuffer buf( 255);
165 buf.append( getLibraryLocation());
166 buf.appendAscii( SAL_CONFIGFILE("/sunjavaplugin") );
167 sIni = buf.makeStringAndClear();
168 JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin: "
169 "Using configuration file \n") + sIni);
170 return sIni;
171 }
172 };
173 }
174
getBootstrap()175 rtl::Bootstrap * getBootstrap()
176 {
177 return rtl_Instance< rtl::Bootstrap, InitBootstrap,
178 ::osl::MutexGuard, ::osl::GetGlobalMutex,
179 OUString, InitBootstrapData >::create(
180 InitBootstrap(), ::osl::GetGlobalMutex(), InitBootstrapData());
181 }
182
183
184
185
186 class FileHandleGuard
187 {
188 public:
189 inline FileHandleGuard(oslFileHandle & rHandle) SAL_THROW(()):
190 m_rHandle(rHandle) {}
191
192 inline ~FileHandleGuard() SAL_THROW(());
193
getHandle()194 inline oslFileHandle & getHandle() SAL_THROW(()) { return m_rHandle; }
195
196 private:
197 oslFileHandle & m_rHandle;
198
199 FileHandleGuard(FileHandleGuard &); // not implemented
200 void operator =(FileHandleGuard); // not implemented
201 };
202
~FileHandleGuard()203 inline FileHandleGuard::~FileHandleGuard() SAL_THROW(())
204 {
205 if (m_rHandle != 0)
206 {
207 if (osl_closeFile(m_rHandle) != osl_File_E_None)
208 {
209 OSL_ENSURE(false, "unexpected situation");
210 }
211 }
212 }
213
214
215 class FileHandleReader
216 {
217 public:
218 enum Result
219 {
220 RESULT_OK,
221 RESULT_EOF,
222 RESULT_ERROR
223 };
224
225 inline FileHandleReader(oslFileHandle & rHandle) SAL_THROW(()):
226 m_aGuard(rHandle), m_nSize(0), m_nIndex(0), m_bLf(false) {}
227
228 Result readLine(rtl::OString * pLine) SAL_THROW(());
229
230 private:
231 enum { BUFFER_SIZE = 1024 };
232
233 sal_Char m_aBuffer[BUFFER_SIZE];
234 FileHandleGuard m_aGuard;
235 int m_nSize;
236 int m_nIndex;
237 bool m_bLf;
238 };
239
240 FileHandleReader::Result
readLine(rtl::OString * pLine)241 FileHandleReader::readLine(rtl::OString * pLine)
242 SAL_THROW(())
243 {
244 OSL_ENSURE(pLine, "specification violation");
245
246 for (bool bEof = true;; bEof = false)
247 {
248 if (m_nIndex == m_nSize)
249 {
250 sal_uInt64 nRead = 0;
251 switch (osl_readFile(
252 m_aGuard.getHandle(), m_aBuffer, sizeof(m_aBuffer), &nRead))
253 {
254 case osl_File_E_PIPE: //HACK! for windows
255 nRead = 0;
256 case osl_File_E_None:
257 if (nRead == 0)
258 {
259 m_bLf = false;
260 return bEof ? RESULT_EOF : RESULT_OK;
261 }
262 m_nIndex = 0;
263 m_nSize = static_cast< int >(nRead);
264 break;
265 case osl_File_E_INTR:
266 continue;
267
268 default:
269 return RESULT_ERROR;
270 }
271 }
272
273 if (m_bLf && m_aBuffer[m_nIndex] == 0x0A)
274 ++m_nIndex;
275 m_bLf = false;
276
277 int nStart = m_nIndex;
278 while (m_nIndex != m_nSize)
279 switch (m_aBuffer[m_nIndex++])
280 {
281 case 0x0D:
282 m_bLf = true;
283 case 0x0A:
284 *pLine += rtl::OString(m_aBuffer + nStart,
285 m_nIndex - 1 - nStart);
286 //TODO! check for overflow, and not very efficient
287 return RESULT_OK;
288 }
289
290 *pLine += rtl::OString(m_aBuffer + nStart, m_nIndex - nStart);
291 //TODO! check for overflow, and not very efficient
292 }
293 }
294
295 class AsynchReader: public Thread
296 {
297 size_t m_nDataSize;
298 boost::scoped_array<sal_Char> m_arData;
299
300 bool m_bError;
301 bool m_bDone;
302 FileHandleGuard m_aGuard;
303
304 void SAL_CALL run();
305 public:
306
307 AsynchReader(oslFileHandle & rHandle);
308 #if OSL_DEBUG_LEVEL >= 2
309 /** only call this function after this thread has finished.
310
311 That is, call join on this instance and then call getData.
312
313 */
314 OString getData();
315 #endif
316 };
317
AsynchReader(oslFileHandle & rHandle)318 AsynchReader::AsynchReader(oslFileHandle & rHandle):
319 m_nDataSize(0), m_bError(false), m_bDone(false), m_aGuard(rHandle)
320 {
321 }
322
323 #if OSL_DEBUG_LEVEL >= 2
getData()324 OString AsynchReader::getData()
325 {
326 OSL_ASSERT(isRunning() == sal_False );
327 return OString(m_arData.get(), m_nDataSize);
328 }
329 #endif
330
run()331 void AsynchReader::run()
332 {
333 const sal_uInt64 BUFFER_SIZE = 4096;
334 sal_Char aBuffer[BUFFER_SIZE];
335 while (true)
336 {
337 sal_uInt64 nRead;
338 //the function blocks until something could be read or the pipe closed.
339 switch (osl_readFile(
340 m_aGuard.getHandle(), aBuffer, BUFFER_SIZE, &nRead))
341 {
342 case osl_File_E_PIPE: //HACK! for windows
343 nRead = 0;
344 case osl_File_E_None:
345 break;
346 default:
347 m_bError = true;
348 return;
349 }
350
351 if (nRead == 0)
352 {
353 m_bDone = true;
354 break;
355 }
356 else if (nRead <= BUFFER_SIZE)
357 {
358 //Save the data we have in m_arData into a temporary array
359 boost::scoped_array<sal_Char> arTmp( new sal_Char[m_nDataSize]);
360 memcpy(arTmp.get(), m_arData.get(), m_nDataSize);
361 //Enlarge m_arData to hold the newly read data
362 m_arData.reset(new sal_Char[(size_t)(m_nDataSize + nRead)]);
363 //Copy back the data that was already in m_arData
364 memcpy(m_arData.get(), arTmp.get(), m_nDataSize);
365 //Add the newly read data to m_arData
366 memcpy(m_arData.get() + m_nDataSize, aBuffer, (size_t) nRead);
367 m_nDataSize += (size_t) nRead;
368 }
369 }
370 }
371
372
getJavaProps(const OUString & exePath,std::vector<std::pair<rtl::OUString,rtl::OUString>> & props,bool * bProcessRun)373 bool getJavaProps(const OUString & exePath,
374 std::vector<std::pair<rtl::OUString, rtl::OUString> >& props,
375 bool * bProcessRun)
376 {
377 bool ret = false;
378
379 OSL_ASSERT( exePath.getLength() > 0);
380 OUString usStartDir;
381 //We need to set the CLASSPATH in case the office is started from
382 //a different directory. The JREProperties.class is expected to reside
383 //next to the plugin.
384 rtl::OUString sThisLib;
385 if (osl_getModuleURLFromAddress((void *) (sal_IntPtr)& getJavaProps,
386 & sThisLib.pData) == sal_False)
387 return false;
388 sThisLib = getDirFromFile(sThisLib);
389 OUString sClassPath;
390 if (osl_getSystemPathFromFileURL(sThisLib.pData, & sClassPath.pData)
391 != osl_File_E_None)
392 return false;
393
394 //check if we shall examine a Java for accessibility support
395 //If the bootstrap variable is "1" then we pass the argument
396 //"noaccessibility" to JREProperties.class. This will prevent
397 //that it calls java.awt.Toolkit.getDefaultToolkit();
398 OUString sValue;
399 getBootstrap()->getFrom(OUSTR("JFW_PLUGIN_DO_NOT_CHECK_ACCESSIBILITY"), sValue);
400
401 //prepare the arguments
402 sal_Int32 cArgs = 3;
403 OUString arg1 = OUString(RTL_CONSTASCII_USTRINGPARAM("-classpath"));// + sClassPath;
404 OUString arg2 = sClassPath;
405 OUString arg3(RTL_CONSTASCII_USTRINGPARAM("JREProperties"));
406 OUString arg4 = OUSTR("noaccessibility");
407 rtl_uString *args[4] = {arg1.pData, arg2.pData, arg3.pData};
408
409 // Only add the fourth param if the bootstrap parameter is set.
410 if (sValue.equals(OUString::valueOf((sal_Int32) 1)))
411 {
412 args[3] = arg4.pData;
413 cArgs = 4;
414 }
415
416 oslProcess javaProcess= 0;
417 oslFileHandle fileOut= 0;
418 oslFileHandle fileErr= 0;
419
420 FileHandleReader stdoutReader(fileOut);
421 AsynchReader stderrReader(fileErr);
422
423 JFW_TRACE2(OUSTR("\n[Java framework] Executing: ") + exePath + OUSTR(".\n"));
424 oslProcessError procErr =
425 osl_executeProcess_WithRedirectedIO( exePath.pData,//usExe.pData,
426 args,
427 cArgs, //sal_uInt32 nArguments,
428 osl_Process_HIDDEN, //oslProcessOption Options,
429 NULL, //oslSecurity Security,
430 usStartDir.pData,//usStartDir.pData,//usWorkDir.pData, //rtl_uString *strWorkDir,
431 NULL, //rtl_uString *strEnvironment[],
432 0, // sal_uInt32 nEnvironmentVars,
433 &javaProcess, //oslProcess *pProcess,
434 NULL,//oslFileHandle *pChildInputWrite,
435 &fileOut,//oslFileHandle *pChildOutputRead,
436 &fileErr);//oslFileHandle *pChildErrorRead);
437
438 if( procErr != osl_Process_E_None)
439 {
440 JFW_TRACE2("[Java framework] Execution failed. \n");
441 *bProcessRun = false;
442 return ret;
443 }
444 else
445 {
446 JFW_TRACE2("[Java framework] Java executed successfully.\n");
447 *bProcessRun = true;
448 }
449
450 //Start asynchronous reading (different thread) of error stream
451 stderrReader.create();
452
453 //Use this thread to read output stream
454 FileHandleReader::Result rs = FileHandleReader::RESULT_OK;
455 while (1)
456 {
457 OString aLine;
458 rs = stdoutReader.readLine( & aLine);
459 if (rs != FileHandleReader::RESULT_OK)
460 break;
461 // JFW_TRACE2(OString("[Java framework] line:\" ")
462 // + aLine + OString(" \".\n"));
463 OUString sLine;
464 if (!decodeOutput(aLine, &sLine))
465 continue;
466 JFW_TRACE2(OString("[Java framework]:\" ")
467 + OString( CHAR_POINTER(sLine)) + OString(" \".\n"));
468 sLine = sLine.trim();
469 if (sLine.getLength() == 0)
470 continue;
471 //The JREProperties class writes key value pairs, separated by '='
472 sal_Int32 index = sLine.indexOf('=', 0);
473 OSL_ASSERT(index != -1);
474 OUString sKey = sLine.copy(0, index);
475 OUString sVal = sLine.copy(index + 1);
476
477 props.push_back(std::make_pair(sKey, sVal));
478 }
479
480 if (rs != FileHandleReader::RESULT_ERROR && props.size()>0)
481 ret = true;
482
483 //process error stream data
484 stderrReader.join();
485 JFW_TRACE2(OString("[Java framework] Java wrote to stderr:\" ")
486 + stderrReader.getData() + OString(" \".\n"));
487
488 TimeValue waitMax= {5 ,0};
489 procErr = osl_joinProcessWithTimeout(javaProcess, &waitMax);
490 OSL_ASSERT(procErr == osl_Process_E_None);
491 osl_freeProcessHandle(javaProcess);
492 return ret;
493 }
494
495 /* converts the properties printed by JREProperties.class into
496 readable strings. The strings are encoded as integer values separated
497 by spaces.
498 */
decodeOutput(const rtl::OString & s,rtl::OUString * out)499 bool decodeOutput(const rtl::OString& s, rtl::OUString* out)
500 {
501 OSL_ASSERT(out != 0);
502 OUStringBuffer buff(512);
503 sal_Int32 nIndex = 0;
504 do
505 {
506 OString aToken = s.getToken( 0, ' ', nIndex );
507 if (aToken.getLength())
508 {
509 for (sal_Int32 i = 0; i < aToken.getLength(); ++i)
510 {
511 if (aToken[i] < '0' || aToken[i] > '9')
512 return false;
513 }
514 sal_Unicode value = (sal_Unicode)(aToken.toInt32());
515 buff.append(value);
516 }
517 } while (nIndex >= 0);
518
519 *out = buff.makeStringAndClear();
520 // JFW_TRACE2(*out);
521 return true;
522 }
523
524
525 #if defined WNT
createJavaInfoFromWinReg(std::vector<rtl::Reference<VendorBase>> & vecInfos)526 void createJavaInfoFromWinReg(std::vector<rtl::Reference<VendorBase> > & vecInfos)
527 {
528 // Get Java s from registry
529 std::vector<OUString> vecJavaHome;
530 if(getSDKInfoFromRegistry(vecJavaHome))
531 {
532 // create impl objects
533 typedef std::vector<OUString>::iterator ItHome;
534 for(ItHome it_home= vecJavaHome.begin(); it_home != vecJavaHome.end();
535 it_home++)
536 {
537 getJREInfoByPath(*it_home, vecInfos);
538 }
539 }
540
541 vecJavaHome.clear();
542 if(getJREInfoFromRegistry(vecJavaHome))
543 {
544 typedef std::vector<OUString>::iterator ItHome;
545 for(ItHome it_home= vecJavaHome.begin(); it_home != vecJavaHome.end();
546 it_home++)
547 {
548 getJREInfoByPath(*it_home, vecInfos);
549 }
550 }
551 }
552
553
getJavaInfoFromRegistry(const wchar_t * szRegKey,vector<OUString> & vecJavaHome)554 bool getJavaInfoFromRegistry(const wchar_t* szRegKey,
555 vector<OUString>& vecJavaHome)
556 {
557 HKEY hRoot;
558 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_ENUMERATE_SUB_KEYS, &hRoot)
559 == ERROR_SUCCESS)
560 {
561 DWORD dwIndex = 0;
562 const DWORD BUFFSIZE = 1024;
563 wchar_t bufVersion[BUFFSIZE];
564 // char bufVersion[BUFFSIZE];
565 DWORD nNameLen = BUFFSIZE;
566 FILETIME fileTime;
567 nNameLen = sizeof(bufVersion);
568
569 // Iterate over all subkeys of HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment
570 while (RegEnumKeyExW(hRoot, dwIndex, bufVersion, &nNameLen, NULL, NULL, NULL, &fileTime) != ERROR_NO_MORE_ITEMS)
571 {
572 HKEY hKey;
573 // Open a Java Runtime Environment sub key, e.g. "1.4.0"
574 if (RegOpenKeyExW(hRoot, bufVersion, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
575 {
576 DWORD dwType;
577 DWORD dwTmpPathLen= 0;
578 // Get the path to the JavaHome every JRE entry
579 // Find out how long the string for JavaHome is and allocate memory to hold the path
580 if( RegQueryValueExW(hKey, L"JavaHome", 0, &dwType, NULL, &dwTmpPathLen)== ERROR_SUCCESS)
581 {
582 char* szTmpPath= (char *) malloc( dwTmpPathLen);
583 // Get the path for the runtime lib
584 if(RegQueryValueExW(hKey, L"JavaHome", 0, &dwType, (unsigned char*) szTmpPath, &dwTmpPathLen) == ERROR_SUCCESS)
585 {
586 // There can be several version entries referring with the same JavaHome,e.g 1.4 and 1.4.1
587 OUString usHome((sal_Unicode*) szTmpPath);
588 // check if there is already an entry with the same JavaHomeruntime lib
589 // if so, we use the one with the more accurate version
590 bool bAppend= true;
591 OUString usHomeUrl;
592 if (osl_getFileURLFromSystemPath(usHome.pData, & usHomeUrl.pData) ==
593 osl_File_E_None)
594 {
595 //iterate over the vector with java home strings
596 typedef vector<OUString>::iterator ItHome;
597 for(ItHome itHome= vecJavaHome.begin();
598 itHome != vecJavaHome.end(); itHome++)
599 {
600 if(usHomeUrl.equals(*itHome))
601 {
602 bAppend= false;
603 break;
604 }
605 }
606 // Save the home dir
607 if(bAppend)
608 {
609 vecJavaHome.push_back(usHomeUrl);
610 }
611 }
612 }
613 free( szTmpPath);
614 RegCloseKey(hKey);
615 }
616 }
617 dwIndex ++;
618 nNameLen = BUFFSIZE;
619 }
620 RegCloseKey(hRoot);
621 }
622 return true;
623 }
624
625
626
getSDKInfoFromRegistry(vector<OUString> & vecHome)627 bool getSDKInfoFromRegistry(vector<OUString> & vecHome)
628 {
629 return getJavaInfoFromRegistry(HKEY_SUN_SDK, vecHome);
630 }
631
getJREInfoFromRegistry(vector<OUString> & vecJavaHome)632 bool getJREInfoFromRegistry(vector<OUString>& vecJavaHome)
633 {
634 return getJavaInfoFromRegistry(HKEY_SUN_JRE, vecJavaHome);
635 }
636
637 #endif // WNT
638
bubbleSortVersion(vector<rtl::Reference<VendorBase>> & vec)639 void bubbleSortVersion(vector<rtl::Reference<VendorBase> >& vec)
640 {
641 if(vec.size() == 0)
642 return;
643 int size= vec.size() - 1;
644 int cIter= 0;
645 // sort for version
646 for(int i= 0; i < size; i++)
647 {
648 for(int j= size; j > 0 + cIter; j--)
649 {
650 rtl::Reference<VendorBase>& cur= vec.at(j);
651 rtl::Reference<VendorBase>& next= vec.at(j-1);
652
653 int nCmp = 0;
654 // comparing invalid SunVersion s is possible, they will be less than a
655 // valid version
656
657 //check if version of current is recognized, by comparing it with itself
658 try
659 {
660 cur->compareVersions(cur->getVersion());
661 }
662 catch (MalformedVersionException &)
663 {
664 nCmp = -1; // current < next
665 }
666 //The version of cur is valid, now compare with the second version
667 if (nCmp == 0)
668 {
669 try
670 {
671 nCmp = cur->compareVersions(next->getVersion());
672 }
673 catch (MalformedVersionException & )
674 {
675 //The second version is invalid, therefor it is regardes less.
676 nCmp = 1;
677 }
678 }
679 if(nCmp == 1) // cur > next
680 {
681 rtl::Reference<VendorBase> less = next;
682 vec.at(j-1)= cur;
683 vec.at(j)= less;
684 }
685 }
686 cIter++;
687 }
688 }
689
690
getJREInfoFromBinPath(const rtl::OUString & path,vector<rtl::Reference<VendorBase>> & vecInfos)691 bool getJREInfoFromBinPath(
692 const rtl::OUString& path, vector<rtl::Reference<VendorBase> > & vecInfos)
693 {
694 // file:///c:/jre/bin
695 //map: jre/bin/java.exe
696 bool ret = false;
697 vector<pair<OUString, OUString> > props;
698
699 for ( sal_Int32 pos = 0;
700 gVendorMap[pos].sVendorName != NULL; ++pos )
701 {
702 vector<OUString> vecPaths;
703 getJavaExePaths_func pFunc = gVendorMap[pos].getJavaFunc;
704
705 int size = 0;
706 char const* const* arExePaths = (*pFunc)(&size);
707 vecPaths = getVectorFromCharArray(arExePaths, size);
708
709 //make sure argument path does not end with '/'
710 OUString sBinPath = path;
711 if (path.lastIndexOf('/') == (path.getLength() - 1))
712 sBinPath = path.copy(0, path.getLength() - 1);
713
714 typedef vector<OUString>::const_iterator c_it;
715 for (c_it i = vecPaths.begin(); i != vecPaths.end(); i++)
716 {
717 //the map contains e.g. jre/bin/java.exe
718 //get the directory where the executable is contained
719 OUString sHome;
720 sal_Int32 index = i->lastIndexOf('/');
721 if (index == -1)
722 {
723 //map contained only : "java.exe, then the argument
724 //path is already the home directory
725 sHome = sBinPath;
726 }
727 else
728 {
729 // jre/bin/jre -> jre/bin
730 OUString sMapPath(i->getStr(), index);
731 index = sBinPath.lastIndexOf(sMapPath);
732 if (index != -1
733 && (index + sMapPath.getLength() == sBinPath.getLength())
734 && sBinPath[index - 1] == '/')
735 {
736 sHome = OUString(sBinPath.getStr(), index - 1);
737 }
738 }
739 if (sHome.getLength() > 0)
740 {
741 ret = getJREInfoByPath(sHome, vecInfos);
742 if (ret)
743 break;
744 }
745 }
746 if (ret)
747 break;
748 }
749 return ret;
750 }
751
getAllJREInfos()752 vector<Reference<VendorBase> > getAllJREInfos()
753 {
754 vector<Reference<VendorBase> > vecInfos;
755
756 #if defined WNT
757 // Get Javas from the registry
758 createJavaInfoFromWinReg(vecInfos);
759 #endif // WNT
760
761 createJavaInfoFromJavaHome(vecInfos);
762 //this function should be called after createJavaInfoDirScan.
763 //Otherwise in SDKs Java may be started twice
764 createJavaInfoFromPath(vecInfos);
765
766 #ifdef UNX
767 createJavaInfoDirScan(vecInfos);
768 #endif
769
770 bubbleSortVersion(vecInfos);
771 return vecInfos;
772 }
773
774
getVectorFromCharArray(char const * const * ar,int size)775 vector<OUString> getVectorFromCharArray(char const * const * ar, int size)
776 {
777 vector<OUString> vec;
778 for( int i = 0; i < size; i++)
779 {
780 OUString s(ar[i], strlen(ar[i]), RTL_TEXTENCODING_UTF8);
781 vec.push_back(s);
782 }
783 return vec;
784 }
getJREInfoByPath(const rtl::OUString & path,std::vector<rtl::Reference<VendorBase>> & vecInfos)785 bool getJREInfoByPath(const rtl::OUString& path,
786 std::vector<rtl::Reference<VendorBase> > & vecInfos)
787 {
788 bool ret = false;
789
790 rtl::Reference<VendorBase> aInfo = getJREInfoByPath(path);
791 if (aInfo.is())
792 {
793 ret = true;
794 vector<rtl::Reference<VendorBase> >::const_iterator it_impl= std::find_if(
795 vecInfos.begin(),vecInfos.end(), InfoFindSame(aInfo->getHome()));
796 if(it_impl == vecInfos.end())
797 {
798 vecInfos.push_back(aInfo);
799 }
800 }
801 return ret;
802 }
803
804 /** Checks if the path is a directory. Links are resolved.
805 In case of an error the returned string has the length 0.
806 Otherwise the returned string is the "resolved" file URL.
807 */
resolveDirPath(const OUString & path)808 OUString resolveDirPath(const OUString & path)
809 {
810 OUString ret;
811 OUString sResolved;
812 //getAbsoluteFileURL also resolves links
813 if (File::getAbsoluteFileURL(
814 OUSTR("file:///"), path, sResolved) != File::E_None)
815 return OUString();
816
817 //check if this is a valid path and if it is a directory
818 DirectoryItem item;
819 if (DirectoryItem::get(sResolved, item) == File::E_None)
820 {
821 FileStatus status(FileStatusMask_Type |
822 FileStatusMask_LinkTargetURL |
823 FileStatusMask_FileURL);
824
825 if (item.getFileStatus(status) == File::E_None
826 && status.getFileType() == FileStatus::Directory)
827 {
828 ret = sResolved;
829 }
830 }
831 else
832 return OUString();
833 return ret;
834 }
835 /** Checks if the path is a file. If it is a link to a file than
836 it is resolved.
837 */
resolveFilePath(const OUString & path)838 OUString resolveFilePath(const OUString & path)
839 {
840 OUString ret;
841 OUString sResolved;
842
843 if (File::getAbsoluteFileURL(
844 OUSTR("file:///"), path, sResolved) != File::E_None)
845 return OUString();
846
847 //check if this is a valid path to a file or and if it is a link
848 DirectoryItem item;
849 if (DirectoryItem::get(sResolved, item) == File::E_None)
850 {
851 FileStatus status(FileStatusMask_Type |
852 FileStatusMask_LinkTargetURL |
853 FileStatusMask_FileURL);
854 if (item.getFileStatus(status) == File::E_None
855 && status.getFileType() == FileStatus::Regular)
856 {
857 ret = sResolved;
858 }
859 }
860 else
861 return OUString();
862
863 return ret;
864 }
865
getJREInfoByPath(const OUString & path)866 rtl::Reference<VendorBase> getJREInfoByPath(
867 const OUString& path)
868 {
869 rtl::Reference<VendorBase> ret;
870 static vector<OUString> vecBadPaths;
871
872 static map<OUString, rtl::Reference<VendorBase> > mapJREs;
873 typedef map<OUString, rtl::Reference<VendorBase> >::const_iterator MapIt;
874 typedef map<OUString, rtl::Reference<VendorBase> > MAPJRE;
875 OUString sFilePath;
876 typedef vector<OUString>::const_iterator cit_path;
877 vector<pair<OUString, OUString> > props;
878
879 OUString sResolvedDir = resolveDirPath(path);
880 // If this path is invalid then there is no chance to find a JRE here
881 if (sResolvedDir.getLength() == 0)
882 return 0;
883
884 //check if the directory path is good, that is a JRE was already recognized.
885 //Then we need not detect it again
886 //For example, a sun JKD contains <jdk>/bin/java and <jdk>/jre/bin/java.
887 //When <jdk>/bin/java has been found then we need not find <jdk>/jre/bin/java.
888 //Otherwise we would execute java two times for evers JDK found.
889 MapIt entry2 = find_if(mapJREs.begin(), mapJREs.end(),
890 SameOrSubDirJREMap(sResolvedDir));
891 if (entry2 != mapJREs.end())
892 {
893 JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin" SAL_DLLEXTENSION ": ")
894 + OUSTR("JRE found again (detected before): ") + sResolvedDir
895 + OUSTR(".\n"));
896 return entry2->second;
897 }
898
899 for ( sal_Int32 pos = 0;
900 gVendorMap[pos].sVendorName != NULL; ++pos )
901 {
902 vector<OUString> vecPaths;
903 getJavaExePaths_func pFunc = gVendorMap[pos].getJavaFunc;
904
905 int size = 0;
906 char const* const* arExePaths = (*pFunc)(&size);
907 vecPaths = getVectorFromCharArray(arExePaths, size);
908
909 bool bBreak = false;
910 typedef vector<OUString>::const_iterator c_it;
911 for (c_it i = vecPaths.begin(); i != vecPaths.end(); i++)
912 {
913 //if the path is a link, then resolve it
914 //check if the executable exists at all
915
916 //path can be only "file:///". Then do not append a '/'
917 //sizeof counts the terminating 0
918 OUString sFullPath;
919 if (path.getLength() == sizeof("file:///") - 1)
920 sFullPath = sResolvedDir + (*i);
921 else
922 sFullPath = sResolvedDir +
923 OUString(RTL_CONSTASCII_USTRINGPARAM("/")) + (*i);
924
925
926 sFilePath = resolveFilePath(sFullPath);
927
928 if (sFilePath.getLength() == 0)
929 {
930 //The file path (to java exe) is not valid
931 cit_path ifull = find(vecBadPaths.begin(), vecBadPaths.end(), sFullPath);
932 if (ifull == vecBadPaths.end())
933 vecBadPaths.push_back(sFullPath);
934 continue;
935 }
936
937 cit_path ifile = find(vecBadPaths.begin(), vecBadPaths.end(), sFilePath);
938 if (ifile != vecBadPaths.end())
939 continue;
940
941 MapIt entry = mapJREs.find(sFilePath);
942 if (entry != mapJREs.end())
943 {
944 JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin" SAL_DLLEXTENSION ": ")
945 + OUSTR("JRE found again (detected before): ") + sFilePath
946 + OUSTR(".\n"));
947
948 return entry->second;
949 }
950
951 bool bProcessRun= false;
952 if (getJavaProps(sFilePath, props, & bProcessRun) == false)
953 {
954 //The java executable could not be run or the system properties
955 //could not be retrieved. We can assume that this java is corrupt.
956 vecBadPaths.push_back(sFilePath);
957 //If there was a java executable, that could be run but we did not get
958 //the system properties, then we also assume that the whole Java installation
959 //does not work. In a jdk there are two executables. One in jdk/bin and the other
960 //in jdk/jre/bin. We do not search any further, because we assume that if one java
961 //does not work then the other does not work as well. This saves us to run java
962 //again which is quite costly.
963 if (bProcessRun == true)
964 {
965 // 1.3.1 special treatment: jdk/bin/java and /jdk/jre/bin/java are links to
966 //a script, named .java_wrapper. The script starts jdk/bin/sparc/native_threads/java
967 //or jdk/jre/bin/sparc/native_threads/java. The script uses the name with which it was
968 //invoked to build the path to the executable. It we start the script directy as .java_wrapper
969 //then it tries to start a jdk/.../native_threads/.java_wrapper. Therefore the link, which
970 //is named java, must be used to start the script.
971 getJavaProps(sFullPath, props, & bProcessRun);
972 // Either we found a working 1.3.1
973 //Or the java is broken. In both cases we stop searchin under this "root" directory
974 bBreak = true;
975 break;
976 }
977 //sFilePath is no working java executable. We continue with another possible
978 //path.
979 else
980 {
981 continue;
982 }
983 }
984 //sFilePath is a java and we could get the system properties. We proceed with this
985 //java.
986 else
987 {
988 bBreak = true;
989 break;
990 }
991 }
992 if (bBreak)
993 break;
994 }
995
996 if (props.size() == 0)
997 return rtl::Reference<VendorBase>();
998
999 //find java.vendor property
1000 typedef vector<pair<OUString, OUString> >::const_iterator c_ip;
1001 OUString sVendor(RTL_CONSTASCII_USTRINGPARAM("java.vendor"));
1002 OUString sVendorName;
1003
1004 for (c_ip i = props.begin(); i != props.end(); i++)
1005 {
1006 if (sVendor.equals(i->first))
1007 {
1008 sVendorName = i->second;
1009 break;
1010 }
1011 }
1012
1013 if (sVendorName.getLength() > 0)
1014 {
1015 //find the creator func for the respective vendor name
1016 for ( sal_Int32 c = 0;
1017 gVendorMap[c].sVendorName != NULL; ++c )
1018 {
1019 OUString sNameMap(gVendorMap[c].sVendorName, strlen(gVendorMap[c].sVendorName),
1020 RTL_TEXTENCODING_ASCII_US);
1021 if (sNameMap.equals(sVendorName))
1022 {
1023 ret = createInstance(gVendorMap[c].createFunc, props);
1024 break;
1025 }
1026 }
1027 if ( !ret.is() )
1028 {
1029 // As of Java 8u222 the vendor name can be anything, so try the most generic one
1030 OUString genericVendorName(RTL_CONSTASCII_USTRINGPARAM("OpenJDK"));
1031 for ( sal_Int32 c = 0;
1032 gVendorMap[c].sVendorName != NULL; ++c )
1033 {
1034 OUString sNameMap(gVendorMap[c].sVendorName, strlen(gVendorMap[c].sVendorName),
1035 RTL_TEXTENCODING_ASCII_US);
1036 if (sNameMap.equals(genericVendorName))
1037 {
1038 for (vector<pair<OUString, OUString> >::iterator i = props.begin(); i != props.end(); i++)
1039 {
1040 if (sVendor.equals(i->first))
1041 i->second = genericVendorName;
1042 }
1043 ret = createInstance(gVendorMap[c].createFunc, props);
1044 break;
1045 }
1046 }
1047 }
1048 }
1049 if (ret.is() == false)
1050 vecBadPaths.push_back(sFilePath);
1051 else
1052 {
1053 JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin" SAL_DLLEXTENSION ": ")
1054 + OUSTR("Found JRE: ") + sResolvedDir
1055 + OUSTR(" \n at: ") + path + OUSTR(".\n"));
1056
1057 mapJREs.insert(MAPJRE::value_type(sResolvedDir, ret));
1058 mapJREs.insert(MAPJRE::value_type(sFilePath, ret));
1059 }
1060
1061 return ret;
1062 }
1063
createInstance(createInstance_func pFunc,vector<pair<OUString,OUString>> properties)1064 Reference<VendorBase> createInstance(createInstance_func pFunc,
1065 vector<pair<OUString, OUString> > properties)
1066 {
1067
1068 Reference<VendorBase> aBase = (*pFunc)();
1069 if (aBase.is())
1070 {
1071 if (aBase->initialize(properties) == false)
1072 aBase = 0;
1073 }
1074 return aBase;
1075 }
1076
getDirFromFile(const OUString & usFilePath)1077 inline OUString getDirFromFile(const OUString& usFilePath)
1078 {
1079 sal_Int32 index= usFilePath.lastIndexOf('/');
1080 return OUString(usFilePath.getStr(), index);
1081 }
1082
createJavaInfoFromPath(vector<rtl::Reference<VendorBase>> & vecInfos)1083 void createJavaInfoFromPath(vector<rtl::Reference<VendorBase> >& vecInfos)
1084 {
1085 // Get Java from PATH environment variable
1086 static OUString sCurDir(RTL_CONSTASCII_USTRINGPARAM("."));
1087 static OUString sParentDir(RTL_CONSTASCII_USTRINGPARAM(".."));
1088 char *szPath= getenv("PATH");
1089 if(szPath)
1090 {
1091 OUString usAllPath(szPath, strlen(szPath), osl_getThreadTextEncoding());
1092 sal_Int32 nIndex = 0;
1093 do
1094 {
1095 OUString usToken = usAllPath.getToken( 0, SAL_PATHSEPARATOR, nIndex );
1096 OUString usTokenUrl;
1097 if(File::getFileURLFromSystemPath(usToken, usTokenUrl) == File::E_None)
1098 {
1099 if(usTokenUrl.getLength())
1100 {
1101 OUString usBin;
1102 // "."
1103 if(usTokenUrl.equals(sCurDir))
1104 {
1105 OUString usWorkDirUrl;
1106 if(osl_Process_E_None == osl_getProcessWorkingDir(&usWorkDirUrl.pData))
1107 usBin= usWorkDirUrl;
1108 }
1109 // ".."
1110 else if(usTokenUrl.equals(sParentDir))
1111 {
1112 OUString usWorkDir;
1113 if(osl_Process_E_None == osl_getProcessWorkingDir(&usWorkDir.pData))
1114 usBin= getDirFromFile(usWorkDir);
1115 }
1116 else
1117 {
1118 usBin = usTokenUrl;
1119 }
1120 if(usBin.getLength())
1121 {
1122 getJREInfoFromBinPath(usBin, vecInfos);
1123 }
1124 }
1125 }
1126 }
1127 while ( nIndex >= 0 );
1128 }
1129 }
1130
createJavaInfoFromJavaHome(vector<rtl::Reference<VendorBase>> & vecInfos)1131 void createJavaInfoFromJavaHome(vector<rtl::Reference<VendorBase> >& vecInfos)
1132 {
1133 // Get Java from JAVA_HOME environment
1134 char *szJavaHome= getenv("JAVA_HOME");
1135 if(szJavaHome)
1136 {
1137 OUString sHome(szJavaHome,strlen(szJavaHome),osl_getThreadTextEncoding());
1138 OUString sHomeUrl;
1139 if(File::getFileURLFromSystemPath(sHome, sHomeUrl) == File::E_None)
1140 {
1141 getJREInfoByPath(sHomeUrl, vecInfos);
1142 }
1143 }
1144 }
1145
makeDriveLetterSame(OUString * fileURL)1146 bool makeDriveLetterSame(OUString * fileURL)
1147 {
1148 bool ret = false;
1149 DirectoryItem item;
1150 if (DirectoryItem::get(*fileURL, item) == File::E_None)
1151 {
1152 FileStatus status(FileStatusMask_FileURL);
1153 if (item.getFileStatus(status) == File::E_None)
1154 {
1155 *fileURL = status.getFileURL();
1156 ret = true;
1157 }
1158 }
1159 return ret;
1160 }
1161
1162 #ifdef UNX
1163 #ifdef SOLARIS
1164
createJavaInfoDirScan(vector<rtl::Reference<VendorBase>> & vecInfos)1165 void createJavaInfoDirScan(vector<rtl::Reference<VendorBase> >& vecInfos)
1166 {
1167 JFW_TRACE2(OUSTR("\n[Java framework] Checking \"/usr/jdk/latest\"\n"));
1168 getJREInfoByPath(OUSTR("file:////usr/jdk/latest"), vecInfos);
1169 }
1170
1171 #else
createJavaInfoDirScan(vector<rtl::Reference<VendorBase>> & vecInfos)1172 void createJavaInfoDirScan(vector<rtl::Reference<VendorBase> >& vecInfos)
1173 {
1174 OUString excMessage = OUSTR("[Java framework] sunjavaplugin: "
1175 "Error in function createJavaInfoDirScan in util.cxx.");
1176 int cJavaNames= sizeof(g_arJavaNames) / sizeof(char*);
1177 boost::scoped_array<OUString> sarJavaNames(new OUString[cJavaNames]);
1178 OUString *arNames = sarJavaNames.get();
1179 for(int i= 0; i < cJavaNames; i++)
1180 arNames[i] = OUString(g_arJavaNames[i], strlen(g_arJavaNames[i]),
1181 RTL_TEXTENCODING_UTF8);
1182
1183 int cSearchPaths= sizeof(g_arSearchPaths) / sizeof(g_arSearchPaths[0]);
1184 boost::scoped_array<OUString> sarPathNames(new OUString[cSearchPaths]);
1185 OUString *arPaths = sarPathNames.get();
1186 for(int c = 0; c < cSearchPaths; c++)
1187 arPaths[c] = OUString(g_arSearchPaths[c].path, strlen(g_arSearchPaths[c].path),
1188 RTL_TEXTENCODING_UTF8);
1189
1190 int cCollectDirs = sizeof(g_arCollectDirs) / sizeof(char*);
1191 boost::scoped_array<OUString> sarCollectDirs(new OUString[cCollectDirs]);
1192 OUString *arCollectDirs = sarCollectDirs.get();
1193 for(int d = 0; d < cCollectDirs; d++)
1194 arCollectDirs[d] = OUString(g_arCollectDirs[d], strlen(g_arCollectDirs[d]),
1195 RTL_TEXTENCODING_UTF8);
1196
1197
1198
1199 OUString usFile(RTL_CONSTASCII_USTRINGPARAM("file:///"));
1200 for( int ii = 0; ii < cSearchPaths; ii ++)
1201 {
1202 OUString usDir1(usFile + arPaths[ii]);
1203 DirectoryItem item;
1204 if(DirectoryItem::get(usDir1, item) == File::E_None)
1205 {
1206 for(int j= 0; j < cCollectDirs; j++)
1207 {
1208 OUString usDir2(usDir1 + arCollectDirs[j]);
1209 // prevent that we scan the whole /usr/bin, /usr/lib, etc directories
1210 if (g_arSearchPaths[ii].searchImmediateContents || arCollectDirs[j] != OUString())
1211 {
1212 //usr/java/xxx
1213 //Examin every subdirectory
1214 Directory aCollectionDir(usDir2);
1215
1216 Directory::RC openErr = aCollectionDir.open();
1217 switch (openErr)
1218 {
1219 case File::E_None:
1220 break;
1221 case File::E_NOENT:
1222 case File::E_NOTDIR:
1223 continue;
1224 case File::E_ACCES:
1225 JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin: "
1226 "Could not read directory ") + usDir2 +
1227 OUSTR(" because of missing access rights."));
1228 continue;
1229 default:
1230 JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin: "
1231 "Could not read directory ")
1232 + usDir2 + OUSTR(". Osl file error: ")
1233 + OUString::valueOf((sal_Int32) openErr));
1234 continue;
1235 }
1236
1237 DirectoryItem curIt;
1238 File::RC errNext = File::E_None;
1239 while( (errNext = aCollectionDir.getNextItem(curIt)) == File::E_None)
1240 {
1241 FileStatus aStatus(FileStatusMask_FileURL);
1242 File::RC errStatus = File::E_None;
1243 if ((errStatus = curIt.getFileStatus(aStatus)) != File::E_None)
1244 {
1245 JFW_TRACE2(excMessage + OUSTR("getFileStatus failed with error ")
1246 + OUString::valueOf((sal_Int32) errStatus));
1247 continue;
1248 }
1249 JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin: "
1250 "Checking if directory: ") + aStatus.getFileURL() +
1251 OUSTR(" is a Java. \n"));
1252
1253 getJREInfoByPath(aStatus.getFileURL(),vecInfos);
1254 #ifdef MACOSX
1255 // On macOS a JDK bundle's home is at <bundle>/Contents/Home
1256 // (e.g. under /Library/Java/JavaVirtualMachines/<jdk>/),
1257 // not the bundle directory itself, so also probe there.
1258 getJREInfoByPath(
1259 aStatus.getFileURL() +
1260 OUSTR("/Contents/Home"), vecInfos);
1261 #endif
1262 }
1263
1264 JFW_ENSURE(errNext == File::E_None || errNext == File::E_NOENT,
1265 OUSTR("[Java framework] sunjavaplugin: "
1266 "Error while iterating over contens of ")
1267 + usDir2 + OUSTR(". Osl file error: ")
1268 + OUString::valueOf((sal_Int32) openErr));
1269 }
1270 }
1271 for (int j= 0; j < cJavaNames; j++)
1272 {
1273 //When we look directly into a dir like /usr/bin, /usr/lib, etc. then we only
1274 //look for certain java directories, such as jre, jdk, etc. Whe do not want
1275 //to examine the whole directory because of performance reasons.
1276
1277 // usFile arNames[j]
1278 // <------> <->
1279 // file:///usr/lib/jvm
1280 // <------>
1281 // arPaths[ii] (usDir1)
1282 //
1283 OUString usDir3(usDir1 + arNames[j]);
1284
1285 DirectoryItem item3;
1286 if(DirectoryItem::get(usDir3, item3) == File::E_None)
1287 {
1288 //remove trailing '/'
1289 sal_Int32 islash = usDir3.lastIndexOf('/');
1290 if (islash == usDir3.getLength() - 1
1291 && (islash
1292 > RTL_CONSTASCII_LENGTH("file://")))
1293 usDir3 = usDir3.copy(0, islash);
1294 getJREInfoByPath(usDir3,vecInfos);
1295 }
1296 }
1297 }
1298 }
1299 }
1300 #endif // ifdef SOLARIS
1301 #endif // ifdef UNX
1302 }
1303