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 "nsINIParser.h"
43 #include "nsVersionComparator.h"
44 #include "nsXPCOMPrivate.h"
52 # include <mbstring.h>
54 # define snprintf _snprintf
59 #elif defined(XP_MACOSX)
60 # include <CFBundle.h>
63 #elif defined(XP_UNIX)
65 # include <sys/param.h>
67 #elif defined(XP_BEOS)
68 # include <FindDirectory.h>
71 # include <sys/param.h>
75 # include <FindDirectory.h>
81 * Like strncat, appends a buffer to another buffer. This is where the
82 * similarity ends. Firstly, the "count" here is the total size of the buffer
83 * (not the number of chars to append. Secondly, the function returns PR_FALSE
84 * if the buffer is not long enough to hold the concatenated string.
86 static PRBool
safe_strncat(char *dest
, const char *append
, PRUint32 count
)
88 char *end
= dest
+ count
- 1;
90 // skip to the end of dest
94 while (*append
&& dest
< end
) {
101 return *append
== '\0';
105 CheckVersion(const char* toCheck
,
106 const GREVersionRange
*versions
,
107 PRUint32 versionsLength
);
109 #if defined(XP_MACOSX)
112 GRE_FindGREFramework(const char* rootPath
,
113 const GREVersionRange
*versions
,
114 PRUint32 versionsLength
,
115 const GREProperty
*properties
,
116 PRUint32 propertiesLength
,
117 char* buffer
, PRUint32 buflen
);
119 #elif defined(XP_UNIX) || defined(XP_BEOS)
122 GRE_GetPathFromConfigDir(const char* dirname
,
123 const GREVersionRange
*versions
,
124 PRUint32 versionsLength
,
125 const GREProperty
*properties
,
126 PRUint32 propertiesLength
,
127 char* buffer
, PRUint32 buflen
);
130 GRE_GetPathFromConfigFile(const char* filename
,
131 const GREVersionRange
*versions
,
132 PRUint32 versionsLength
,
133 const GREProperty
*properties
,
134 PRUint32 propertiesLength
,
135 char* buffer
, PRUint32 buflen
);
137 #elif defined(XP_WIN)
140 GRE_GetPathFromRegKey(HKEY aRegKey
,
141 const GREVersionRange
*versions
,
142 PRUint32 versionsLength
,
143 const GREProperty
*properties
,
144 PRUint32 propertiesLength
,
145 char* buffer
, PRUint32 buflen
);
150 GRE_GetGREPathWithProperties(const GREVersionRange
*versions
,
151 PRUint32 versionsLength
,
152 const GREProperty
*properties
,
153 PRUint32 propertiesLength
,
154 char *aBuffer
, PRUint32 aBufLen
)
156 #ifdef TARGET_XPCOM_ABI
157 // append the ABI to the properties to match only binary
159 static const GREProperty kExtraProperty
=
160 { "abi", TARGET_XPCOM_ABI
};
162 GREProperty
*allProperties
= new GREProperty
[propertiesLength
+ 1];
164 return NS_ERROR_OUT_OF_MEMORY
;
166 for (PRUint32 i
=0; i
<propertiesLength
; i
++) {
167 allProperties
[i
].property
= properties
[i
].property
;
168 allProperties
[i
].value
= properties
[i
].value
;
170 allProperties
[propertiesLength
].property
= kExtraProperty
.property
;
171 allProperties
[propertiesLength
].value
= kExtraProperty
.value
;
172 PRUint32 allPropertiesLength
= propertiesLength
+ 1;
174 const GREProperty
*allProperties
= properties
;
175 PRUint32 allPropertiesLength
= propertiesLength
;
178 // if GRE_HOME is in the environment, use that GRE
179 const char* env
= getenv("GRE_HOME");
182 snprintf(p
, sizeof(p
), "%s" XPCOM_FILE_PATH_SEPARATOR XPCOM_DLL
, env
);
183 p
[sizeof(p
) - 1] = '\0';
186 if (realpath(p
, aBuffer
))
189 if (_fullpath(aBuffer
, p
, aBufLen
))
192 // realpath on OS/2 returns a unix-ized path, so re-native-ize
193 if (realpath(p
, aBuffer
)) {
194 for (char* ptr
= strchr(aBuffer
, '/'); ptr
; ptr
= strchr(ptr
, '/'))
201 result
= path
.SetTo(p
,0,true);
204 sprintf(aBuffer
, path
.Path());
209 // xxxbsmedberg: other platforms should have a "make absolute" function
212 if (strlen(p
) >= aBufLen
)
213 return NS_ERROR_FILE_NAME_TOO_LONG
;
220 // the Gecko bits that sit next to the application or in the LD_LIBRARY_PATH
221 env
= getenv("USE_LOCAL_GRE");
230 // Check the bundle first, for <bundle>/Contents/Frameworks/XUL.framework/libxpcom.dylib
231 CFBundleRef appBundle
= CFBundleGetMainBundle();
233 CFURLRef fwurl
= CFBundleCopyPrivateFrameworksURL(appBundle
);
234 CFURLRef absfwurl
= nsnull
;
236 absfwurl
= CFURLCopyAbsoluteURL(fwurl
);
242 CFURLCreateCopyAppendingPathComponent(NULL
, absfwurl
,
243 CFSTR(GRE_FRAMEWORK_NAME
),
248 CFURLCreateCopyAppendingPathComponent(NULL
, xulurl
,
249 CFSTR("libxpcom.dylib"),
253 char tbuffer
[MAXPATHLEN
];
255 if (CFURLGetFileSystemRepresentation(xpcomurl
, PR_TRUE
,
258 access(tbuffer
, R_OK
| X_OK
) == 0) {
259 if (!realpath(tbuffer
, aBuffer
)) {
277 // Check ~/Library/Frameworks/XUL.framework/Versions/<version>/libxpcom.dylib
278 const char *home
= getenv("HOME");
279 if (home
&& *home
&& GRE_FindGREFramework(home
,
280 versions
, versionsLength
,
281 allProperties
, allPropertiesLength
,
286 // Check /Library/Frameworks/XUL.framework/Versions/<version>/libxpcom.dylib
287 if (GRE_FindGREFramework("",
288 versions
, versionsLength
,
289 allProperties
, allPropertiesLength
,
294 #elif defined(XP_UNIX)
295 env
= getenv("MOZ_GRE_CONF");
296 if (env
&& GRE_GetPathFromConfigFile(env
,
297 versions
, versionsLength
,
298 allProperties
, allPropertiesLength
,
303 env
= getenv("HOME");
305 char buffer
[MAXPATHLEN
];
307 // Look in ~/.gre.config
309 snprintf(buffer
, sizeof(buffer
),
310 "%s" XPCOM_FILE_PATH_SEPARATOR GRE_CONF_NAME
, env
);
312 if (GRE_GetPathFromConfigFile(buffer
,
313 versions
, versionsLength
,
314 allProperties
, allPropertiesLength
,
319 // Look in ~/.gre.d/*.conf
321 snprintf(buffer
, sizeof(buffer
),
322 "%s" XPCOM_FILE_PATH_SEPARATOR GRE_USER_CONF_DIR
, env
);
324 if (GRE_GetPathFromConfigDir(buffer
,
325 versions
, versionsLength
,
326 allProperties
, allPropertiesLength
,
332 // Look for a global /etc/gre.conf file
333 if (GRE_GetPathFromConfigFile(GRE_CONF_PATH
,
334 versions
, versionsLength
,
335 allProperties
, allPropertiesLength
,
340 // Look for a group of config files in /etc/gre.d/
341 if (GRE_GetPathFromConfigDir(GRE_CONF_DIR
,
342 versions
, versionsLength
,
343 allProperties
, allPropertiesLength
,
348 #elif defined(XP_BEOS)
349 env
= getenv("MOZ_GRE_CONF");
350 if (env
&& GRE_GetPathFromConfigFile(env
,
351 versions
, versionsLength
,
352 allProperties
, allPropertiesLength
,
358 if (find_directory(B_USER_SETTINGS_DIRECTORY
, 0, 0, p
, MAXPATHLEN
)) {
359 char buffer
[MAXPATHLEN
];
361 // Look in B_USER_SETTINGS_DIRECTORY/gre.config
362 snprintf(buffer
, sizeof(buffer
),
363 "%s" XPCOM_FILE_PATH_SEPARATOR GRE_CONF_NAME
, p
);
365 if (GRE_GetPathFromConfigFile(buffer
,
366 versions
, versionsLength
,
367 allProperties
, allPropertiesLength
,
372 // Look in B_USER_SETTINGS_DIRECTORY/gre.d/*.conf
373 snprintf(buffer
, sizeof(buffer
),
374 "%s" XPCOM_FILE_PATH_SEPARATOR GRE_CONF_DIR
, p
);
376 if (GRE_GetPathFromConfigDir(buffer
,
377 versions
, versionsLength
,
378 allProperties
, allPropertiesLength
,
384 // Hope Zeta OS and Haiku OS multiuser versions will respect BeBook,
385 // for BeOS R5 COMMON and USER are equal
386 if (find_directory(B_COMMON_SETTINGS_DIRECTORY
, 0, 0, p
, MAXPATHLEN
)) {
387 char buffer
[MAXPATHLEN
];
389 // Look for a B_COMMON_SETTINGS_DIRECTORY/gre.conf file
390 snprintf(buffer
, sizeof(buffer
),
391 "%s" XPCOM_FILE_PATH_SEPARATOR GRE_CONF_PATH
, p
);
392 if (GRE_GetPathFromConfigFile(buffer
,
393 versions
, versionsLength
,
394 allProperties
, allPropertiesLength
,
399 // Look for a group of config files in B_COMMON_SETTINGS_DIRECTORY/gre.d/
400 snprintf(buffer
, sizeof(buffer
),
401 "%s" XPCOM_FILE_PATH_SEPARATOR GRE_CONF_DIR
, p
);
402 if (GRE_GetPathFromConfigDir(buffer
,
403 versions
, versionsLength
,
404 allProperties
, allPropertiesLength
,
410 #elif defined(XP_WIN)
413 // A couple of key points here:
414 // 1. Note the usage of the "Software\\mozilla.org\\GRE" subkey - this allows
415 // us to have multiple versions of GREs on the same machine by having
416 // subkeys such as 1.0, 1.1, 2.0 etc. under it.
417 // 2. In this sample below we're looking for the location of GRE version 1.2
418 // i.e. we're compatible with GRE 1.2 and we're trying to find it's install
421 // Please see http://www.mozilla.org/projects/embedding/GRE.html for
424 if (::RegOpenKeyEx(HKEY_CURRENT_USER
, GRE_WIN_REG_LOC
, 0,
425 KEY_READ
, &hRegKey
) == ERROR_SUCCESS
) {
426 PRBool ok
= GRE_GetPathFromRegKey(hRegKey
,
427 versions
, versionsLength
,
428 allProperties
, allPropertiesLength
,
430 ::RegCloseKey(hRegKey
);
436 if (::RegOpenKeyEx(HKEY_LOCAL_MACHINE
, GRE_WIN_REG_LOC
, 0,
437 KEY_ENUMERATE_SUB_KEYS
, &hRegKey
) == ERROR_SUCCESS
) {
438 PRBool ok
= GRE_GetPathFromRegKey(hRegKey
,
439 versions
, versionsLength
,
440 allProperties
, allPropertiesLength
,
442 ::RegCloseKey(hRegKey
);
449 return NS_ERROR_FAILURE
;
453 CheckVersion(const char* toCheck
,
454 const GREVersionRange
*versions
,
455 PRUint32 versionsLength
)
458 for (const GREVersionRange
*versionsEnd
= versions
+ versionsLength
;
459 versions
< versionsEnd
;
461 PRInt32 c
= NS_CompareVersions(toCheck
, versions
->lower
);
465 if (!c
&& !versions
->lowerInclusive
)
468 c
= NS_CompareVersions(toCheck
, versions
->upper
);
472 if (!c
&& !versions
->upperInclusive
)
483 GRE_FindGREFramework(const char* rootPath
,
484 const GREVersionRange
*versions
,
485 PRUint32 versionsLength
,
486 const GREProperty
*properties
,
487 PRUint32 propertiesLength
,
488 char* buffer
, PRUint32 buflen
)
490 PRBool found
= PR_FALSE
;
492 snprintf(buffer
, buflen
,
493 "%s/Library/Frameworks/" GRE_FRAMEWORK_NAME
"/Versions", rootPath
);
494 DIR *dir
= opendir(buffer
);
496 struct dirent
*entry
;
497 while (!found
&& (entry
= readdir(dir
))) {
498 if (CheckVersion(entry
->d_name
, versions
, versionsLength
)) {
499 snprintf(buffer
, buflen
,
500 "%s/Library/Frameworks/" GRE_FRAMEWORK_NAME
501 "/Versions/%s/" XPCOM_DLL
, rootPath
, entry
->d_name
);
502 if (access(buffer
, R_OK
| X_OK
) == 0)
517 #elif defined(XP_UNIX) || defined(XP_BEOS)
519 static PRBool
IsConfFile(const char *filename
)
521 const char *dot
= strrchr(filename
, '.');
523 return (dot
&& strcmp(dot
, ".conf") == 0);
527 GRE_GetPathFromConfigDir(const char* dirname
,
528 const GREVersionRange
*versions
,
529 PRUint32 versionsLength
,
530 const GREProperty
*properties
,
531 PRUint32 propertiesLength
,
532 char* buffer
, PRUint32 buflen
)
534 // Open the directory provided and try to read any files in that
535 // directory that end with .conf. We look for an entry that might
536 // point to the GRE that we're interested in.
537 DIR *dir
= opendir(dirname
);
541 PRBool found
= PR_FALSE
;
542 struct dirent
*entry
;
544 while (!found
&& (entry
= readdir(dir
))) {
546 // Only look for files that end in .conf
547 // IsConfFile will skip "." and ".."
548 if (!IsConfFile(entry
->d_name
))
551 char fullPath
[MAXPATHLEN
];
552 snprintf(fullPath
, sizeof(fullPath
), "%s" XPCOM_FILE_PATH_SEPARATOR
"%s",
553 dirname
, entry
->d_name
);
555 found
= GRE_GetPathFromConfigFile(fullPath
,
556 versions
, versionsLength
,
557 properties
, propertiesLength
,
566 #define READ_BUFFER_SIZE 1024
571 const GREVersionRange
*versions
;
572 PRUint32 versionsLength
;
573 const GREProperty
*properties
;
574 PRUint32 propertiesLength
;
581 CheckINIHeader(const char *aHeader
, void *aClosure
)
585 INIClosure
*c
= reinterpret_cast<INIClosure
*>(aClosure
);
587 if (!CheckVersion(aHeader
, c
->versions
, c
->versionsLength
))
590 const GREProperty
*properties
= c
->properties
;
591 const GREProperty
*endProperties
= properties
+ c
->propertiesLength
;
592 for (; properties
< endProperties
; ++properties
) {
593 char buffer
[MAXPATHLEN
];
594 rv
= c
->parser
->GetString(aHeader
, properties
->property
,
595 buffer
, sizeof(buffer
));
599 if (strcmp(buffer
, properties
->value
))
603 rv
= c
->parser
->GetString(aHeader
, "GRE_PATH", c
->pathBuffer
, c
->buflen
);
607 if (!safe_strncat(c
->pathBuffer
, "/" XPCOM_DLL
, c
->buflen
) ||
608 access(c
->pathBuffer
, R_OK
))
611 // We found a good GRE! Stop looking.
617 GRE_GetPathFromConfigFile(const char* filename
,
618 const GREVersionRange
*versions
,
619 PRUint32 versionsLength
,
620 const GREProperty
*properties
,
621 PRUint32 propertiesLength
,
622 char* pathBuffer
, PRUint32 buflen
)
625 nsresult rv
= parser
.Init(filename
);
631 versions
, versionsLength
,
632 properties
, propertiesLength
,
637 parser
.GetSections(CheckINIHeader
, &c
);
641 #elif defined(XP_WIN)
644 CopyWithEnvExpansion(char* aDest
, const char* aSource
, PRUint32 aBufLen
,
649 if (strlen(aSource
) >= aBufLen
)
652 strcpy(aDest
, aSource
);
656 if (ExpandEnvironmentStrings(aSource
, aDest
, aBufLen
) > aBufLen
)
662 // Whoops! We expected REG_SZ or REG_EXPAND_SZ, what happened here?
668 GRE_GetPathFromRegKey(HKEY aRegKey
,
669 const GREVersionRange
*versions
,
670 PRUint32 versionsLength
,
671 const GREProperty
*properties
,
672 PRUint32 propertiesLength
,
673 char* aBuffer
, PRUint32 aBufLen
)
675 // Formerly, GREs were registered at the registry key
676 // HKLM/Software/mozilla.org/GRE/<version> valuepair GreHome=Path.
677 // Nowadays, they are registered in any subkey of
678 // Software/mozilla.org/GRE, with the following valuepairs:
679 // Version=<version> (REG_SZ)
680 // GreHome=<path> (REG_SZ or REG_EXPAND_SZ)
681 // <Property>=<value> (REG_SZ)
683 // Additional meta-info may be available in the future, including
684 // localization info and other information which might be pertinent
685 // to selecting one GRE over another.
687 // When a GRE is being registered, it should try to register itself at
688 // HKLM/Software/mozilla.org/GRE/<Version> first, to preserve compatibility
689 // with older glue. If this key is already taken (i.e. there is more than
690 // one GRE of that version installed), it should append a unique number to
691 // the version, for example:
692 // 1.1 (already in use), 1.1_1, 1.1_2, etc...
697 char name
[MAXPATHLEN
+ 1];
698 DWORD nameLen
= MAXPATHLEN
;
699 if (::RegEnumKeyEx(aRegKey
, i
, name
, &nameLen
, NULL
, NULL
, NULL
, NULL
) !=
705 if (::RegOpenKeyEx(aRegKey
, name
, 0, KEY_QUERY_VALUE
, &subKey
) !=
711 DWORD versionlen
= 40;
712 char pathbuf
[MAXPATHLEN
];
716 PRBool ok
= PR_FALSE
;
718 if (::RegQueryValueEx(subKey
, "Version", NULL
, NULL
,
719 (BYTE
*) version
, &versionlen
) == ERROR_SUCCESS
&&
720 CheckVersion(version
, versions
, versionsLength
)) {
723 const GREProperty
*props
= properties
;
724 const GREProperty
*propsEnd
= properties
+ propertiesLength
;
725 for (; ok
&& props
< propsEnd
; ++props
) {
726 pathlen
= sizeof(pathbuf
);
728 if (::RegQueryValueEx(subKey
, props
->property
, NULL
, &pathtype
,
729 (BYTE
*) pathbuf
, &pathlen
) != ERROR_SUCCESS
||
730 strcmp(pathbuf
, props
->value
))
734 pathlen
= sizeof(pathbuf
);
736 (!::RegQueryValueEx(subKey
, "GreHome", NULL
, &pathtype
,
737 (BYTE
*) pathbuf
, &pathlen
) == ERROR_SUCCESS
||
739 !CopyWithEnvExpansion(aBuffer
, pathbuf
, aBufLen
, pathtype
))) {
742 else if (!safe_strncat(aBuffer
, "\\" XPCOM_DLL
, aBufLen
) ||
743 access(aBuffer
, R_OK
)) {