Back to index

lightning-sunbird  0.9+nobinonly
nsGREGlue.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corp.
00019  * Portions created by the Initial Developer are Copyright (C) 2003
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Sean Su <ssu@netscape.com>
00024  *   Benjamin Smedberg <benjamin@smedbergs.us>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsXPCOMGlue.h"
00041 
00042 #include "nsINIParser.h"
00043 #include "nsVersionComparator.h"
00044 #include "nsXPCOMPrivate.h"
00045 
00046 #include <stdio.h>
00047 #include <stdlib.h>
00048 #include <string.h>
00049 
00050 #ifdef XP_WIN32
00051 # include <windows.h>
00052 # include <mbstring.h>
00053 # include <io.h>
00054 # define snprintf _snprintf
00055 # define R_OK 04
00056 #elif defined(XP_OS2)
00057 # define INCL_DOS
00058 # include <os2.h>
00059 #elif defined(XP_MACOSX)
00060 # include <CFBundle.h>
00061 # include <unistd.h>
00062 # include <dirent.h>
00063 #elif defined(XP_UNIX)
00064 # include <unistd.h>
00065 # include <sys/param.h>
00066 # include <dirent.h>
00067 #elif defined(XP_BEOS)
00068 # include <FindDirectory.h>
00069 # include <Path.h>
00070 # include <unistd.h>
00071 # include <sys/param.h>
00072 # include <OS.h>
00073 # include <image.h>
00074 #endif
00075 
00076 #include <sys/stat.h>
00077 
00084 static PRBool safe_strncat(char *dest, const char *append, PRUint32 count)
00085 {
00086   char *end = dest + count - 1;
00087 
00088   // skip to the end of dest
00089   while (*dest)
00090     ++dest;
00091 
00092   while (*append && dest < end) {
00093     *dest = *append;
00094     ++dest, ++append;
00095   }
00096 
00097   *dest = '\0';
00098 
00099   return *append == '\0';
00100 }
00101 
00102 static PRBool
00103 CheckVersion(const char* toCheck,
00104              const GREVersionRange *versions,
00105              PRUint32 versionsLength);
00106 
00107 #if defined(XP_MACOSX)
00108 
00109 static PRBool
00110 GRE_FindGREFramework(const char* rootPath,
00111                      const GREVersionRange *versions,
00112                      PRUint32 versionsLength,
00113                      const GREProperty *properties,
00114                      PRUint32 propertiesLength,
00115                      char* buffer, PRUint32 buflen);
00116 
00117 #elif defined(XP_UNIX)
00118 
00119 static PRBool
00120 GRE_GetPathFromConfigDir(const char* dirname,
00121                          const GREVersionRange *versions,
00122                          PRUint32 versionsLength,
00123                          const GREProperty *properties,
00124                          PRUint32 propertiesLength,
00125                          char* buffer, PRUint32 buflen);
00126 
00127 static PRBool
00128 GRE_GetPathFromConfigFile(const char* filename,
00129                           const GREVersionRange *versions,
00130                           PRUint32 versionsLength,
00131                           const GREProperty *properties,
00132                           PRUint32 propertiesLength,
00133                           char* buffer, PRUint32 buflen);
00134 
00135 #elif defined(XP_WIN)
00136 
00137 static PRBool
00138 GRE_GetPathFromRegKey(HKEY aRegKey,
00139                       const GREVersionRange *versions,
00140                       PRUint32 versionsLength,
00141                       const GREProperty *properties,
00142                       PRUint32 propertiesLength,
00143                       char* buffer, PRUint32 buflen);
00144 
00145 #endif
00146 
00147 nsresult
00148 GRE_GetGREPathWithProperties(const GREVersionRange *versions,
00149                              PRUint32 versionsLength,
00150                              const GREProperty *properties,
00151                              PRUint32 propertiesLength,
00152                              char *aBuffer, PRUint32 aBufLen)
00153 {
00154   // if GRE_HOME is in the environment, use that GRE
00155   const char* env = getenv("GRE_HOME");
00156   if (env && *env) {
00157     char p[MAXPATHLEN];
00158     snprintf(p, sizeof(p), "%s" XPCOM_FILE_PATH_SEPARATOR XPCOM_DLL, env);
00159     p[sizeof(p) - 1] = '\0';
00160 
00161 #if XP_UNIX
00162     if (realpath(p, aBuffer))
00163       return NS_OK;
00164 #elif XP_WIN
00165     if (_fullpath(aBuffer, p, aBufLen))
00166       return NS_OK;
00167 #else
00168     // hope for the best
00169     // xxxbsmedberg: other platforms should have a "make absolute" function
00170 #endif
00171 
00172     if (strlen(p) >= aBufLen)
00173       return NS_ERROR_FILE_NAME_TOO_LONG;
00174 
00175     strcpy(aBuffer, p);
00176 
00177     return NS_OK;
00178   }
00179 
00180   // the Gecko bits that sit next to the application or in the LD_LIBRARY_PATH
00181   env = getenv("USE_LOCAL_GRE");
00182   if (env && *env) {
00183     *aBuffer = nsnull;
00184     return NS_OK;
00185   }
00186 
00187 #ifdef XP_MACOSX
00188   aBuffer[0] = '\0';
00189 
00190   // Check the bundle first, for <bundle>/Contents/Frameworks/XUL.framework/libxpcom.dylib
00191   CFBundleRef appBundle = CFBundleGetMainBundle();
00192   if (appBundle) {
00193     CFURLRef fwurl = CFBundleCopyPrivateFrameworksURL(appBundle);
00194     CFURLRef absfwurl = nsnull;
00195     if (fwurl) {
00196       absfwurl = CFURLCopyAbsoluteURL(fwurl);
00197       CFRelease(fwurl);
00198     }
00199 
00200     if (absfwurl) {
00201       CFURLRef xulurl =
00202         CFURLCreateCopyAppendingPathComponent(NULL, absfwurl,
00203                                               CFSTR(GRE_FRAMEWORK_NAME),
00204                                               PR_TRUE);
00205 
00206       if (xulurl) {
00207         CFURLRef xpcomurl =
00208           CFURLCreateCopyAppendingPathComponent(NULL, xulurl,
00209                                                 CFSTR("libxpcom.dylib"),
00210                                                 PR_FALSE);
00211 
00212         if (xpcomurl) {
00213           char tbuffer[MAXPATHLEN];
00214 
00215           if (CFURLGetFileSystemRepresentation(xpcomurl, PR_TRUE,
00216                                                (UInt8*) tbuffer,
00217                                                sizeof(tbuffer)) &&
00218               access(tbuffer, R_OK | X_OK) == 0) {
00219             if (!realpath(tbuffer, aBuffer)) {
00220               aBuffer[0] = '\0';
00221             }
00222           }
00223 
00224           CFRelease(xpcomurl);
00225         }
00226 
00227         CFRelease(xulurl);
00228       }
00229 
00230       CFRelease(absfwurl);
00231     }
00232   }
00233 
00234   if (aBuffer[0])
00235     return NS_OK;
00236 
00237   // Check ~/Library/Frameworks/XUL.framework/Versions/<version>/libxpcom.dylib
00238   const char *home = getenv("HOME");
00239   if (home && *home && GRE_FindGREFramework(home,
00240                                             versions, versionsLength,
00241                                             properties, propertiesLength,
00242                                             aBuffer, aBufLen)) {
00243     return NS_OK;
00244   }
00245 
00246   // Check /Library/Frameworks/XUL.framework/Versions/<version>/libxpcom.dylib
00247   if (GRE_FindGREFramework("",
00248                            versions, versionsLength,
00249                            properties, propertiesLength,
00250                            aBuffer, aBufLen)) {
00251     return NS_OK;
00252   }
00253 
00254 #elif defined(XP_UNIX) 
00255   env = getenv("MOZ_GRE_CONF");
00256   if (env && GRE_GetPathFromConfigFile(env,
00257                                        versions, versionsLength,
00258                                        properties, propertiesLength,
00259                                        aBuffer, aBufLen)) {
00260     return NS_OK;
00261   }
00262 
00263   env = getenv("HOME");
00264   if (env && *env) {
00265     char buffer[MAXPATHLEN];
00266 
00267     // Look in ~/.gre.config
00268 
00269     snprintf(buffer, sizeof(buffer),
00270              "%s" XPCOM_FILE_PATH_SEPARATOR GRE_CONF_NAME, env);
00271     
00272     if (GRE_GetPathFromConfigFile(buffer,
00273                                   versions, versionsLength,
00274                                   properties, propertiesLength,
00275                                   aBuffer, aBufLen)) {
00276       return NS_OK;
00277     }
00278 
00279     // Look in ~/.gre.d/*.conf
00280 
00281     snprintf(buffer, sizeof(buffer),
00282              "%s" XPCOM_FILE_PATH_SEPARATOR GRE_USER_CONF_DIR, env);
00283 
00284     if (GRE_GetPathFromConfigDir(buffer,
00285                                  versions, versionsLength,
00286                                  properties, propertiesLength,
00287                                  aBuffer, aBufLen)) {
00288       return NS_OK;
00289     }
00290   }
00291 
00292   // Look for a global /etc/gre.conf file
00293   if (GRE_GetPathFromConfigFile(GRE_CONF_PATH,
00294                                 versions, versionsLength,
00295                                 properties, propertiesLength,
00296                                 aBuffer, aBufLen)) {
00297     return NS_OK;
00298   }
00299 
00300   // Look for a group of config files in /etc/gre.d/
00301   if (GRE_GetPathFromConfigDir(GRE_CONF_DIR,
00302                                versions, versionsLength,
00303                                properties, propertiesLength,
00304                                aBuffer, aBufLen)) {
00305     return NS_OK;
00306   }
00307 
00308 #elif defined(XP_WIN)
00309   HKEY hRegKey = NULL;
00310     
00311   // A couple of key points here:
00312   // 1. Note the usage of the "Software\\Mozilla\\GRE" subkey - this allows
00313   //    us to have multiple versions of GREs on the same machine by having
00314   //    subkeys such as 1.0, 1.1, 2.0 etc. under it.
00315   // 2. In this sample below we're looking for the location of GRE version 1.2
00316   //    i.e. we're compatible with GRE 1.2 and we're trying to find it's install
00317   //    location.
00318   //
00319   // Please see http://www.mozilla.org/projects/embedding/GRE.html for
00320   // more info.
00321   //
00322   if (::RegOpenKeyEx(HKEY_CURRENT_USER, GRE_WIN_REG_LOC, 0,
00323                      KEY_READ, &hRegKey) == ERROR_SUCCESS) {
00324     PRBool ok = GRE_GetPathFromRegKey(hRegKey,
00325                                       versions, versionsLength,
00326                                       properties, propertiesLength,
00327                                       aBuffer, aBufLen);
00328     ::RegCloseKey(hRegKey);
00329 
00330     if (ok)
00331       return NS_OK;
00332   }
00333 
00334   if (::RegOpenKeyEx(HKEY_LOCAL_MACHINE, GRE_WIN_REG_LOC, 0,
00335                      KEY_ENUMERATE_SUB_KEYS, &hRegKey) == ERROR_SUCCESS) {
00336     PRBool ok = GRE_GetPathFromRegKey(hRegKey,
00337                                       versions, versionsLength,
00338                                       properties, propertiesLength,
00339                                       aBuffer, aBufLen);
00340     ::RegCloseKey(hRegKey);
00341 
00342     if (ok)
00343       return NS_OK;
00344   }
00345 #endif
00346 
00347   return NS_ERROR_FAILURE;
00348 }
00349 
00350 static PRBool
00351 CheckVersion(const char* toCheck,
00352              const GREVersionRange *versions,
00353              PRUint32 versionsLength)
00354 {
00355   
00356   for (const GREVersionRange *versionsEnd = versions + versionsLength;
00357        versions < versionsEnd;
00358        ++versions) {
00359     PRInt32 c = NS_CompareVersions(toCheck, versions->lower);
00360     if (c < 0)
00361       continue;
00362 
00363     if (!c && !versions->lowerInclusive)
00364       continue;
00365 
00366     c = NS_CompareVersions(toCheck, versions->upper);
00367     if (c > 0)
00368       continue;
00369 
00370     if (!c && !versions->upperInclusive)
00371       continue;
00372 
00373     return PR_TRUE;
00374   }
00375 
00376   return PR_FALSE;
00377 }
00378 
00379 #ifdef XP_MACOSX
00380 PRBool
00381 GRE_FindGREFramework(const char* rootPath,
00382                      const GREVersionRange *versions,
00383                      PRUint32 versionsLength,
00384                      const GREProperty *properties,
00385                      PRUint32 propertiesLength,
00386                      char* buffer, PRUint32 buflen)
00387 {
00388   PRBool found = PR_FALSE;
00389 
00390   snprintf(buffer, buflen,
00391            "%s/Library/Frameworks/" GRE_FRAMEWORK_NAME "/Versions", rootPath);
00392   DIR *dir = opendir(buffer);
00393   if (dir) {
00394     struct dirent *entry;
00395     while (!found && (entry = readdir(dir))) {
00396       if (CheckVersion(entry->d_name, versions, versionsLength)) {
00397         snprintf(buffer, buflen,
00398                  "%s/Library/Frameworks/" GRE_FRAMEWORK_NAME
00399                  "/Versions/%s/" XPCOM_DLL, rootPath, entry->d_name);
00400         if (access(buffer, R_OK | X_OK) == 0)
00401           found = PR_TRUE;
00402       }
00403     }
00404 
00405     closedir(dir);
00406   }
00407   
00408   if (found)
00409     return PR_TRUE;
00410 
00411   buffer[0] = '\0';
00412   return PR_FALSE;
00413 }
00414     
00415 #elif defined(XP_UNIX)
00416 
00417 static PRBool IsConfFile(const char *filename)
00418 {
00419   const char *dot = strrchr(filename, '.');
00420 
00421   return (dot && strcmp(dot, ".conf") == 0);
00422 }
00423 
00424 PRBool
00425 GRE_GetPathFromConfigDir(const char* dirname,
00426                          const GREVersionRange *versions,
00427                          PRUint32 versionsLength,
00428                          const GREProperty *properties,
00429                          PRUint32 propertiesLength,
00430                          char* buffer, PRUint32 buflen)
00431 {
00432   // Open the directory provided and try to read any files in that
00433   // directory that end with .conf.  We look for an entry that might
00434   // point to the GRE that we're interested in.
00435   DIR *dir = opendir(dirname);
00436   if (!dir)
00437     return nsnull;
00438 
00439   PRBool found = PR_FALSE;
00440   struct dirent *entry;
00441 
00442   while (!found && (entry = readdir(dir))) {
00443 
00444     // Only look for files that end in .conf
00445     // IsConfFile will skip "." and ".."
00446     if (!IsConfFile(entry->d_name))
00447       continue;
00448 
00449     char fullPath[MAXPATHLEN];
00450     snprintf(fullPath, sizeof(fullPath), "%s" XPCOM_FILE_PATH_SEPARATOR "%s",
00451              dirname, entry->d_name);
00452 
00453     found = GRE_GetPathFromConfigFile(fullPath,
00454                                       versions, versionsLength,
00455                                       properties, propertiesLength,
00456                                       buffer, buflen);
00457   }
00458 
00459   closedir(dir);
00460 
00461   return found;
00462 }
00463 
00464 #define READ_BUFFER_SIZE 1024
00465 
00466 struct INIClosure
00467 {
00468   nsINIParser           *parser;
00469   const GREVersionRange *versions;
00470   PRUint32               versionsLength;
00471   const GREProperty     *properties;
00472   PRUint32               propertiesLength;
00473   char                  *pathBuffer;
00474   PRUint32               buflen;
00475   PRBool                 found;
00476 };
00477 
00478 static PRBool
00479 CheckINIHeader(const char *aHeader, void *aClosure)
00480 {
00481   nsresult rv;
00482 
00483   INIClosure *c = NS_REINTERPRET_CAST(INIClosure *, aClosure);
00484 
00485   if (!CheckVersion(aHeader, c->versions, c->versionsLength))
00486     return PR_TRUE;
00487 
00488   const GREProperty *properties = c->properties;
00489   const GREProperty *endProperties = properties + c->propertiesLength;
00490   for (; properties < endProperties; ++properties) {
00491     char buffer[MAXPATHLEN];
00492     rv = c->parser->GetString(aHeader, properties->property,
00493                              buffer, sizeof(buffer));
00494     if (NS_FAILED(rv))
00495       return PR_TRUE;
00496 
00497     if (strcmp(buffer, properties->value))
00498       return PR_TRUE;
00499   }
00500 
00501   rv = c->parser->GetString(aHeader, "GRE_PATH", c->pathBuffer, c->buflen);
00502   if (NS_FAILED(rv))
00503     return PR_TRUE;
00504 
00505   if (!safe_strncat(c->pathBuffer, "/" XPCOM_DLL, c->buflen) ||
00506       access(c->pathBuffer, R_OK))
00507     return PR_TRUE;
00508 
00509   // We found a good GRE! Stop looking.
00510   c->found = PR_TRUE;
00511   return PR_FALSE;
00512 }
00513 
00514 PRBool
00515 GRE_GetPathFromConfigFile(const char* filename,
00516                           const GREVersionRange *versions,
00517                           PRUint32 versionsLength,
00518                           const GREProperty *properties,
00519                           PRUint32 propertiesLength,
00520                           char* pathBuffer, PRUint32 buflen)
00521 {
00522   nsINIParser parser;
00523   nsresult rv = parser.Init(filename);
00524   if (NS_FAILED(rv))
00525     return PR_FALSE;
00526 
00527   INIClosure c = {
00528     &parser,
00529     versions, versionsLength,
00530     properties, propertiesLength,
00531     pathBuffer, buflen,
00532     PR_FALSE
00533   };
00534 
00535   parser.GetSections(CheckINIHeader, &c);
00536   return c.found;
00537 }
00538 
00539 #elif defined(XP_WIN)
00540 
00541 static PRBool
00542 CopyWithEnvExpansion(char* aDest, const char* aSource, PRUint32 aBufLen,
00543                      DWORD aType)
00544 {
00545   switch (aType) {
00546   case REG_SZ:
00547     if (strlen(aSource) >= aBufLen)
00548       return PR_FALSE;
00549 
00550     strcpy(aDest, aSource);
00551     return PR_TRUE;
00552 
00553   case REG_EXPAND_SZ:
00554     if (ExpandEnvironmentStrings(aSource, aDest, aBufLen) > aBufLen)
00555       return PR_FALSE;
00556 
00557     return PR_TRUE;
00558   };
00559 
00560   // Whoops! We expected REG_SZ or REG_EXPAND_SZ, what happened here?
00561 
00562   return PR_FALSE;
00563 }
00564 
00565 PRBool
00566 GRE_GetPathFromRegKey(HKEY aRegKey,
00567                       const GREVersionRange *versions,
00568                       PRUint32 versionsLength,
00569                       const GREProperty *properties,
00570                       PRUint32 propertiesLength,
00571                       char* aBuffer, PRUint32 aBufLen)
00572 {
00573   // Formerly, GREs were registered at the key HKLM/Software/Mozilla/GRE/<version>
00574   // valuepair GreHome=Path. Nowadays, they are registered in any subkey of
00575   // Software/Mozilla/GRE, with the following valuepairs:
00576   //   Version=<version> (REG_SZ)
00577   //   GreHome=<path>    (REG_SZ or REG_EXPAND_SZ)
00578   //   <Property>=<value> (REG_SZ)
00579   //
00580   // Additional meta-info may be available in the future, including
00581   // localization info, ABI, and other information which might be pertinent
00582   // to selecting one GRE over another.
00583   //
00584   // When a GRE is being registered, it should try to register itself at
00585   // HKLM/Software/Mozilla/GRE/<Version> first, to preserve compatibility
00586   // with older glue. If this key is already taken (i.e. there is more than
00587   // one GRE of that version installed), it should append a unique number to
00588   // the version, for example:
00589   //   1.1 (already in use), 1.1_1, 1.1_2, etc...
00590 
00591   DWORD i = 0;
00592 
00593   while (PR_TRUE) {
00594     char name[MAXPATHLEN + 1];
00595     DWORD nameLen = MAXPATHLEN;
00596     if (::RegEnumKeyEx(aRegKey, i, name, &nameLen, NULL, NULL, NULL, NULL) !=
00597         ERROR_SUCCESS) {
00598       break;
00599     }
00600 
00601     HKEY subKey = NULL;
00602     if (::RegOpenKeyEx(aRegKey, name, 0, KEY_QUERY_VALUE, &subKey) !=
00603         ERROR_SUCCESS) {
00604       continue;
00605     }
00606 
00607     char version[40];
00608     DWORD versionlen = 40;
00609     char pathbuf[MAXPATHLEN];
00610     DWORD pathlen;
00611     DWORD pathtype;
00612 
00613     PRBool ok = PR_FALSE;
00614 
00615     if (::RegQueryValueEx(subKey, "Version", NULL, NULL,
00616                           (BYTE*) version, &versionlen) == ERROR_SUCCESS &&
00617         CheckVersion(version, versions, versionsLength)) {
00618 
00619       ok = PR_TRUE;
00620       const GREProperty *props = properties;
00621       const GREProperty *propsEnd = properties + propertiesLength;
00622       for (; ok && props < propsEnd; ++props) {
00623         pathlen = sizeof(pathbuf);
00624 
00625         if (::RegQueryValueEx(subKey, props->property, NULL, &pathtype,
00626                               (BYTE*) pathbuf, &pathlen) != ERROR_SUCCESS ||
00627             strcmp(pathbuf, props->value))
00628           ok = PR_FALSE;
00629       }
00630 
00631       pathlen = sizeof(pathbuf);
00632       if (ok &&
00633           (!::RegQueryValueEx(subKey, "GreHome", NULL, &pathtype,
00634                               (BYTE*) pathbuf, &pathlen) == ERROR_SUCCESS ||
00635            !*pathbuf ||
00636            !CopyWithEnvExpansion(aBuffer, pathbuf, aBufLen, pathtype))) {
00637         ok = PR_FALSE;
00638       }
00639       else if (!safe_strncat(aBuffer, "\\" XPCOM_DLL, aBufLen) ||
00640                access(aBuffer, R_OK)) {
00641         ok = PR_FALSE;
00642       }
00643     }
00644 
00645     RegCloseKey(subKey);
00646 
00647     if (ok)
00648       return PR_TRUE;
00649 
00650     ++i;
00651   }
00652 
00653   aBuffer[0] = '\0';
00654 
00655   return PR_FALSE;
00656 }
00657 #endif // XP_WIN