1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla Communicator.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corp.
19 * Portions created by the Initial Developer are Copyright (C) 2003
20 * the Initial Developer. All Rights Reserved.
23 * Sean Su <ssu@netscape.com>
24 * Benjamin Smedberg <benjamin@smedbergs.us>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsXPCOMGlue.h"
42 #include "nsAutoPtr.h"
43 #include "nsINIParser.h"
44 #include "nsVersionComparator.h"
45 #include "nsXPCOMPrivate.h"
54 # include <mbstring.h>
56 # define snprintf _snprintf
61 #elif defined(XP_MACOSX)
62 # include <CoreFoundation/CoreFoundation.h>
65 #elif defined(XP_UNIX)
67 # include <sys/param.h>
69 #elif defined(XP_BEOS)
70 # include <FindDirectory.h>
73 # include <sys/param.h>
77 # include <FindDirectory.h>
83 * Like strncat, appends a buffer to another buffer. This is where the
84 * similarity ends. Firstly, the "count" here is the total size of the buffer
85 * (not the number of chars to append. Secondly, the function returns PR_FALSE
86 * if the buffer is not long enough to hold the concatenated string.
88 static PRBool
safe_strncat(char *dest
, const char *append
, PRUint32 count
)
90 char *end
= dest
+ count
- 1;
92 // skip to the end of dest
96 while (*append
&& dest
< end
) {
103 return *append
== '\0';
108 CheckVersion(const PRUnichar
* toCheck
,
109 const GREVersionRange
*versions
,
110 PRUint32 versionsLength
);
113 CheckVersion(const char* toCheck
,
114 const GREVersionRange
*versions
,
115 PRUint32 versionsLength
);
118 #if defined(XP_MACOSX)
121 GRE_FindGREFramework(const char* rootPath
,
122 const GREVersionRange
*versions
,
123 PRUint32 versionsLength
,
124 const GREProperty
*properties
,
125 PRUint32 propertiesLength
,
126 char* buffer
, PRUint32 buflen
);
128 #elif defined(XP_UNIX) || defined(XP_BEOS)
131 GRE_GetPathFromConfigDir(const char* dirname
,
132 const GREVersionRange
*versions
,
133 PRUint32 versionsLength
,
134 const GREProperty
*properties
,
135 PRUint32 propertiesLength
,
136 char* buffer
, PRUint32 buflen
);
139 GRE_GetPathFromConfigFile(const char* filename
,
140 const GREVersionRange
*versions
,
141 PRUint32 versionsLength
,
142 const GREProperty
*properties
,
143 PRUint32 propertiesLength
,
144 char* buffer
, PRUint32 buflen
);
146 #elif defined(XP_WIN)
149 GRE_GetPathFromRegKey(HKEY aRegKey
,
150 const GREVersionRange
*versions
,
151 PRUint32 versionsLength
,
152 const GREProperty
*properties
,
153 PRUint32 propertiesLength
,
154 char* buffer
, PRUint32 buflen
);
159 GRE_GetGREPathWithProperties(const GREVersionRange
*versions
,
160 PRUint32 versionsLength
,
161 const GREProperty
*properties
,
162 PRUint32 propertiesLength
,
163 char *aBuffer
, PRUint32 aBufLen
)
165 #ifdef TARGET_XPCOM_ABI
166 // append the ABI to the properties to match only binary
168 static const GREProperty kExtraProperty
=
169 { "abi", TARGET_XPCOM_ABI
};
171 GREProperty
*allProperties
= new GREProperty
[propertiesLength
+ 1];
173 return NS_ERROR_OUT_OF_MEMORY
;
175 for (PRUint32 i
=0; i
<propertiesLength
; i
++) {
176 allProperties
[i
].property
= properties
[i
].property
;
177 allProperties
[i
].value
= properties
[i
].value
;
179 allProperties
[propertiesLength
].property
= kExtraProperty
.property
;
180 allProperties
[propertiesLength
].value
= kExtraProperty
.value
;
181 PRUint32 allPropertiesLength
= propertiesLength
+ 1;
183 const GREProperty
*allProperties
= properties
;
184 PRUint32 allPropertiesLength
= propertiesLength
;
187 // if GRE_HOME is in the environment, use that GRE
188 const char* env
= getenv("GRE_HOME");
191 snprintf(p
, sizeof(p
), "%s" XPCOM_FILE_PATH_SEPARATOR XPCOM_DLL
, env
);
192 p
[sizeof(p
) - 1] = '\0';
195 if (realpath(p
, aBuffer
))
201 WCHAR path
[MAX_PATH
];
202 MultiByteToWideChar(CP_ACP
, 0, p
, -1, path
, MAX_PATH
);
203 _wfullpath(dir
,path
,MAX_PATH
);
204 WideCharToMultiByte(CP_ACP
, 0, dir
, -1, aBuffer
, MAX_PATH
, NULL
, NULL
);
211 if (_fullpath(aBuffer
, p
, aBufLen
))
214 // realpath on OS/2 returns a unix-ized path, so re-native-ize
215 if (realpath(p
, aBuffer
)) {
216 for (char* ptr
= strchr(aBuffer
, '/'); ptr
; ptr
= strchr(ptr
, '/'))
223 result
= path
.SetTo(p
,0,true);
226 sprintf(aBuffer
, path
.Path());
231 // xxxbsmedberg: other platforms should have a "make absolute" function
234 if (strlen(p
) >= aBufLen
)
235 return NS_ERROR_FILE_NAME_TOO_LONG
;
242 // the Gecko bits that sit next to the application or in the LD_LIBRARY_PATH
243 env
= getenv("USE_LOCAL_GRE");
252 // Check the bundle first, for <bundle>/Contents/Frameworks/XUL.framework/libxpcom.dylib
253 CFBundleRef appBundle
= CFBundleGetMainBundle();
255 CFURLRef fwurl
= CFBundleCopyPrivateFrameworksURL(appBundle
);
256 CFURLRef absfwurl
= nsnull
;
258 absfwurl
= CFURLCopyAbsoluteURL(fwurl
);
264 CFURLCreateCopyAppendingPathComponent(NULL
, absfwurl
,
265 CFSTR(GRE_FRAMEWORK_NAME
),
270 CFURLCreateCopyAppendingPathComponent(NULL
, xulurl
,
271 CFSTR("libxpcom.dylib"),
275 char tbuffer
[MAXPATHLEN
];
277 if (CFURLGetFileSystemRepresentation(xpcomurl
, PR_TRUE
,
280 access(tbuffer
, R_OK
| X_OK
) == 0) {
281 if (!realpath(tbuffer
, aBuffer
)) {
299 // Check ~/Library/Frameworks/XUL.framework/Versions/<version>/libxpcom.dylib
300 const char *home
= getenv("HOME");
301 if (home
&& *home
&& GRE_FindGREFramework(home
,
302 versions
, versionsLength
,
303 allProperties
, allPropertiesLength
,
308 // Check /Library/Frameworks/XUL.framework/Versions/<version>/libxpcom.dylib
309 if (GRE_FindGREFramework("",
310 versions
, versionsLength
,
311 allProperties
, allPropertiesLength
,
316 #elif defined(XP_UNIX)
317 env
= getenv("MOZ_GRE_CONF");
318 if (env
&& GRE_GetPathFromConfigFile(env
,
319 versions
, versionsLength
,
320 allProperties
, allPropertiesLength
,
325 env
= getenv("HOME");
327 char buffer
[MAXPATHLEN
];
329 // Look in ~/.gre.config
331 snprintf(buffer
, sizeof(buffer
),
332 "%s" XPCOM_FILE_PATH_SEPARATOR GRE_CONF_NAME
, env
);
334 if (GRE_GetPathFromConfigFile(buffer
,
335 versions
, versionsLength
,
336 allProperties
, allPropertiesLength
,
341 // Look in ~/.gre.d/*.conf
343 snprintf(buffer
, sizeof(buffer
),
344 "%s" XPCOM_FILE_PATH_SEPARATOR GRE_USER_CONF_DIR
, env
);
346 if (GRE_GetPathFromConfigDir(buffer
,
347 versions
, versionsLength
,
348 allProperties
, allPropertiesLength
,
354 // Look for a global /etc/gre.conf file
355 if (GRE_GetPathFromConfigFile(GRE_CONF_PATH
,
356 versions
, versionsLength
,
357 allProperties
, allPropertiesLength
,
362 // Look for a group of config files in /etc/gre.d/
363 if (GRE_GetPathFromConfigDir(GRE_CONF_DIR
,
364 versions
, versionsLength
,
365 allProperties
, allPropertiesLength
,
370 #elif defined(XP_BEOS)
371 env
= getenv("MOZ_GRE_CONF");
372 if (env
&& GRE_GetPathFromConfigFile(env
,
373 versions
, versionsLength
,
374 allProperties
, allPropertiesLength
,
380 if (find_directory(B_USER_SETTINGS_DIRECTORY
, 0, 0, p
, MAXPATHLEN
)) {
381 char buffer
[MAXPATHLEN
];
383 // Look in B_USER_SETTINGS_DIRECTORY/gre.config
384 snprintf(buffer
, sizeof(buffer
),
385 "%s" XPCOM_FILE_PATH_SEPARATOR GRE_CONF_NAME
, p
);
387 if (GRE_GetPathFromConfigFile(buffer
,
388 versions
, versionsLength
,
389 allProperties
, allPropertiesLength
,
394 // Look in B_USER_SETTINGS_DIRECTORY/gre.d/*.conf
395 snprintf(buffer
, sizeof(buffer
),
396 "%s" XPCOM_FILE_PATH_SEPARATOR GRE_CONF_DIR
, p
);
398 if (GRE_GetPathFromConfigDir(buffer
,
399 versions
, versionsLength
,
400 allProperties
, allPropertiesLength
,
406 // Hope Zeta OS and Haiku OS multiuser versions will respect BeBook,
407 // for BeOS R5 COMMON and USER are equal
408 if (find_directory(B_COMMON_SETTINGS_DIRECTORY
, 0, 0, p
, MAXPATHLEN
)) {
409 char buffer
[MAXPATHLEN
];
411 // Look for a B_COMMON_SETTINGS_DIRECTORY/gre.conf file
412 snprintf(buffer
, sizeof(buffer
),
413 "%s" XPCOM_FILE_PATH_SEPARATOR GRE_CONF_PATH
, p
);
414 if (GRE_GetPathFromConfigFile(buffer
,
415 versions
, versionsLength
,
416 allProperties
, allPropertiesLength
,
421 // Look for a group of config files in B_COMMON_SETTINGS_DIRECTORY/gre.d/
422 snprintf(buffer
, sizeof(buffer
),
423 "%s" XPCOM_FILE_PATH_SEPARATOR GRE_CONF_DIR
, p
);
424 if (GRE_GetPathFromConfigDir(buffer
,
425 versions
, versionsLength
,
426 allProperties
, allPropertiesLength
,
432 #elif defined(XP_WIN)
435 // A couple of key points here:
436 // 1. Note the usage of the "Software\\mozilla.org\\GRE" subkey - this allows
437 // us to have multiple versions of GREs on the same machine by having
438 // subkeys such as 1.0, 1.1, 2.0 etc. under it.
439 // 2. In this sample below we're looking for the location of GRE version 1.2
440 // i.e. we're compatible with GRE 1.2 and we're trying to find it's install
443 // Please see http://www.mozilla.org/projects/embedding/GRE.html for
446 if (::RegOpenKeyExW(HKEY_CURRENT_USER
, GRE_WIN_REG_LOC
, 0,
447 KEY_READ
, &hRegKey
) == ERROR_SUCCESS
) {
448 PRBool ok
= GRE_GetPathFromRegKey(hRegKey
,
449 versions
, versionsLength
,
450 allProperties
, allPropertiesLength
,
452 ::RegCloseKey(hRegKey
);
458 if (::RegOpenKeyExW(HKEY_LOCAL_MACHINE
, GRE_WIN_REG_LOC
, 0,
459 KEY_ENUMERATE_SUB_KEYS
, &hRegKey
) == ERROR_SUCCESS
) {
460 PRBool ok
= GRE_GetPathFromRegKey(hRegKey
,
461 versions
, versionsLength
,
462 allProperties
, allPropertiesLength
,
464 ::RegCloseKey(hRegKey
);
471 return NS_ERROR_FAILURE
;
475 CheckVersion(const char* toCheck
,
476 const GREVersionRange
*versions
,
477 PRUint32 versionsLength
)
480 for (const GREVersionRange
*versionsEnd
= versions
+ versionsLength
;
481 versions
< versionsEnd
;
483 PRInt32 c
= NS_CompareVersions(toCheck
, versions
->lower
);
487 if (!c
&& !versions
->lowerInclusive
)
490 c
= NS_CompareVersions(toCheck
, versions
->upper
);
494 if (!c
&& !versions
->upperInclusive
)
505 // Allocate an array of characters using new[], converting from UTF8 to UTF-16.
506 // @note Use nsAutoArrayPtr for this result.
509 ConvertUTF8toNewUTF16(const char *cstr
)
511 int len
= MultiByteToWideChar(CP_UTF8
, 0, cstr
, -1, NULL
, 0);
512 WCHAR
*wstr
= new WCHAR
[len
];
513 MultiByteToWideChar(CP_UTF8
, 0, cstr
, -1, wstr
, len
);
517 typedef nsAutoArrayPtr
<PRUnichar
> AutoWString
;
520 CheckVersion(const PRUnichar
* toCheck
,
521 const GREVersionRange
*versions
,
522 PRUint32 versionsLength
)
524 for (const GREVersionRange
*versionsEnd
= versions
+ versionsLength
;
525 versions
< versionsEnd
;
527 AutoWString
wlower(ConvertUTF8toNewUTF16(versions
->lower
));
528 PRInt32 c
= NS_CompareVersions(toCheck
, wlower
);
532 if (!c
&& !versions
->lowerInclusive
)
535 AutoWString
wupper(ConvertUTF8toNewUTF16(versions
->upper
));
536 c
= NS_CompareVersions(toCheck
, wupper
);
540 if (!c
&& !versions
->upperInclusive
)
553 GRE_FindGREFramework(const char* rootPath
,
554 const GREVersionRange
*versions
,
555 PRUint32 versionsLength
,
556 const GREProperty
*properties
,
557 PRUint32 propertiesLength
,
558 char* buffer
, PRUint32 buflen
)
560 PRBool found
= PR_FALSE
;
562 snprintf(buffer
, buflen
,
563 "%s/Library/Frameworks/" GRE_FRAMEWORK_NAME
"/Versions", rootPath
);
564 DIR *dir
= opendir(buffer
);
566 struct dirent
*entry
;
567 while (!found
&& (entry
= readdir(dir
))) {
568 if (CheckVersion(entry
->d_name
, versions
, versionsLength
)) {
569 snprintf(buffer
, buflen
,
570 "%s/Library/Frameworks/" GRE_FRAMEWORK_NAME
571 "/Versions/%s/" XPCOM_DLL
, rootPath
, entry
->d_name
);
572 if (access(buffer
, R_OK
| X_OK
) == 0)
587 #elif defined(XP_UNIX) || defined(XP_BEOS)
589 static PRBool
IsConfFile(const char *filename
)
591 const char *dot
= strrchr(filename
, '.');
593 return (dot
&& strcmp(dot
, ".conf") == 0);
597 GRE_GetPathFromConfigDir(const char* dirname
,
598 const GREVersionRange
*versions
,
599 PRUint32 versionsLength
,
600 const GREProperty
*properties
,
601 PRUint32 propertiesLength
,
602 char* buffer
, PRUint32 buflen
)
604 // Open the directory provided and try to read any files in that
605 // directory that end with .conf. We look for an entry that might
606 // point to the GRE that we're interested in.
607 DIR *dir
= opendir(dirname
);
611 PRBool found
= PR_FALSE
;
612 struct dirent
*entry
;
614 while (!found
&& (entry
= readdir(dir
))) {
616 // Only look for files that end in .conf
617 // IsConfFile will skip "." and ".."
618 if (!IsConfFile(entry
->d_name
))
621 char fullPath
[MAXPATHLEN
];
622 snprintf(fullPath
, sizeof(fullPath
), "%s" XPCOM_FILE_PATH_SEPARATOR
"%s",
623 dirname
, entry
->d_name
);
625 found
= GRE_GetPathFromConfigFile(fullPath
,
626 versions
, versionsLength
,
627 properties
, propertiesLength
,
636 #define READ_BUFFER_SIZE 1024
641 const GREVersionRange
*versions
;
642 PRUint32 versionsLength
;
643 const GREProperty
*properties
;
644 PRUint32 propertiesLength
;
651 CheckINIHeader(const char *aHeader
, void *aClosure
)
655 INIClosure
*c
= reinterpret_cast<INIClosure
*>(aClosure
);
657 if (!CheckVersion(aHeader
, c
->versions
, c
->versionsLength
))
660 const GREProperty
*properties
= c
->properties
;
661 const GREProperty
*endProperties
= properties
+ c
->propertiesLength
;
662 for (; properties
< endProperties
; ++properties
) {
663 char buffer
[MAXPATHLEN
];
664 rv
= c
->parser
->GetString(aHeader
, properties
->property
,
665 buffer
, sizeof(buffer
));
669 if (strcmp(buffer
, properties
->value
))
673 rv
= c
->parser
->GetString(aHeader
, "GRE_PATH", c
->pathBuffer
, c
->buflen
);
677 if (!safe_strncat(c
->pathBuffer
, "/" XPCOM_DLL
, c
->buflen
) ||
678 access(c
->pathBuffer
, R_OK
))
681 // We found a good GRE! Stop looking.
687 GRE_GetPathFromConfigFile(const char* filename
,
688 const GREVersionRange
*versions
,
689 PRUint32 versionsLength
,
690 const GREProperty
*properties
,
691 PRUint32 propertiesLength
,
692 char* pathBuffer
, PRUint32 buflen
)
695 nsresult rv
= parser
.Init(filename
);
701 versions
, versionsLength
,
702 properties
, propertiesLength
,
707 parser
.GetSections(CheckINIHeader
, &c
);
711 #elif defined(XP_WIN)
714 CopyWithEnvExpansion(PRUnichar
* aDest
, const PRUnichar
* aSource
, PRUint32 aBufLen
,
719 if (wcslen(aSource
) >= aBufLen
)
722 wcscpy(aDest
, aSource
);
726 if (ExpandEnvironmentStringsW(aSource
, aDest
, aBufLen
) > aBufLen
)
732 // Whoops! We expected REG_SZ or REG_EXPAND_SZ, what happened here?
738 GRE_GetPathFromRegKey(HKEY aRegKey
,
739 const GREVersionRange
*versions
,
740 PRUint32 versionsLength
,
741 const GREProperty
*properties
,
742 PRUint32 propertiesLength
,
743 char* aBuffer
, PRUint32 aBufLen
)
745 // Formerly, GREs were registered at the registry key
746 // HKLM/Software/mozilla.org/GRE/<version> valuepair GreHome=Path.
747 // Nowadays, they are registered in any subkey of
748 // Software/mozilla.org/GRE, with the following valuepairs:
749 // Version=<version> (REG_SZ)
750 // GreHome=<path> (REG_SZ or REG_EXPAND_SZ)
751 // <Property>=<value> (REG_SZ)
753 // Additional meta-info may be available in the future, including
754 // localization info and other information which might be pertinent
755 // to selecting one GRE over another.
757 // When a GRE is being registered, it should try to register itself at
758 // HKLM/Software/mozilla.org/GRE/<Version> first, to preserve compatibility
759 // with older glue. If this key is already taken (i.e. there is more than
760 // one GRE of that version installed), it should append a unique number to
761 // the version, for example:
762 // 1.1 (already in use), 1.1_1, 1.1_2, etc...
765 PRUnichar buffer
[MAXPATHLEN
+ 1];
768 PRUnichar name
[MAXPATHLEN
+ 1];
769 DWORD nameLen
= MAXPATHLEN
;
770 if (::RegEnumKeyExW(aRegKey
, i
, name
, &nameLen
, NULL
, NULL
, NULL
, NULL
) !=
776 if (::RegOpenKeyExW(aRegKey
, name
, 0, KEY_QUERY_VALUE
, &subKey
) !=
781 PRUnichar version
[40];
782 DWORD versionlen
= 40;
783 PRUnichar pathbuf
[MAXPATHLEN
+ 1];
787 PRBool ok
= PR_FALSE
;
789 if (::RegQueryValueExW(subKey
, L
"Version", NULL
, NULL
,
790 (BYTE
*) version
, &versionlen
) == ERROR_SUCCESS
&&
791 CheckVersion(version
, versions
, versionsLength
)) {
794 const GREProperty
*props
= properties
;
795 const GREProperty
*propsEnd
= properties
+ propertiesLength
;
796 for (; ok
&& props
< propsEnd
; ++props
) {
797 pathlen
= MAXPATHLEN
+ 1;
799 AutoWString
wproperty(ConvertUTF8toNewUTF16(props
->property
));
800 AutoWString
wvalue(ConvertUTF8toNewUTF16(props
->value
));
801 if (::RegQueryValueExW(subKey
, wproperty
, NULL
, &pathtype
,
802 (BYTE
*) pathbuf
, &pathlen
) != ERROR_SUCCESS
||
803 wcscmp(pathbuf
, wvalue
))
807 pathlen
= sizeof(pathbuf
);
809 (!::RegQueryValueExW(subKey
, L
"GreHome", NULL
, &pathtype
,
810 (BYTE
*) pathbuf
, &pathlen
) == ERROR_SUCCESS
||
812 !CopyWithEnvExpansion(buffer
, pathbuf
, MAXPATHLEN
, pathtype
))) {
815 else if (!wcsncat(buffer
, L
"\\" LXPCOM_DLL
, aBufLen
)
817 || (GetFileAttributesW(buffer
) == INVALID_FILE_ATTRIBUTES
)
819 || _waccess(buffer
, R_OK
)
829 WideCharToMultiByte(CP_UTF8
, 0, buffer
, -1, aBuffer
, aBufLen
, NULL
, NULL
);