Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* Any copyright is dedicated to the Public Domain. |
michael@0 | 2 | * http://creativecommons.org/publicdomain/zero/1.0/ |
michael@0 | 3 | */ |
michael@0 | 4 | |
michael@0 | 5 | /** |
michael@0 | 6 | * Server side http server script for application update tests. |
michael@0 | 7 | * |
michael@0 | 8 | * !IMPORTANT - Since xpcshell used by the http server is launched with -v 170 |
michael@0 | 9 | * this file must not use features greater than JavaScript 1.7. |
michael@0 | 10 | */ |
michael@0 | 11 | |
michael@0 | 12 | const AUS_Cc = Components.classes; |
michael@0 | 13 | const AUS_Ci = Components.interfaces; |
michael@0 | 14 | |
michael@0 | 15 | #include ../sharedUpdateXML.js |
michael@0 | 16 | |
michael@0 | 17 | const URL_HOST = "http://example.com"; |
michael@0 | 18 | const URL_PATH_UPDATE_XML = "/chrome/toolkit/mozapps/update/tests/chrome/update.sjs"; |
michael@0 | 19 | const URL_HTTP_UPDATE_SJS = URL_HOST + URL_PATH_UPDATE_XML; |
michael@0 | 20 | const REL_PATH_DATA = "chrome/toolkit/mozapps/update/tests/data/"; |
michael@0 | 21 | const SERVICE_URL = URL_HOST + "/" + REL_PATH_DATA + FILE_SIMPLE_MAR; |
michael@0 | 22 | |
michael@0 | 23 | const SLOW_MAR_DOWNLOAD_INTERVAL = 100; |
michael@0 | 24 | var gTimer; |
michael@0 | 25 | |
michael@0 | 26 | function handleRequest(aRequest, aResponse) { |
michael@0 | 27 | var params = { }; |
michael@0 | 28 | if (aRequest.queryString) |
michael@0 | 29 | params = parseQueryString(aRequest.queryString); |
michael@0 | 30 | |
michael@0 | 31 | var statusCode = params.statusCode ? parseInt(params.statusCode) : 200; |
michael@0 | 32 | var statusReason = params.statusReason ? params.statusReason : "OK"; |
michael@0 | 33 | aResponse.setStatusLine(aRequest.httpVersion, statusCode, statusReason); |
michael@0 | 34 | aResponse.setHeader("Cache-Control", "no-cache", false); |
michael@0 | 35 | |
michael@0 | 36 | if (params.addonID) { |
michael@0 | 37 | aResponse.write(getUpdateRDF(params)); |
michael@0 | 38 | return; |
michael@0 | 39 | } |
michael@0 | 40 | |
michael@0 | 41 | // When a mar download is started by the update service it can finish |
michael@0 | 42 | // downloading before the ui has loaded. By specifying a serviceURL for the |
michael@0 | 43 | // update patch that points to this file and has a slowDownloadMar param the |
michael@0 | 44 | // mar will be downloaded asynchronously which will allow the ui to load |
michael@0 | 45 | // before the download completes. |
michael@0 | 46 | if (params.slowDownloadMar) { |
michael@0 | 47 | aResponse.processAsync(); |
michael@0 | 48 | aResponse.setHeader("Content-Type", "binary/octet-stream"); |
michael@0 | 49 | aResponse.setHeader("Content-Length", SIZE_SIMPLE_MAR); |
michael@0 | 50 | var marFile = AUS_Cc["@mozilla.org/file/directory_service;1"]. |
michael@0 | 51 | getService(AUS_Ci.nsIProperties). |
michael@0 | 52 | get("CurWorkD", AUS_Ci.nsILocalFile); |
michael@0 | 53 | var path = REL_PATH_DATA + FILE_SIMPLE_MAR; |
michael@0 | 54 | var pathParts = path.split("/"); |
michael@0 | 55 | for(var i = 0; i < pathParts.length; ++i) |
michael@0 | 56 | marFile.append(pathParts[i]); |
michael@0 | 57 | var contents = readFileBytes(marFile); |
michael@0 | 58 | gTimer = AUS_Cc["@mozilla.org/timer;1"]. |
michael@0 | 59 | createInstance(AUS_Ci.nsITimer); |
michael@0 | 60 | gTimer.initWithCallback(function(aTimer) { |
michael@0 | 61 | aResponse.write(contents); |
michael@0 | 62 | aResponse.finish(); |
michael@0 | 63 | }, SLOW_MAR_DOWNLOAD_INTERVAL, AUS_Ci.nsITimer.TYPE_ONE_SHOT); |
michael@0 | 64 | return; |
michael@0 | 65 | } |
michael@0 | 66 | |
michael@0 | 67 | if (params.uiURL) { |
michael@0 | 68 | var remoteType = ""; |
michael@0 | 69 | if (!params.remoteNoTypeAttr && |
michael@0 | 70 | (params.uiURL == "BILLBOARD" || params.uiURL == "LICENSE")) { |
michael@0 | 71 | remoteType = " " + params.uiURL.toLowerCase() + "=\"1\""; |
michael@0 | 72 | } |
michael@0 | 73 | aResponse.write("<html><head><meta http-equiv=\"content-type\" content=" + |
michael@0 | 74 | "\"text/html; charset=utf-8\"></head><body" + |
michael@0 | 75 | remoteType + ">" + params.uiURL + |
michael@0 | 76 | "<br><br>this is a test mar that will not affect your " + |
michael@0 | 77 | "build.</body></html>"); |
michael@0 | 78 | return; |
michael@0 | 79 | } |
michael@0 | 80 | |
michael@0 | 81 | if (params.xmlMalformed) { |
michael@0 | 82 | aResponse.write("xml error"); |
michael@0 | 83 | return; |
michael@0 | 84 | } |
michael@0 | 85 | |
michael@0 | 86 | if (params.noUpdates) { |
michael@0 | 87 | aResponse.write(getRemoteUpdatesXMLString("")); |
michael@0 | 88 | return; |
michael@0 | 89 | } |
michael@0 | 90 | |
michael@0 | 91 | if (params.unsupported) { |
michael@0 | 92 | aResponse.write(getRemoteUpdatesXMLString(" <update type=\"major\" " + |
michael@0 | 93 | "unsupported=\"true\" " + |
michael@0 | 94 | "detailsURL=\"" + URL_HOST + |
michael@0 | 95 | "\"></update>\n")); |
michael@0 | 96 | return; |
michael@0 | 97 | } |
michael@0 | 98 | |
michael@0 | 99 | var hash; |
michael@0 | 100 | var patches = ""; |
michael@0 | 101 | if (!params.partialPatchOnly) { |
michael@0 | 102 | hash = SHA512_HASH_SIMPLE_MAR + (params.invalidCompleteHash ? "e" : ""); |
michael@0 | 103 | patches += getRemotePatchString("complete", SERVICE_URL, "SHA512", |
michael@0 | 104 | hash, SIZE_SIMPLE_MAR); |
michael@0 | 105 | } |
michael@0 | 106 | |
michael@0 | 107 | if (!params.completePatchOnly) { |
michael@0 | 108 | hash = SHA512_HASH_SIMPLE_MAR + (params.invalidPartialHash ? "e" : ""); |
michael@0 | 109 | patches += getRemotePatchString("partial", SERVICE_URL, "SHA512", |
michael@0 | 110 | hash, SIZE_SIMPLE_MAR); |
michael@0 | 111 | } |
michael@0 | 112 | |
michael@0 | 113 | var type = params.type ? params.type : "major"; |
michael@0 | 114 | var name = params.name ? params.name : "App Update Test"; |
michael@0 | 115 | var appVersion = params.appVersion ? params.appVersion : "99.9"; |
michael@0 | 116 | var displayVersion = params.displayVersion ? params.displayVersion |
michael@0 | 117 | : "version " + appVersion; |
michael@0 | 118 | var platformVersion = params.platformVersion ? params.platformVersion : "99.8"; |
michael@0 | 119 | var buildID = params.buildID ? params.buildID : "01234567890123"; |
michael@0 | 120 | // XXXrstrong - not specifying a detailsURL will cause a leak due to bug 470244 |
michael@0 | 121 | // var detailsURL = params.showDetails ? URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS" : null; |
michael@0 | 122 | var detailsURL = URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS"; |
michael@0 | 123 | var billboardURL = params.showBillboard ? URL_HTTP_UPDATE_SJS + "?uiURL=BILLBOARD" : null; |
michael@0 | 124 | if (billboardURL && params.remoteNoTypeAttr) |
michael@0 | 125 | billboardURL += "&remoteNoTypeAttr=1"; |
michael@0 | 126 | if (params.billboard404) |
michael@0 | 127 | billboardURL = URL_HOST + "/missing.html"; |
michael@0 | 128 | var licenseURL = params.showLicense ? URL_HTTP_UPDATE_SJS + "?uiURL=LICENSE" : null; |
michael@0 | 129 | if (licenseURL && params.remoteNoTypeAttr) |
michael@0 | 130 | licenseURL += "&remoteNoTypeAttr=1"; |
michael@0 | 131 | if (params.license404) |
michael@0 | 132 | licenseURL = URL_HOST + "/missing.html"; |
michael@0 | 133 | var showPrompt = params.showPrompt ? "true" : null; |
michael@0 | 134 | var showNever = params.showNever ? "true" : null; |
michael@0 | 135 | var promptWaitTime = params.promptWaitTime ? params.promptWaitTime : null; |
michael@0 | 136 | var showSurvey = params.showSurvey ? "true" : null; |
michael@0 | 137 | |
michael@0 | 138 | // For testing the deprecated update xml format |
michael@0 | 139 | if (params.oldFormat) { |
michael@0 | 140 | appVersion = null; |
michael@0 | 141 | displayVersion = null; |
michael@0 | 142 | billboardURL = null; |
michael@0 | 143 | showPrompt = null; |
michael@0 | 144 | showNever = null; |
michael@0 | 145 | showSurvey = null; |
michael@0 | 146 | detailsURL = URL_HTTP_UPDATE_SJS + "?uiURL=BILLBOARD"; |
michael@0 | 147 | if (params.remoteNoTypeAttr) |
michael@0 | 148 | detailsURL += "&remoteNoTypeAttr=1"; |
michael@0 | 149 | var extensionVersion = params.appVersion ? params.appVersion : "99.9"; |
michael@0 | 150 | var version = params.displayVersion ? params.displayVersion |
michael@0 | 151 | : "version " + extensionVersion; |
michael@0 | 152 | } |
michael@0 | 153 | |
michael@0 | 154 | var updates = getRemoteUpdateString(patches, type, "App Update Test", |
michael@0 | 155 | displayVersion, appVersion, |
michael@0 | 156 | platformVersion, buildID, detailsURL, |
michael@0 | 157 | billboardURL, licenseURL, showPrompt, |
michael@0 | 158 | showNever, promptWaitTime, showSurvey, |
michael@0 | 159 | version, extensionVersion); |
michael@0 | 160 | |
michael@0 | 161 | aResponse.write(getRemoteUpdatesXMLString(updates)); |
michael@0 | 162 | } |
michael@0 | 163 | |
michael@0 | 164 | /** |
michael@0 | 165 | * Helper function to create a JS object representing the url parameters from |
michael@0 | 166 | * the request's queryString. |
michael@0 | 167 | * |
michael@0 | 168 | * @param aQueryString |
michael@0 | 169 | * The request's query string. |
michael@0 | 170 | * @return A JS object representing the url parameters from the request's |
michael@0 | 171 | * queryString. |
michael@0 | 172 | */ |
michael@0 | 173 | function parseQueryString(aQueryString) { |
michael@0 | 174 | var paramArray = aQueryString.split("&"); |
michael@0 | 175 | var regex = /^([^=]+)=(.*)$/; |
michael@0 | 176 | var params = {}; |
michael@0 | 177 | for (var i = 0, sz = paramArray.length; i < sz; i++) { |
michael@0 | 178 | var match = regex.exec(paramArray[i]); |
michael@0 | 179 | if (!match) |
michael@0 | 180 | throw "Bad parameter in queryString! '" + paramArray[i] + "'"; |
michael@0 | 181 | params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]); |
michael@0 | 182 | } |
michael@0 | 183 | |
michael@0 | 184 | return params; |
michael@0 | 185 | } |
michael@0 | 186 | |
michael@0 | 187 | /** |
michael@0 | 188 | * Helper function to gets the string representation of the contents of the |
michael@0 | 189 | * add-on's update manifest file. |
michael@0 | 190 | * |
michael@0 | 191 | * @param aParams |
michael@0 | 192 | * A JS object representing the url parameters from the request's |
michael@0 | 193 | * queryString. |
michael@0 | 194 | * @return A string representation of the contents of the add-on's update |
michael@0 | 195 | * manifest file. |
michael@0 | 196 | */ |
michael@0 | 197 | function getUpdateRDF(aParams) { |
michael@0 | 198 | var addonVersion; |
michael@0 | 199 | var addonID = aParams.addonID; |
michael@0 | 200 | var addonUpdateType = addonID.split("_")[0]; |
michael@0 | 201 | var maxVersion = aParams.platformVersion; |
michael@0 | 202 | |
michael@0 | 203 | switch (addonUpdateType) { |
michael@0 | 204 | case "updatecompatibility": |
michael@0 | 205 | // Use "1.0" for the add-on version for the compatibility update case since |
michael@0 | 206 | // the tests create all add-ons with "1.0" for the version. |
michael@0 | 207 | addonVersion = "1.0"; |
michael@0 | 208 | break; |
michael@0 | 209 | case "updateversion": |
michael@0 | 210 | // Use "2.0" for the add-on version for the version update case since the |
michael@0 | 211 | // tests create all add-ons with "1.0" for the version. |
michael@0 | 212 | addonVersion = "2.0"; |
michael@0 | 213 | break; |
michael@0 | 214 | default: |
michael@0 | 215 | return "<?xml version=\"1.0\"?>\n" + |
michael@0 | 216 | "<RDF:RDF xmlns:RDF=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" " + |
michael@0 | 217 | " xmlns:em=\"http://www.mozilla.org/2004/em-rdf#\">\n" + |
michael@0 | 218 | "</RDF:RDF>\n"; |
michael@0 | 219 | } |
michael@0 | 220 | |
michael@0 | 221 | return "<?xml version=\"1.0\"?>\n" + |
michael@0 | 222 | "<RDF:RDF xmlns:RDF=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" " + |
michael@0 | 223 | " xmlns:em=\"http://www.mozilla.org/2004/em-rdf#\">\n" + |
michael@0 | 224 | " <RDF:Description about=\"urn:mozilla:extension:" + addonID + "\">\n" + |
michael@0 | 225 | " <em:updates>\n" + |
michael@0 | 226 | " <RDF:Seq>\n" + |
michael@0 | 227 | " <RDF:li resource=\"urn:mozilla:extension:" + addonID + ":" + addonVersion + "\"/>\n" + |
michael@0 | 228 | " </RDF:Seq>\n" + |
michael@0 | 229 | " </em:updates>\n" + |
michael@0 | 230 | " </RDF:Description>\n" + |
michael@0 | 231 | " <RDF:Description about=\"urn:mozilla:extension:" + addonID + ":" + addonVersion + "\">\n" + |
michael@0 | 232 | " <em:version>" + addonVersion + "</em:version>\n" + |
michael@0 | 233 | " <em:targetApplication>\n" + |
michael@0 | 234 | " <RDF:Description>\n" + |
michael@0 | 235 | " <em:id>toolkit@mozilla.org</em:id>\n" + |
michael@0 | 236 | " <em:minVersion>0</em:minVersion>\n" + |
michael@0 | 237 | " <em:maxVersion>" + maxVersion + "</em:maxVersion>\n" + |
michael@0 | 238 | " <em:updateLink>" + URL_HTTP_UPDATE_SJS + "</em:updateLink>\n" + |
michael@0 | 239 | " <em:updateHash>sha256:0</em:updateHash>\n" + |
michael@0 | 240 | " </RDF:Description>\n" + |
michael@0 | 241 | " </em:targetApplication>\n" + |
michael@0 | 242 | " </RDF:Description>\n" + |
michael@0 | 243 | "</RDF:RDF>\n"; |
michael@0 | 244 | } |
michael@0 | 245 | |
michael@0 | 246 | /** |
michael@0 | 247 | * Reads the binary contents of a file and returns it as a string. |
michael@0 | 248 | * |
michael@0 | 249 | * @param aFile |
michael@0 | 250 | * The file to read from. |
michael@0 | 251 | * @return The contents of the file as a string. |
michael@0 | 252 | */ |
michael@0 | 253 | function readFileBytes(aFile) { |
michael@0 | 254 | var fis = AUS_Cc["@mozilla.org/network/file-input-stream;1"]. |
michael@0 | 255 | createInstance(AUS_Ci.nsIFileInputStream); |
michael@0 | 256 | fis.init(aFile, -1, -1, false); |
michael@0 | 257 | var bis = AUS_Cc["@mozilla.org/binaryinputstream;1"]. |
michael@0 | 258 | createInstance(AUS_Ci.nsIBinaryInputStream); |
michael@0 | 259 | bis.setInputStream(fis); |
michael@0 | 260 | var data = []; |
michael@0 | 261 | var count = fis.available(); |
michael@0 | 262 | while (count > 0) { |
michael@0 | 263 | var bytes = bis.readByteArray(Math.min(65535, count)); |
michael@0 | 264 | data.push(String.fromCharCode.apply(null, bytes)); |
michael@0 | 265 | count -= bytes.length; |
michael@0 | 266 | if (bytes.length == 0) |
michael@0 | 267 | throw "Nothing read from input stream!"; |
michael@0 | 268 | } |
michael@0 | 269 | data.join(''); |
michael@0 | 270 | fis.close(); |
michael@0 | 271 | return data.toString(); |
michael@0 | 272 | } |