aboutsummaryrefslogblamecommitdiffstats
path: root/lib/libbugzilla.js
blob: aecceadb3af01be9b955d568095475293e0c0a27 (plain) (tree)
1
2
3
4
5
6
7
8
9







                                                     
                               

                                         

                              
                                      

                                





                                                         
                                                              
                                 
 









                                                              

















                                                                                                      
                                                           
   
                                                      






                                                                        
                                                                           










                                                                                        
                                               


                                                                                          
                     

                                                                              
      
 


































                                                                            
             
         
       
 



                                                  
 
                                                                               
                                  


                                                                     
     





                                                            




                                       
   
                                                                                          

                               

                                             



                                            



                                                                       

                                                                                                  





                                                                                       



                                                              









                                                                                  
 
                                                      



                                                          
                                         
                                                                                    
                                         
             

         
 

                                                                      




                                                                  

                                                           














                                                                          

  



                                                  



                                                     












                                                                         
                   

  
                                                                                     
                                

                        
                   
       


                 
                                                                               







                              
 
                                                                                  



                               




                                                                                  




                         


                                                                                        





                                                                                            
             




















                                                                     

  
                                                            































                                                                                
                                                                               





                                 


                                                                                           


                                                                                               
                                         





                                                                        





                                                                                   
                 
 
                                                            
                                                                                                  


                                                                            

                                                                         


                                                                       

                                                                    



                                                                      

                                                                       
                 
            
                   


             
// 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 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, callback) {
    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 retObject = {
        password:  null, // password string or null if no password provided
        withoutPass: false // whether user doesn't want to use password at all
    };

    passUtils.search({
        username: login,
        url: domain,
        realm: BTSPassRealm,
        onComplete: function onComplete([credential]) {
            if (credential) {
                // We found the password, just go ahead and use it
                retObject.password = credential.password;
                callback(retObject);
            } else {
                // We don't have a stored password, ask for one
                var passwordText = prompts.promptPassword(passPrompt);
                if (passwordText && passwordText.length > 0) {
                    // Right, we've got it … store it and then use it.
                    retObject.password = passwordText;
                    passUtils.store({
                        username: login,
                        password: passwordText,
                        url: domain,
                        realm: BTSPassRealm,
                        onComplete: function onComplete() {
                            callback(retObject);
                        }
                    });
                } else {
                    // We don't have password, and user haven't entered one?
                    // Does he want to live passwordless?
                    // FIXME should we call the callback at all?
                    var switchOff = prompts.promptYesNoCancel(switchPrompt);
                    if (switchOff) {
                        preferences.set(prefName,true);
                    }
                    retObject.withoutPass = switchOff;
                    callback(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 && (reply != url)) {
        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]+/));
            }
        }

        var allIdx = null;
        if ((allIdx = enabledPackages.indexOf("all")) != -1) {
            enabledPackages = enabledPackages.splice(allIdx,
                config.gJSONData.commentPackages.keys());
        }

        // 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;
    getPassword(locationLoginObj.login, passDomain, function (passwObj) {
        // 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);
    getPassword(login,
        urlObj.schema + "://" + urlObj.host,
        function (passwObj) {
            if (!passwObj.password) {
                // TODO this should happen, only when user presses Escape in password prompt
                return null;
            }

            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();
}