/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla. * * The Initial Developer of the Original Code is IBM Corporation. * Portions created by IBM Corporation are Copyright (C) 2003 * IBM Corporation. All Rights Reserved. * * Contributor(s): * Darin Fisher * Benjamin Smedberg * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include #include #ifdef XP_WIN #include #endif #include "nsXULAppAPI.h" #include "nsXPCOMGlue.h" #include "nsRegisterGRE.h" #include "nsAppRunner.h" #include "nsINIParser.h" #include "nsILocalFile.h" #include "nsIXULAppInstall.h" #include "nsCOMPtr.h" #include "nsMemory.h" #include "nsNativeCharsetUtils.h" #include "nsBuildID.h" #include "plstr.h" #include "prprf.h" #include "prenv.h" /** * Output a string to the user. This method is really only meant to be used to * output last-ditch error messages designed for developers NOT END USERS. * * @param isError * Pass true to indicate severe errors. * @param fmt * printf-style format string followed by arguments. */ static void Output(PRBool isError, const char *fmt, ... ) { va_list ap; va_start(ap, fmt); #if defined(XP_WIN) && !MOZ_WINCONSOLE char *msg = PR_vsmprintf(fmt, ap); if (msg) { UINT flags = MB_OK; if (isError) flags |= MB_ICONERROR; else flags |= MB_ICONINFORMATION; MessageBox(NULL, msg, "XULRunner", flags); PR_smprintf_free(msg); } #else vfprintf(stderr, fmt, ap); #endif va_end(ap); } /** * Return true if |arg| matches the given argument name. */ static PRBool IsArg(const char* arg, const char* s) { if (*arg == '-') { if (*++arg == '-') ++arg; return !PL_strcasecmp(arg, s); } #if defined(XP_WIN) || defined(XP_OS2) if (*arg == '/') return !PL_strcasecmp(++arg, s); #endif return PR_FALSE; } /** * Version checking. */ static PRUint32 geckoVersion = 0; static PRUint32 ParseVersion(const char *versionStr) { PRUint16 major, minor; if (PR_sscanf(versionStr, "%hu.%hu", &major, &minor) != 2) { NS_WARNING("invalid version string"); return 0; } return PRUint32(major) << 16 | PRUint32(minor); } static PRBool CheckMinVersion(const char *versionStr) { PRUint32 v = ParseVersion(versionStr); return geckoVersion >= v; } static PRBool CheckMaxVersion(const char *versionStr) { PRUint32 v = ParseVersion(versionStr); return geckoVersion <= v; } /** * Parse application data. */ static int LoadAppData(const char* appDataFile, nsXREAppData* aResult, nsCString& vendor, nsCString& name, nsCString& version, nsCString& buildID, nsCString& appID, nsCString& copyright) { nsresult rv; nsCOMPtr lf; XRE_GetFileFromPath(appDataFile, getter_AddRefs(lf)); if (!lf) return 2; nsCOMPtr appDir; rv = lf->GetParent(getter_AddRefs(appDir)); if (NS_FAILED(rv)) return 2; rv = CallQueryInterface(appDir, &aResult->directory); if (NS_FAILED(rv)) return 2; nsINIParser parser; rv = parser.Init(lf); if (NS_FAILED(rv)) return 2; // Gecko version checking // // TODO: If these version checks fail, then look for a compatible XULRunner // version on the system, and launch it instead. nsCAutoString gkVersion; rv = parser.GetString("Gecko", "MinVersion", gkVersion); if (NS_FAILED(rv) || !CheckMinVersion(gkVersion.get())) { Output(PR_TRUE, "Error: Gecko MinVersion requirement not met.\n"); return 1; } rv = parser.GetString("Gecko", "MaxVersion", gkVersion); if (NS_SUCCEEDED(rv) && !CheckMaxVersion(gkVersion.get())) { Output(PR_TRUE, "Error: Gecko MaxVersion requirement not met.\n"); return 1; } PRUint32 i; // Read string-valued fields const struct { const char *key; const char **fill; nsCString *buf; PRBool required; } string_fields[] = { { "Vendor", &aResult->vendor, &vendor, PR_FALSE }, { "Name", &aResult->name, &name, PR_TRUE }, { "Version", &aResult->version, &version, PR_FALSE }, { "BuildID", &aResult->buildID, &buildID, PR_TRUE }, { "ID", &aResult->ID, &appID, PR_FALSE }, { "Copyright", &aResult->copyright, ©right, PR_FALSE } }; for (i = 0; i < NS_ARRAY_LENGTH(string_fields); ++i) { rv = parser.GetString("App", string_fields[i].key, *string_fields[i].buf); if (NS_SUCCEEDED(rv)) { *string_fields[i].fill = string_fields[i].buf->get(); } else if (string_fields[i].required) { Output(PR_TRUE, "Error: %x: No \"%s\" field.\n", rv, string_fields[i].key); return 1; } } // Read boolean-valued fields const struct { const char* key; PRUint32 flag; } boolean_fields[] = { { "EnableProfileMigrator", NS_XRE_ENABLE_PROFILE_MIGRATOR }, { "EnableExtensionManager", NS_XRE_ENABLE_EXTENSION_MANAGER } }; char buf[6]; // large enough to hold "false" aResult->flags = 0; for (i = 0; i < NS_ARRAY_LENGTH(boolean_fields); ++i) { rv = parser.GetString("XRE", boolean_fields[i].key, buf, sizeof(buf)); // accept a truncated result since we are only interested in the // first character. this is designed to allow the possibility of // expanding these boolean attributes to express additional options. if ((NS_SUCCEEDED(rv) || rv == NS_ERROR_LOSS_OF_SIGNIFICANT_DATA) && (buf[0] == '1' || buf[0] == 't' || buf[0] == 'T')) { aResult->flags |= boolean_fields[i].flag; } } #ifdef DEBUG printf("---------------------------------------------------------\n"); printf(" Vendor %s\n", aResult->vendor); printf(" Name %s\n", aResult->name); printf(" Version %s\n", aResult->version); printf(" BuildID %s\n", aResult->buildID); printf(" Copyright %s\n", aResult->copyright); printf(" Flags %08x\n", aResult->flags); printf("---------------------------------------------------------\n"); #endif return 0; } static void Usage() { // display additional information (XXX make localizable?) Output(PR_FALSE, "Mozilla XULRunner %s\n\n" "Usage: " XULRUNNER_PROGNAME " [OPTIONS]\n" " " XULRUNNER_PROGNAME " APP-FILE [APP-OPTIONS...]\n" "\n" "OPTIONS\n" " --app specify APP-FILE (optional)\n" " -h, --help show this message\n" " -v, --version show version\n" " --gre-version print the GRE version string on stdout\n" " --register-global register this GRE in the machine registry\n" " --register-user register this GRE in the user registry\n" " --unregister-global unregister this GRE formerly registered with\n" " --register-global\n" " --unregister-user unregister this GRE formely registered with\n" " --register-user\n" " --find-gre Find a GRE with version and print\n" " the path on stdout\n" " --install-app [ []]\n" " Install a XUL application.\n" "\n" "APP-FILE\n" " Application initialization file.\n" "\n" "APP-OPTIONS\n" " Application specific options.\n", GRE_BUILD_ID); } static nsresult GetXULRunnerDir(const char *argv0, nsIFile* *aResult) { nsresult rv; nsCOMPtr appFile; rv = XRE_GetBinaryPath(argv0, getter_AddRefs(appFile)); if (NS_FAILED(rv)) { Output(PR_TRUE, "Could not find XULRunner application path.\n"); return rv; } rv = appFile->GetParent(aResult); if (NS_FAILED(rv)) { Output(PR_TRUE, "Could not find XULRunner installation dir.\n"); } return rv; } static int InstallXULApp(nsIFile* aXULRunnerDir, const char *aAppLocation, const char *aInstallTo, const char *aLeafName) { nsCOMPtr appLocation; nsCOMPtr installTo; nsAutoString leafName; nsresult rv = XRE_GetFileFromPath(aAppLocation, getter_AddRefs(appLocation)); if (NS_FAILED(rv)) return 2; if (aInstallTo) { rv = XRE_GetFileFromPath(aInstallTo, getter_AddRefs(installTo)); if (NS_FAILED(rv)) return 2; } if (aLeafName) NS_CopyNativeToUnicode(nsDependentCString(aLeafName), leafName); rv = NS_InitXPCOM2(nsnull, aXULRunnerDir, nsnull); if (NS_FAILED(rv)) return 3; { // Scope our COMPtr to avoid holding XPCOM refs beyond xpcom shutdown nsCOMPtr install (do_GetService("@mozilla.org/xulrunner/app-install-service;1")); if (!install) { rv = NS_ERROR_FAILURE; } else { rv = install->InstallApplication(appLocation, installTo, leafName); } } NS_ShutdownXPCOM(nsnull); if (NS_FAILED(rv)) return 3; return 0; } static const GREProperty kGREProperties[] = { { "xulrunner", "true" } #ifdef MOZ_JAVAXPCOM , { "javaxpcom", "1" } #endif }; int main(int argc, char* argv[]) { if (argc > 1 && (IsArg(argv[1], "h") || IsArg(argv[1], "help") || IsArg(argv[1], "?"))) { Usage(); return 0; } if (argc == 2 && (IsArg(argv[1], "v") || IsArg(argv[1], "version"))) { Output(PR_FALSE, "Mozilla XULRunner %s\n", GRE_BUILD_ID); return 0; } if (argc > 1) { PRBool registerGlobal = IsArg(argv[1], "register-global"); PRBool registerUser = IsArg(argv[1], "register-user"); if (registerGlobal || registerUser) { if (argc != 2) { Usage(); return 1; } nsCOMPtr regDir; nsresult rv = GetXULRunnerDir(argv[0], getter_AddRefs(regDir)); if (NS_FAILED(rv)) return 2; return RegisterXULRunner(registerGlobal, regDir, kGREProperties, NS_ARRAY_LENGTH(kGREProperties)) ? 0 : 2; } registerGlobal = IsArg(argv[1], "unregister-global"); registerUser = IsArg(argv[1], "unregister-user"); if (registerGlobal || registerUser) { if (argc != 2) { Usage(); return 1; } nsCOMPtr regDir; nsresult rv = GetXULRunnerDir(argv[0], getter_AddRefs(regDir)); if (NS_FAILED(rv)) return 2; UnregisterXULRunner(registerGlobal, regDir); return 0; } if (IsArg(argv[1], "find-gre")) { if (argc != 3) { Usage(); return 1; } char path[MAXPATHLEN]; static const GREVersionRange vr = { argv[2], PR_TRUE, argv[2], PR_TRUE }; static const GREProperty kProperties[] = { { "xulrunner", "true" } }; nsresult rv = GRE_GetGREPathWithProperties(&vr, 1, kProperties, NS_ARRAY_LENGTH(kProperties), path, sizeof(path)); if (NS_FAILED(rv)) return 1; printf("%s\n", path); return 0; } if (IsArg(argv[1], "gre-version")) { if (argc != 2) { Usage(); return 1; } printf("%s\n", GRE_BUILD_ID); return 0; } if (IsArg(argv[1], "install-app")) { if (argc < 3 || argc > 5) { Usage(); return 1; } char *appLocation = argv[2]; char *installTo = nsnull; if (argc > 3) { installTo = argv[3]; if (!*installTo) // left blank? installTo = nsnull; } char *leafName = nsnull; if (argc > 4) { leafName = argv[4]; if (!*leafName) leafName = nsnull; } nsCOMPtr regDir; nsresult rv = GetXULRunnerDir(argv[0], getter_AddRefs(regDir)); if (NS_FAILED(rv)) return 2; return InstallXULApp(regDir, appLocation, installTo, leafName); } } geckoVersion = ParseVersion(GRE_BUILD_ID); const char *appDataFile = PR_GetEnv("XUL_APP_FILE"); if (!(appDataFile && *appDataFile)) { if (argc < 2) { Usage(); return 1; } if (IsArg(argv[1], "app")) { if (argc == 2) { Usage(); return 1; } argv[1] = argv[0]; ++argv; --argc; } appDataFile = argv[1]; argv[1] = argv[0]; ++argv; --argc; static char kAppEnv[MAXPATHLEN]; PR_snprintf(kAppEnv, MAXPATHLEN, "XUL_APP_FILE=%s", appDataFile); PR_SetEnv(kAppEnv); } nsCAutoString vendor, name, version, buildID, appID, copyright; nsXREAppData appData = { sizeof(nsXREAppData), 0 }; int rv = LoadAppData(appDataFile, &appData, vendor, name, version, buildID, appID, copyright); if (!rv) rv = XRE_main(argc, argv, &appData); NS_IF_RELEASE(appData.directory); return rv; } #if defined( XP_WIN ) && defined( WIN32 ) && !defined(__GNUC__) // We need WinMain in order to not be a console app. This function is // unused if we are a console application. int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR args, int ) { // Do the real work. return main( __argc, __argv ); } #endif