// Released under the MIT/X11 license // http://www.opensource.org/licenses/mit-license.php // "use strict"; var preferences = require("preferences-service"); var prompts = require("prompts"); var clipboard = require("clipboard"); var tabs = require("tabs"); var logger = require("logger"); var passUtils = require("passwords"); var Request = require("request").Request; var selfMod = require("self"); var urlMod = require("url"); var dataUtils = require("utils/data"); var xrpc = require("xmlrpc"); var panelMod = require("panel"); var JSONURLDefault = "https://fedorahosted.org/released"+ "/bugzilla-triage-scripts/Config_data.json"; var BTSPrefNS = "bugzilla-triage.setting."; var BTSPassRealm = "BTSXMLRPCPass"; var passwords = {}; // hash of passwords indexed by a hostname var config = exports.config = {}; function Message(cmd, data) { console.log("Message: cmd = " + cmd + ", data = " + data); this.cmd = cmd; this.data = data; } function log(msg) { postMessage(new Message("LogMessage", msg)); } /** * parse XML object out of string working around various bugs in Gecko implementation * see https://developer.mozilla.org/en/E4X for more information * * @param inStr String with unparsed XML string * @return XML object */ function parseXMLfromString (inStuff) { // if (typeof inStuff !== 'string') In future we should recognize this.response // and get just .text property out of it. TODO var respStr = inStuff.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, ""); // bug 336551 return new XML(respStr); } /** * In case URL contains alias, not the real bug number, get the real bug no * from the XML representation. Sets correct value to this.bugNo. * * This is a slow variant for bugs other than actual window */ function getRealBugNoSlow(bugNo, location, callback) { console.log("We have to deal with bug aliased as " + this.bugNo); // https://bugzilla.redhat.com/show_bug.cgi?ctype=xml&id=serialWacom Request({ url: location.href+"&ctype=xml", onComplete: function(response) { if (response.status === 200) { var xmlRepr = parseXMLfromString(response.text); // TODO this is probably wrong, both XPath and .text attribute var bugID = parseInt(xmlRepr.bug.bug_id.text, 10); if (isNaN(bugID)) { throw new Error("Cannot get bug no. even from XML representation!"); } console.log("The real bug no. is " + bugID); callback(bugID) } } }).get(); } function getPassword(login, domain) { var passPrompt = "Enter your Bugzilla password for fixing MIME attachment types"; var switchPrompt = "Do you want to switch off features requiring password completely"; var prefName = BTSPrefNS+"withoutPassowrd"; var pass = passUtils.getPassword(login, domain, BTSPassRealm); var retObject = { password: null, // password string or null if no password provided withoutPass: false // whether user doesn't want to use password at all }; // pass === null means no appropriatjslie password in the storage if (!preferences.get(prefName,false) && (pass === null)) { var passwordText = prompts.promptPassword(passPrompt); if (passwordText && passwordText.length > 0) { passUtils.setLogin(login, passwordText, domain, BTSPassRealm); retObject.password = passwordText; } else { var switchOff = prompts.promptYesNoCancel(switchPrompt); if (switchOff) { preferences.set(prefName,true); } retObject.withoutPass = switchOff; } } else { retObject.password = pass; } return retObject; } exports.changeJSONURL = function changeJSONURL() { var prfNm = BTSPrefNS+"JSONURL"; var url = preferences.get(prfNm,""); var reply = prompts.prompt("New location of JSON configuration file", url); if (reply) { preferences.set(prfNm, reply.trim()); // TODO Restartless add-on needs to resolve this. prompts.alert("For now, you should really restart Firefox!"); } }; /** * libbz.getInstalledPackages(msg.data, function (pkgsMsg) { worker.postMessage(pkgsMsg); locationLoginObj: { location: window.location.href, login: getLogin() } */ exports.getInstalledPackages = function getInstalledPackages(locationLoginObj, callback) { var installedPackages = {}; var enabledPackages = []; var location = locationLoginObj.location; if (typeof location == "string") { location = new urlMod.URL(location); } // Collect enabled packages per hostname (plus default ones) if (config.gJSONData && ("commentPackages" in config.gJSONData)) { if ("enabledPackages" in config.gJSONData.configData) { var epObject = config.gJSONData.configData.enabledPackages; if (location.host in epObject) { enabledPackages = enabledPackages.concat(epObject[location.host].split(/[,\s]+/)); } if ("any" in epObject) { enabledPackages = enabledPackages.concat(epObject.any.split(/[,\s]+/)); } } if ((enabledPackages.length === 1) && (enabledPackages[0] === "all")) { enabledPackages = []; for (var key in config.gJSONData.commentPackages) { enabledPackages.push(key); } } // TODO To be decided, whether we cannot just eliminate packages in // installedPackages and having it just as a plain list of all cmdObjects. enabledPackages.forEach(function (pkg, idx, arr) { if (pkg in config.gJSONData.commentPackages) { installedPackages[pkg] = config.gJSONData.commentPackages[pkg]; } }); } // Expand commentIdx properties into full comments var cmdObj = {}; for (var pkgKey in installedPackages) { for (var cmdObjKey in installedPackages[pkgKey]) { cmdObj = installedPackages[pkgKey][cmdObjKey]; if ("commentIdx" in cmdObj) { cmdObj.comment = config.gJSONData.commentStrings[cmdObj.commentIdx]; delete cmdObj.commentIdx; } } } if (config.gJSONData.commentStrings && "sentUpstreamString" in config.gJSONData.commentStrings) { config.constantData.commentStrings = {}; config.constantData.commentStrings.sentUpstreamString = config.gJSONData.commentStrings["sentUpstreamString"]; } var locURL = new urlMod.URL(locationLoginObj.location); var passDomain = locURL.scheme + "://" + locURL.host; var passwObj = getPassword(locationLoginObj.login, passDomain); // In order to avoid sending whole password to the content script, // we are sending just these two Booleans. config.constantData.passwordState = { passAvailable: (passwObj.password !== null), withoutPass: passwObj.withoutPass }; callback(new Message("CreateButtons", { instPkgs: installedPackages, constData: config.constantData, config: config.configData, kNodes: config.gJSONData.configData.killNodes })); }; exports.getClipboard = function getClipboard(cb) { cb(clipboard.get()); }; exports.setClipboard = function setClipboard(stuff) { clipboard.set(stuff, "text"); }; exports.getURL = function getURL(url, callback) { Request({ url: url, onComplete: function(response) { if (response.status == 200) { callback(response.text); } } }).get(); }; exports.openStringInNewPanel = function openStringInNewPanel(inHTMLStr) { openURLInNewPanel("data:text/html;charset=utf-8," + inHTMLStr); }; var openURLInNewPanel = exports.openURLInNewPanel = function openURLInNewPanel(url) { var panel = panelMod.Panel({ contentURL: url, width: 640, height: 640 }); panel.show(); }; var openURLInNewTab = exports.openURLInNewTab = function openURLInNewTab(url) { tabs.open({ url: url, inBackground: true, onReady: function(t) { t.activate(); } }); }; exports.createUpstreamBug = function createUpstreamBug(urlStr, subject, comment) { tabs.open({ url: urlStr, inBackground: true, onReady: function (t) { var otherElems = t.contentDocument.forms.namedItem("Create").elements; // Summary otherElems.namedItem("short_desc").value = subject; // Comment otherElems.namedItem("comment").value = collectComments(); t.activate(); } }); }; // Make a XML-RPC call ... most of the business logic should stay in the content script exports.makeXMLRPCCall = function makeXMLRPCCall(url, login, method, params, callback) { var urlObj = urlMod.URL(url); var passwObj = getPassword(login, urlObj.schema + "://" + urlObj.host); if (!passwObj.password) { return null; // TODO this should happen, only when user presses Escape in password prompt } var msg = new xrpc.XMLRPCMessage(method); params.forEach(function (par) { msg.addParameter(par); }); msg.addParameter(login); msg.addParameter(passwObj.password); Request({ url: url, onComplete: function(response) { if (response.status == 200) { var resp = parseXMLfromString(response.text); callback(resp.toXMLString()); } }, content: msg.xml(), contentType: "text/xml" }).post(); }; exports.initialize = function initialize(config, callback) { var prefName = BTSPrefNS+"JSONURL"; var urlStr = ""; if (preferences.isSet(prefName)) { urlStr = preferences.get(prefName); } else { urlStr = JSONURLDefault; preferences.set(prefName, JSONURLDefault); } // Randomize URL to avoid caching // TODO see https://fedorahosted.org/bugzilla-triage-scripts/ticket/21 // for more thorough discussion and possible further improvement urlStr += (urlStr.match(/\?/) == null ? "?" : "&") + (new Date()).getTime(); Request({ url: urlStr, onComplete: function (response) { if (response.status == 200) { config.gJSONData = response.json; // Get additional tables if ("downloadJSON" in config.gJSONData.configData) { var URLsList = config.gJSONData.configData.downloadJSON; var dwnldObj = ""; URLsList.forEach(function (arr) { var title = arr[0]; var url = arr[1]; Request({ url: url, onComplete: function(response) { if (response.status == 200) { config.constantData[title] = response.json; } } }).get(); }); } config.configData = {}; config.configData.matches = config.gJSONData.configData.matches; config.configData.skipMatches = config.configData.matches.map(function(x) { return x.replace("show_bug.cgi.*","((process|post)_bug|attachment)\.cgi$"); }); config.constantData = {}; if ("constantData" in config.gJSONData) { config.constantData = config.gJSONData.constantData; config.constantData.queryUpstreamBug = JSON.parse( selfMod.data.load("queryUpstreamBug.json")); config.constantData.XMLRPCData = JSON.parse( selfMod.data.load("XMLRPCdata.json")); config.constantData.bugzillaLabelNames = JSON.parse(selfMod.data.load("bugzillalabelNames.json")); config.constantData.newUpstreamBug = JSON.parse(selfMod.data.load("newUpstreamBug.json")); config.constantData.ProfessionalProducts = JSON.parse(selfMod.data.load("professionalProducts.json")); } if ("CCmaintainer" in config.constantData) { config.configData.defBugzillaMaintainerArr = config.constantData.CCmaintainer; } if ("suspiciousComponents" in config.gJSONData.configData) { config.configData.suspiciousComponents = config.gJSONData.configData.suspiciousComponents; } if ("XorgLogAnalysis" in config.gJSONData.configData) { config.configData.xorglogAnalysis = config.gJSONData.configData.XorgLogAnalysis; } if ("submitsLogging" in config.gJSONData.configData && config.gJSONData.configData.submitsLogging) { logger.initialize(JSON.parse(selfMod.data.load( "bugzillalabelAbbreviations.json"))); } } callback(); } }).get(); }