michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: * http://creativecommons.org/publicdomain/zero/1.0/ michael@0: */ michael@0: michael@0: /** michael@0: * Server side http server script for application update tests. michael@0: * michael@0: * !IMPORTANT - Since xpcshell used by the http server is launched with -v 170 michael@0: * this file must not use features greater than JavaScript 1.7. michael@0: */ michael@0: michael@0: const AUS_Cc = Components.classes; michael@0: const AUS_Ci = Components.interfaces; michael@0: michael@0: #include ../sharedUpdateXML.js michael@0: michael@0: const URL_HOST = "http://example.com"; michael@0: const URL_PATH_UPDATE_XML = "/chrome/toolkit/mozapps/update/tests/chrome/update.sjs"; michael@0: const URL_HTTP_UPDATE_SJS = URL_HOST + URL_PATH_UPDATE_XML; michael@0: const REL_PATH_DATA = "chrome/toolkit/mozapps/update/tests/data/"; michael@0: const SERVICE_URL = URL_HOST + "/" + REL_PATH_DATA + FILE_SIMPLE_MAR; michael@0: michael@0: const SLOW_MAR_DOWNLOAD_INTERVAL = 100; michael@0: var gTimer; michael@0: michael@0: function handleRequest(aRequest, aResponse) { michael@0: var params = { }; michael@0: if (aRequest.queryString) michael@0: params = parseQueryString(aRequest.queryString); michael@0: michael@0: var statusCode = params.statusCode ? parseInt(params.statusCode) : 200; michael@0: var statusReason = params.statusReason ? params.statusReason : "OK"; michael@0: aResponse.setStatusLine(aRequest.httpVersion, statusCode, statusReason); michael@0: aResponse.setHeader("Cache-Control", "no-cache", false); michael@0: michael@0: if (params.addonID) { michael@0: aResponse.write(getUpdateRDF(params)); michael@0: return; michael@0: } michael@0: michael@0: // When a mar download is started by the update service it can finish michael@0: // downloading before the ui has loaded. By specifying a serviceURL for the michael@0: // update patch that points to this file and has a slowDownloadMar param the michael@0: // mar will be downloaded asynchronously which will allow the ui to load michael@0: // before the download completes. michael@0: if (params.slowDownloadMar) { michael@0: aResponse.processAsync(); michael@0: aResponse.setHeader("Content-Type", "binary/octet-stream"); michael@0: aResponse.setHeader("Content-Length", SIZE_SIMPLE_MAR); michael@0: var marFile = AUS_Cc["@mozilla.org/file/directory_service;1"]. michael@0: getService(AUS_Ci.nsIProperties). michael@0: get("CurWorkD", AUS_Ci.nsILocalFile); michael@0: var path = REL_PATH_DATA + FILE_SIMPLE_MAR; michael@0: var pathParts = path.split("/"); michael@0: for(var i = 0; i < pathParts.length; ++i) michael@0: marFile.append(pathParts[i]); michael@0: var contents = readFileBytes(marFile); michael@0: gTimer = AUS_Cc["@mozilla.org/timer;1"]. michael@0: createInstance(AUS_Ci.nsITimer); michael@0: gTimer.initWithCallback(function(aTimer) { michael@0: aResponse.write(contents); michael@0: aResponse.finish(); michael@0: }, SLOW_MAR_DOWNLOAD_INTERVAL, AUS_Ci.nsITimer.TYPE_ONE_SHOT); michael@0: return; michael@0: } michael@0: michael@0: if (params.uiURL) { michael@0: var remoteType = ""; michael@0: if (!params.remoteNoTypeAttr && michael@0: (params.uiURL == "BILLBOARD" || params.uiURL == "LICENSE")) { michael@0: remoteType = " " + params.uiURL.toLowerCase() + "=\"1\""; michael@0: } michael@0: aResponse.write("" + params.uiURL + michael@0: "

this is a test mar that will not affect your " + michael@0: "build."); michael@0: return; michael@0: } michael@0: michael@0: if (params.xmlMalformed) { michael@0: aResponse.write("xml error"); michael@0: return; michael@0: } michael@0: michael@0: if (params.noUpdates) { michael@0: aResponse.write(getRemoteUpdatesXMLString("")); michael@0: return; michael@0: } michael@0: michael@0: if (params.unsupported) { michael@0: aResponse.write(getRemoteUpdatesXMLString(" \n")); michael@0: return; michael@0: } michael@0: michael@0: var hash; michael@0: var patches = ""; michael@0: if (!params.partialPatchOnly) { michael@0: hash = SHA512_HASH_SIMPLE_MAR + (params.invalidCompleteHash ? "e" : ""); michael@0: patches += getRemotePatchString("complete", SERVICE_URL, "SHA512", michael@0: hash, SIZE_SIMPLE_MAR); michael@0: } michael@0: michael@0: if (!params.completePatchOnly) { michael@0: hash = SHA512_HASH_SIMPLE_MAR + (params.invalidPartialHash ? "e" : ""); michael@0: patches += getRemotePatchString("partial", SERVICE_URL, "SHA512", michael@0: hash, SIZE_SIMPLE_MAR); michael@0: } michael@0: michael@0: var type = params.type ? params.type : "major"; michael@0: var name = params.name ? params.name : "App Update Test"; michael@0: var appVersion = params.appVersion ? params.appVersion : "99.9"; michael@0: var displayVersion = params.displayVersion ? params.displayVersion michael@0: : "version " + appVersion; michael@0: var platformVersion = params.platformVersion ? params.platformVersion : "99.8"; michael@0: var buildID = params.buildID ? params.buildID : "01234567890123"; michael@0: // XXXrstrong - not specifying a detailsURL will cause a leak due to bug 470244 michael@0: // var detailsURL = params.showDetails ? URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS" : null; michael@0: var detailsURL = URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS"; michael@0: var billboardURL = params.showBillboard ? URL_HTTP_UPDATE_SJS + "?uiURL=BILLBOARD" : null; michael@0: if (billboardURL && params.remoteNoTypeAttr) michael@0: billboardURL += "&remoteNoTypeAttr=1"; michael@0: if (params.billboard404) michael@0: billboardURL = URL_HOST + "/missing.html"; michael@0: var licenseURL = params.showLicense ? URL_HTTP_UPDATE_SJS + "?uiURL=LICENSE" : null; michael@0: if (licenseURL && params.remoteNoTypeAttr) michael@0: licenseURL += "&remoteNoTypeAttr=1"; michael@0: if (params.license404) michael@0: licenseURL = URL_HOST + "/missing.html"; michael@0: var showPrompt = params.showPrompt ? "true" : null; michael@0: var showNever = params.showNever ? "true" : null; michael@0: var promptWaitTime = params.promptWaitTime ? params.promptWaitTime : null; michael@0: var showSurvey = params.showSurvey ? "true" : null; michael@0: michael@0: // For testing the deprecated update xml format michael@0: if (params.oldFormat) { michael@0: appVersion = null; michael@0: displayVersion = null; michael@0: billboardURL = null; michael@0: showPrompt = null; michael@0: showNever = null; michael@0: showSurvey = null; michael@0: detailsURL = URL_HTTP_UPDATE_SJS + "?uiURL=BILLBOARD"; michael@0: if (params.remoteNoTypeAttr) michael@0: detailsURL += "&remoteNoTypeAttr=1"; michael@0: var extensionVersion = params.appVersion ? params.appVersion : "99.9"; michael@0: var version = params.displayVersion ? params.displayVersion michael@0: : "version " + extensionVersion; michael@0: } michael@0: michael@0: var updates = getRemoteUpdateString(patches, type, "App Update Test", michael@0: displayVersion, appVersion, michael@0: platformVersion, buildID, detailsURL, michael@0: billboardURL, licenseURL, showPrompt, michael@0: showNever, promptWaitTime, showSurvey, michael@0: version, extensionVersion); michael@0: michael@0: aResponse.write(getRemoteUpdatesXMLString(updates)); michael@0: } michael@0: michael@0: /** michael@0: * Helper function to create a JS object representing the url parameters from michael@0: * the request's queryString. michael@0: * michael@0: * @param aQueryString michael@0: * The request's query string. michael@0: * @return A JS object representing the url parameters from the request's michael@0: * queryString. michael@0: */ michael@0: function parseQueryString(aQueryString) { michael@0: var paramArray = aQueryString.split("&"); michael@0: var regex = /^([^=]+)=(.*)$/; michael@0: var params = {}; michael@0: for (var i = 0, sz = paramArray.length; i < sz; i++) { michael@0: var match = regex.exec(paramArray[i]); michael@0: if (!match) michael@0: throw "Bad parameter in queryString! '" + paramArray[i] + "'"; michael@0: params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]); michael@0: } michael@0: michael@0: return params; michael@0: } michael@0: michael@0: /** michael@0: * Helper function to gets the string representation of the contents of the michael@0: * add-on's update manifest file. michael@0: * michael@0: * @param aParams michael@0: * A JS object representing the url parameters from the request's michael@0: * queryString. michael@0: * @return A string representation of the contents of the add-on's update michael@0: * manifest file. michael@0: */ michael@0: function getUpdateRDF(aParams) { michael@0: var addonVersion; michael@0: var addonID = aParams.addonID; michael@0: var addonUpdateType = addonID.split("_")[0]; michael@0: var maxVersion = aParams.platformVersion; michael@0: michael@0: switch (addonUpdateType) { michael@0: case "updatecompatibility": michael@0: // Use "1.0" for the add-on version for the compatibility update case since michael@0: // the tests create all add-ons with "1.0" for the version. michael@0: addonVersion = "1.0"; michael@0: break; michael@0: case "updateversion": michael@0: // Use "2.0" for the add-on version for the version update case since the michael@0: // tests create all add-ons with "1.0" for the version. michael@0: addonVersion = "2.0"; michael@0: break; michael@0: default: michael@0: return "\n" + michael@0: "\n" + michael@0: "\n"; michael@0: } michael@0: michael@0: return "\n" + michael@0: "\n" + michael@0: " \n" + michael@0: " \n" + michael@0: " \n" + michael@0: " \n" + michael@0: " \n" + michael@0: " \n" + michael@0: " \n" + michael@0: " \n" + michael@0: " " + addonVersion + "\n" + michael@0: " \n" + michael@0: " \n" + michael@0: " toolkit@mozilla.org\n" + michael@0: " 0\n" + michael@0: " " + maxVersion + "\n" + michael@0: " " + URL_HTTP_UPDATE_SJS + "\n" + michael@0: " sha256:0\n" + michael@0: " \n" + michael@0: " \n" + michael@0: " \n" + michael@0: "\n"; michael@0: } michael@0: michael@0: /** michael@0: * Reads the binary contents of a file and returns it as a string. michael@0: * michael@0: * @param aFile michael@0: * The file to read from. michael@0: * @return The contents of the file as a string. michael@0: */ michael@0: function readFileBytes(aFile) { michael@0: var fis = AUS_Cc["@mozilla.org/network/file-input-stream;1"]. michael@0: createInstance(AUS_Ci.nsIFileInputStream); michael@0: fis.init(aFile, -1, -1, false); michael@0: var bis = AUS_Cc["@mozilla.org/binaryinputstream;1"]. michael@0: createInstance(AUS_Ci.nsIBinaryInputStream); michael@0: bis.setInputStream(fis); michael@0: var data = []; michael@0: var count = fis.available(); michael@0: while (count > 0) { michael@0: var bytes = bis.readByteArray(Math.min(65535, count)); michael@0: data.push(String.fromCharCode.apply(null, bytes)); michael@0: count -= bytes.length; michael@0: if (bytes.length == 0) michael@0: throw "Nothing read from input stream!"; michael@0: } michael@0: data.join(''); michael@0: fis.close(); michael@0: return data.toString(); michael@0: }