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