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