<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

let base64 = require("sdk/base64");
let data = require("sdk/self").data;
let events = require("sdk/system/events");
let utils = require("sdk/window/utils");

let { Cc, Ci, Cu } = require("chrome");

let { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
let { ctypes } = Cu.import("resource://gre/modules/ctypes.jsm", {});
let { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});

let gDebug = true;
let gPrefix = "SignText: ";
function log(x) {
  if (gDebug) {
    console.log(gPrefix + x);
  }
}

const CERTCertDBHandle = ctypes.voidptr_t;
const CERTCertificate = ctypes.voidptr_t;
const SECCertUsage = ctypes.int;
const certUsageEmailSigner = 4;
const SECItemType = ctypes.int;
const siBuffer = 0;
const SECItem = ctypes.StructType("SECItem", [
  {"type": SECItemType},
  {"data": ctypes.uint8_t.ptr},
  {"len": ctypes.int}
]);
const SEC_PKCS7ContentInfo = ctypes.voidptr_t;
const SECOidTag = ctypes.int;
const SEC_OID_SHA1 = 4;
const SECStatus = ctypes.int;
const SECSuccess = 0;

let nss3 = null;
let smime3 = null;
let CERT_GetDefaultCertDB = null;
let CERT_FindUserCertByUsage = null;
let CERT_DestroyCertificate = null;
let SECITEM_FreeItem = null;
let SEC_PKCS7CreateSignedData = null;
let SEC_PKCS7IncludeCertChain = null;
let SEC_PKCS7AddSigningTime = null;
let SEC_PKCS7EncodeItem = null;
let SEC_PKCS7DestroyContentInfo = null;

function loadLibraries() {
  let nss3path = ctypes.libraryName("nss3");
  try {
    nss3 = ctypes.open(nss3path);
  } catch (e) {
    log("opening nss3 failed: " + e);
    return;
  }

  let smime3path = ctypes.libraryName("smime3");
  try {
    smime3 = ctypes.open(smime3path);
  } catch (e) {
    log("opening smime3 failed: " + e);
  }

  CERT_GetDefaultCertDB = nss3.declare("CERT_GetDefaultCertDB",
                                       ctypes.default_abi,
                                       CERTCertDBHandle);
  CERT_FindUserCertByUsage = nss3.declare("CERT_FindUserCertByUsage",
                                          ctypes.default_abi,
                                          CERTCertificate,
                                          CERTCertDBHandle,
                                          ctypes.char.ptr,
                                          SECCertUsage,
                                          ctypes.bool,
                                          ctypes.voidptr_t);
  CERT_DestroyCertificate = nss3.declare("CERT_DestroyCertificate",
                                         ctypes.default_abi,
                                         ctypes.void_t,
                                         CERTCertificate);
  SECITEM_FreeItem = nss3.declare("SECITEM_FreeItem",
                                   ctypes.default_abi,
                                   ctypes.void_t,
                                   SECItem.ptr,
                                   ctypes.bool);
  SEC_PKCS7CreateSignedData = smime3.declare("SEC_PKCS7CreateSignedData",
                                             ctypes.default_abi,
                                             SEC_PKCS7ContentInfo,
                                             CERTCertificate,
                                             SECCertUsage,
                                             CERTCertDBHandle,
                                             SECOidTag,
                                             SECItem.ptr,
                                             ctypes.voidptr_t,
                                             ctypes.voidptr_t);
  SEC_PKCS7IncludeCertChain = smime3.declare("SEC_PKCS7IncludeCertChain",
                                             ctypes.default_abi,
                                             SECStatus,
                                             SEC_PKCS7ContentInfo,
                                             ctypes.voidptr_t);
  SEC_PKCS7AddSigningTime = smime3.declare("SEC_PKCS7AddSigningTime",
                                           ctypes.default_abi,
                                           SECStatus,
                                           SEC_PKCS7ContentInfo);
  SEC_PKCS7EncodeItem = smime3.declare("SEC_PKCS7EncodeItem",
                                       ctypes.default_abi,
                                       SECItem.ptr,
                                       ctypes.voidptr_t,
                                       ctypes.voidptr_t,
                                       SEC_PKCS7ContentInfo,
                                       ctypes.voidptr_t,
                                       ctypes.voidptr_t,
                                       ctypes.voidptr_t);
  SEC_PKCS7DestroyContentInfo = smime3.declare("SEC_PKCS7DestroyContentInfo",
                                               ctypes.default_abi,
                                               ctypes.void_t,
                                               SEC_PKCS7ContentInfo);
}

function unloadLibraries() {
  if (nss3) {
    nss3.close();
  }
  if (smime3) {
    smime3.close();
  }
}

function getUserCerts() {
  let certCache = Cc["@mozilla.org/security/nsscertcache;1"]
                    .createInstance(Ci.nsINSSCertCache);
  certCache.cacheAllCerts();
  let certList = certCache.getX509CachedCerts();
  let userCerts = [];
  let certListEnumerator = certList.getEnumerator();
  while (certListEnumerator.hasMoreElements()) {
    let cert = certListEnumerator.getNext().QueryInterface(Ci.nsIX509Cert);
    if (cert.certType & Ci.nsIX509Cert.USER_CERT) {
      userCerts.push(cert);
    }
  }

  return userCerts;
}

function cleanupSignTextResources(cert, contentInfo, encodedItem) {
  try {
    if (cert && !cert.isNull()) {
      CERT_DestroyCertificate(cert);
    }
  } catch (error) {
    log("CERT_DestroyCertificate failed");
  }

  try {
    if (contentInfo && !contentInfo.isNull()) {
      SEC_PKCS7DestroyContentInfo(contentInfo);
    }
  } catch (error) {
    log("SEC_PKCS7DestroyContentInfo failed");
  }

  try {
    if (encodedItem && !encodedItem.isNull()) {
      SECITEM_FreeItem(encodedItem, true);
    }
  } catch (error) {
    log("SECITEM_FreeItem failed");
  }
}

function selectCert(userCerts, text) {
  let sandboxDeclarations = "var domain;\n";
  sandboxDeclarations += "var textToSign;\n";
  sandboxDeclarations += "var certs = {};\n";
  sandboxDeclarations += "var cancelled;\n";
  sandboxDeclarations += "var selectedCert;\n";
  sandboxDeclarations += "function Cert() {}\n";
  let sandbox = Cu.Sandbox(data.url("certChooser.html"));
  Cu.evalInSandbox(sandboxDeclarations, sandbox);
  let domWindow = utils.getMostRecentBrowserWindow();
  sandbox.domain = domWindow.content.location.hostname;
  sandbox.textToSign = text;
  for (let cert of userCerts) {
    let certSubjectName = cert.subjectName;
    if (!certSubjectName) {
      certSubjectName = cert.emailAddress;
    }
    sandbox.certs[cert.nickname] = new sandbox.Cert();
    sandbox.certs[cert.nickname].nickname = cert.nickname;
    sandbox.certs[cert.nickname].subject = certSubjectName;
    sandbox.certs[cert.nickname].issuer = cert.issuerCommonName;
    sandbox.certs[cert.nickname].token = cert.tokenName;
  }
  let watcher = Cc["@mozilla.org/embedcomp/window-watcher;1"]
                  .getService(Ci.nsIWindowWatcher);
  let dialog = domWindow.openDialog(data.url("certChooser.html"),
                                    "_blank",
                                    "dialog,centerscreen,chrome,modal",
                                    sandbox);
  if (sandbox.cancelled) {
    log("error:userCancelled");
    return "error:userCancelled";
  }
  return sandbox.selectedCert;
}

function signText(text) {
  let userCerts = getUserCerts();
  if (userCerts.length < 1) {
    log("error:noMatchingCert");
    return "error:noMatchingCert";
  }

  let certName = selectCert(userCerts, text);
  if (certName == "error:userCancelled") {
    return "error:userCancelled";
  }

  // These are the resources that, if non-null, must be cleaned-up on all code
  // paths in this function.
  let cert = null;
  let contentInfo = null;
  let encodedItem = null;

  try {
    let certDB = CERT_GetDefaultCertDB();
    if (certDB.isNull()) {
      log("CERT_GetDefaultCertDB failed");
      return "error:internalError";
    }
    log("using '" + certName);
    cert = CERT_FindUserCertByUsage(certDB, certName, certUsageEmailSigner,
                                    true, null);
    if (cert.isNull()) {
      log("CERT_FindUserCertByUsage failed");
      return "error:internalError";
    }

    let digestBytes = hash(text);
    const byteArray = ctypes.ArrayType(ctypes.uint8_t);
    let digestBytesBuffer = new byteArray(digestBytes.length);
    for (let i = 0; i < digestBytes.length; i++) {
      digestBytesBuffer[i] = digestBytes.charCodeAt(i);
    }
    let digest = new SECItem;
    digest.type = siBuffer;
    digest.data = digestBytesBuffer;
    digest.len = digestBytes.length;
    contentInfo = SEC_PKCS7CreateSignedData(cert, certUsageEmailSigner, certDB,
                                            SEC_OID_SHA1, digest.address(),
                                            null, null);
    if (contentInfo.isNull()) {
      log("SEC_PKCS7CreateSignedData failed");
      cleanupSignTextResources(cert, contentInfo, encodedItem);
      return "error:internalError";
    }

    let status = SEC_PKCS7IncludeCertChain(contentInfo, null);
    if (status != SECSuccess) {
      log("SEC_PKCS7IncludeCertChain failed");
      cleanupSignTextResources(cert, contentInfo, encodedItem);
      return "error:internalError";
    }

    status = SEC_PKCS7AddSigningTime(contentInfo);
    if (status != SECSuccess) {
      log("SEC_PKCS7AddSigningTime failed");
      cleanupSignTextResources(cert, contentInfo, encodedItem);
      return "error:internalError";
    }

    encodedItem = SEC_PKCS7EncodeItem(null, null, contentInfo, null, null,
                                      null);
    if (encodedItem.isNull()) {
      log("SEC_PKCS7EncodeItem failed");
      cleanupSignTextResources(cert, contentInfo, encodedItem);
      return "error:internalError";
    }
    let data = encodedItem.contents.data;
    let length = encodedItem.contents.len;
    let output = "";
    for (let i = 0; i < length; i++) {
      output += String.fromCharCode(data.contents);
      data = data.increment();
    }
    let result = base64.encode(output);
    cleanupSignTextResources(cert, contentInfo, encodedItem);
    return result;
  } catch (error) {
    log("signText failed: " + error);
    cleanupSignTextResources(cert, contentInfo, encodedItem);
    throw error;
  }

  return "error:internalError";
}

// modified from browser/base/content/aboutaccounts/aboutaccounts.js
function hash(string) {
  let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
                    .createInstance(Ci.nsIScriptableUnicodeConverter);
  converter.charset = "UTF-8"; // XXX TODO: use current document's charset
  // Data is an array of bytes.
  let data = converter.convertToByteArray(string, {});
  let hasher = Cc["@mozilla.org/security/hash;1"]
                 .createInstance(Ci.nsICryptoHash);
  hasher.init(Ci.nsICryptoHash.SHA1);
  hasher.update(data, data.length);

  return hasher.finish(false);
}

function injectSignText(event) {
  // event.subject is an nsIDOMWindow
  // event.data is a string representing the origin
  log("injecting signText for origin " + event.data);
  let domWindow = event.subject;

  // Add signText() to window.crypto
  // Note: If you want to re-add other legacy functions, just repeat
  // these two lines with "defineAs" set to the API name, and the
  // first argument replaced by your implementation function.
  Cu.exportFunction(signText, domWindow.crypto.wrappedJSObject,
                    { defineAs: "signText" });
}

exports.main = function(options, callbacks) {
  if (options.loadReason == "startup") {
    log("startup");
    loadLibraries();
    events.on("content-document-global-created", injectSignText);
  }
};

exports.onUnload = function(reason) {
  if (reason == "shutdown") {
    log("shutdown");
    events.off("content-document-global-created", injectSignText);
    unloadLibraries();
  }
};
<commit_msg>fixes #5 (linebreak output) and minor nits<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

let base64 = require("sdk/base64");
let data = require("sdk/self").data;
let events = require("sdk/system/events");
let utils = require("sdk/window/utils");

let { Cc, Ci, Cu } = require("chrome");

let { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
let { ctypes } = Cu.import("resource://gre/modules/ctypes.jsm", {});
let { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});

let gDebug = true;
let gPrefix = "SignText: ";
function log(x) {
  if (gDebug) {
    console.log(gPrefix + x);
  }
}

const ERROR_NO_MATCHING_CERT = "error:noMatchingCert";
const ERROR_USER_CANCEL = "error:userCancel";
const ERROR_INTERNAL = "error:internalError";

const CERTCertDBHandle = ctypes.voidptr_t;
const CERTCertificate = ctypes.voidptr_t;
const SECCertUsage = ctypes.int;
const certUsageEmailSigner = 4;
const SECItemType = ctypes.int;
const siBuffer = 0;
const SECItem = ctypes.StructType("SECItem", [
  { "type": SECItemType },
  { "data": ctypes.uint8_t.ptr },
  { "len": ctypes.int }
]);
const SEC_PKCS7ContentInfo = ctypes.voidptr_t;
const SECOidTag = ctypes.int;
const SEC_OID_SHA1 = 4;
const SECStatus = ctypes.int;
const SECSuccess = 0;

let nss3 = null;
let smime3 = null;
let CERT_GetDefaultCertDB = null;
let CERT_FindUserCertByUsage = null;
let CERT_DestroyCertificate = null;
let SECITEM_FreeItem = null;
let SEC_PKCS7CreateSignedData = null;
let SEC_PKCS7IncludeCertChain = null;
let SEC_PKCS7AddSigningTime = null;
let SEC_PKCS7EncodeItem = null;
let SEC_PKCS7DestroyContentInfo = null;

function loadLibraries() {
  let nss3path = ctypes.libraryName("nss3");
  try {
    nss3 = ctypes.open(nss3path);
  } catch (e) {
    log("opening nss3 failed: " + e);
    return;
  }

  let smime3path = ctypes.libraryName("smime3");
  try {
    smime3 = ctypes.open(smime3path);
  } catch (e) {
    log("opening smime3 failed: " + e);
  }

  CERT_GetDefaultCertDB = nss3.declare("CERT_GetDefaultCertDB",
                                       ctypes.default_abi,
                                       CERTCertDBHandle);
  CERT_FindUserCertByUsage = nss3.declare("CERT_FindUserCertByUsage",
                                          ctypes.default_abi,
                                          CERTCertificate,
                                          CERTCertDBHandle,
                                          ctypes.char.ptr,
                                          SECCertUsage,
                                          ctypes.bool,
                                          ctypes.voidptr_t);
  CERT_DestroyCertificate = nss3.declare("CERT_DestroyCertificate",
                                         ctypes.default_abi,
                                         ctypes.void_t,
                                         CERTCertificate);
  SECITEM_FreeItem = nss3.declare("SECITEM_FreeItem",
                                   ctypes.default_abi,
                                   ctypes.void_t,
                                   SECItem.ptr,
                                   ctypes.bool);
  SEC_PKCS7CreateSignedData = smime3.declare("SEC_PKCS7CreateSignedData",
                                             ctypes.default_abi,
                                             SEC_PKCS7ContentInfo,
                                             CERTCertificate,
                                             SECCertUsage,
                                             CERTCertDBHandle,
                                             SECOidTag,
                                             SECItem.ptr,
                                             ctypes.voidptr_t,
                                             ctypes.voidptr_t);
  SEC_PKCS7IncludeCertChain = smime3.declare("SEC_PKCS7IncludeCertChain",
                                             ctypes.default_abi,
                                             SECStatus,
                                             SEC_PKCS7ContentInfo,
                                             ctypes.voidptr_t);
  SEC_PKCS7AddSigningTime = smime3.declare("SEC_PKCS7AddSigningTime",
                                           ctypes.default_abi,
                                           SECStatus,
                                           SEC_PKCS7ContentInfo);
  SEC_PKCS7EncodeItem = smime3.declare("SEC_PKCS7EncodeItem",
                                       ctypes.default_abi,
                                       SECItem.ptr,
                                       ctypes.voidptr_t,
                                       ctypes.voidptr_t,
                                       SEC_PKCS7ContentInfo,
                                       ctypes.voidptr_t,
                                       ctypes.voidptr_t,
                                       ctypes.voidptr_t);
  SEC_PKCS7DestroyContentInfo = smime3.declare("SEC_PKCS7DestroyContentInfo",
                                               ctypes.default_abi,
                                               ctypes.void_t,
                                               SEC_PKCS7ContentInfo);
}

function unloadLibraries() {
  if (nss3) {
    nss3.close();
  }
  if (smime3) {
    smime3.close();
  }
}

function getUserCerts() {
  let certCache = Cc["@mozilla.org/security/nsscertcache;1"]
                    .createInstance(Ci.nsINSSCertCache);
  certCache.cacheAllCerts();
  let certList = certCache.getX509CachedCerts();
  let userCerts = [];
  let certListEnumerator = certList.getEnumerator();
  while (certListEnumerator.hasMoreElements()) {
    let cert = certListEnumerator.getNext().QueryInterface(Ci.nsIX509Cert);
    if (cert.certType & Ci.nsIX509Cert.USER_CERT) {
      userCerts.push(cert);
    }
  }

  return userCerts;
}

function cleanupSignTextResources(cert, contentInfo, encodedItem) {
  try {
    if (cert && !cert.isNull()) {
      CERT_DestroyCertificate(cert);
    }
  } catch (error) {
    log("CERT_DestroyCertificate failed");
  }

  try {
    if (contentInfo && !contentInfo.isNull()) {
      SEC_PKCS7DestroyContentInfo(contentInfo);
    }
  } catch (error) {
    log("SEC_PKCS7DestroyContentInfo failed");
  }

  try {
    if (encodedItem && !encodedItem.isNull()) {
      SECITEM_FreeItem(encodedItem, true);
    }
  } catch (error) {
    log("SECITEM_FreeItem failed");
  }
}

function selectCert(userCerts, text) {
  // We have to create a sandbox with the same origin as this addon's content
  // resources so we can pass data into (and get it back out of) the dialog we
  // open up to let the user select a signing certificate and confirm the
  // signing.
  let sandboxDeclarations = "var domain;\n";
  sandboxDeclarations += "var textToSign;\n";
  sandboxDeclarations += "var certs = {};\n";
  sandboxDeclarations += "var cancelled;\n";
  sandboxDeclarations += "var selectedCert;\n";
  sandboxDeclarations += "function Cert() {};\n";
  let sandbox = Cu.Sandbox(data.url("certChooser.html"));
  Cu.evalInSandbox(sandboxDeclarations, sandbox);
  let domWindow = utils.getMostRecentBrowserWindow();
  sandbox.domain = domWindow.content.location.hostname;
  sandbox.textToSign = text;
  for (let cert of userCerts) {
    let certSubjectName = cert.subjectName;
    if (!certSubjectName) {
      certSubjectName = cert.emailAddress;
    }
    sandbox.certs[cert.nickname] = new sandbox.Cert();
    sandbox.certs[cert.nickname].nickname = cert.nickname;
    sandbox.certs[cert.nickname].subject = certSubjectName;
    sandbox.certs[cert.nickname].issuer = cert.issuerCommonName;
    sandbox.certs[cert.nickname].token = cert.tokenName;
  }
  let watcher = Cc["@mozilla.org/embedcomp/window-watcher;1"]
                  .getService(Ci.nsIWindowWatcher);
  let dialog = domWindow.openDialog(data.url("certChooser.html"),
                                    "_blank",
                                    "dialog,centerscreen,chrome,modal",
                                    sandbox);
  if (sandbox.cancelled) {
    return ERROR_USER_CANCEL;
  }
  return sandbox.selectedCert;
}

function signText(text) {
  let userCerts = getUserCerts();
  if (userCerts.length < 1) {
    return ERROR_NO_MATCHING_CERT;
  }

  let certName = selectCert(userCerts, text);
  if (certName == ERROR_USER_CANCEL) {
    return ERROR_USER_CANCEL;
  }

  // These are the resources that, if non-null, must be cleaned-up on all code
  // paths in this function.
  let cert = null;
  let contentInfo = null;
  let encodedItem = null;

  try {
    let certDB = CERT_GetDefaultCertDB();
    if (certDB.isNull()) {
      log("CERT_GetDefaultCertDB failed");
      return ERROR_INTERNAL;
    }
    log("using '" + certName);
    cert = CERT_FindUserCertByUsage(certDB, certName, certUsageEmailSigner,
                                    true, null);
    if (cert.isNull()) {
      log("CERT_FindUserCertByUsage failed");
      return ERROR_INTERNAL;
    }

    let digestBytes = hash(text);
    let byteArray = ctypes.ArrayType(ctypes.uint8_t);
    let digestBytesBuffer = new byteArray(digestBytes.length);
    for (let i = 0; i < digestBytes.length; i++) {
      digestBytesBuffer[i] = digestBytes.charCodeAt(i);
    }
    let digest = new SECItem;
    digest.type = siBuffer;
    digest.data = digestBytesBuffer;
    digest.len = digestBytes.length;
    contentInfo = SEC_PKCS7CreateSignedData(cert, certUsageEmailSigner, certDB,
                                            SEC_OID_SHA1, digest.address(),
                                            null, null);
    if (contentInfo.isNull()) {
      log("SEC_PKCS7CreateSignedData failed");
      cleanupSignTextResources(cert, contentInfo, encodedItem);
      return ERROR_INTERNAL;
    }

    let status = SEC_PKCS7IncludeCertChain(contentInfo, null);
    if (status != SECSuccess) {
      log("SEC_PKCS7IncludeCertChain failed");
      cleanupSignTextResources(cert, contentInfo, encodedItem);
      return ERROR_INTERNAL;
    }

    status = SEC_PKCS7AddSigningTime(contentInfo);
    if (status != SECSuccess) {
      log("SEC_PKCS7AddSigningTime failed");
      cleanupSignTextResources(cert, contentInfo, encodedItem);
      return ERROR_INTERNAL;
    }

    encodedItem = SEC_PKCS7EncodeItem(null, null, contentInfo, null, null,
                                      null);
    if (encodedItem.isNull()) {
      log("SEC_PKCS7EncodeItem failed");
      cleanupSignTextResources(cert, contentInfo, encodedItem);
      return ERROR_INTERNAL;
    }
    let data = encodedItem.contents.data;
    let length = encodedItem.contents.len;
    let output = "";
    for (let i = 0; i < length; i++) {
      output += String.fromCharCode(data.contents);
      data = data.increment();
    }
    cleanupSignTextResources(cert, contentInfo, encodedItem);
    let result = base64.encode(output).replace(/.{64}/g, "$&\n");
    return result;
  } catch (error) {
    log("signText failed: " + error);
    cleanupSignTextResources(cert, contentInfo, encodedItem);
  }

  return ERROR_INTERNAL;
}

// modified from browser/base/content/aboutaccounts/aboutaccounts.js
function hash(string) {
  let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
                    .createInstance(Ci.nsIScriptableUnicodeConverter);
  converter.charset = "UTF-8"; // XXX TODO: use current document's charset
  // Data is an array of bytes.
  let data = converter.convertToByteArray(string, {});
  let hasher = Cc["@mozilla.org/security/hash;1"]
                 .createInstance(Ci.nsICryptoHash);
  hasher.init(Ci.nsICryptoHash.SHA1);
  hasher.update(data, data.length);

  return hasher.finish(false);
}

function injectSignText(event) {
  // event.subject is an nsIDOMWindow
  // event.data is a string representing the origin
  log("injecting signText for origin " + event.data);
  let domWindow = event.subject;

  // Add signText() to window.crypto
  // Note: If you want to re-add other legacy functions, just repeat
  // these two lines with "defineAs" set to the API name, and the
  // first argument replaced by your implementation function.
  Cu.exportFunction(signText, domWindow.crypto.wrappedJSObject,
                    { defineAs: "signText" });
}

exports.main = function(options, callbacks) {
  if (options.loadReason == "startup") {
    log("startup");
    loadLibraries();
    events.on("content-document-global-created", injectSignText);
  }
};

exports.onUnload = function(reason) {
  if (reason == "shutdown") {
    log("shutdown");
    events.off("content-document-global-created", injectSignText);
    unloadLibraries();
  }
};
<|endoftext|>


<commit_before>"use strict";
var Promise = require("bluebird");
var extend = require("extend");
var Datastore = require("nedb");
var fs = require("fs");

var AppServiceRegistration = require("matrix-appservice-bridge").AppServiceRegistration;
var RoomBridgeStore = require("matrix-appservice-bridge").RoomBridgeStore;
var UserBridgeStore = require("matrix-appservice-bridge").UserBridgeStore;

var IrcBridge = require("./bridge/IrcBridge.js");
var IrcServer = require("./irc/IrcServer.js");
var stats = require("./config/stats");
var ident = require("./irc/ident");
var logging = require("./logging");
var log = logging.get("main");

// Allow up to 1000 HTTP(S) sockets. Default is Infinity, which means that a slow HS coupled
// with lots of traffic could consume up to the fd ulimit on a process.
require("http").globalAgent.maxSockets = 1000;
require("https").globalAgent.maxSockets = 1000;

process.on("unhandledRejection", function(reason, promise) {
    log.error(reason ? reason.stack : "No reason given");
});

function enableCpuProfiling() {
    var profiler = require('v8-profiler');
    process.on('SIGUSR2', function() {
        log.warn("cpuprofile: Recevied SIGUSR2: starting cpu profiling");
        profiler.startProfiling("", true);
        setTimeout(function() {
            log.warn("cpuprofile: stopping cpu profiling");
            var profile = profiler.stopProfiling("");
            profile.export(function(err, res) {
                log.warn("cpuprofile: exported. Writing file. err="+err);
                if (err) {
                    return;
                }
                fs.writeFile(
                    new Date().toISOString().replace(/[.:TZ]/g, "") + ".cpuprofile",
                    res,
                    function(err) {
                        if (err) {
                            log.warn("cpuprofile: failed to write .cpuprofile - " + err);
                        }
                    }
                );

            });
        }, 10 * 1000);
    });
}

enableCpuProfiling(); // can't do this and heap dumps at the same time since both want SIGUSR2

var _toServer = function(domain, serverConfig, homeserverDomain) {
    // set server config defaults
    if (serverConfig.dynamicChannels.visibility) {
        throw new Error(
            `[DEPRECATED] Use of the config field dynamicChannels.visibility
            is deprecated. Use dynamicChannels.published, dynamicChannels.joinRule
            and dynamicChannels.createAlias instead.`
        );
    }
    return new IrcServer(
        domain, extend(true, IrcServer.DEFAULT_CONFIG, serverConfig), homeserverDomain
    );
};

module.exports.generateRegistration = Promise.coroutine(function*(reg, config) {
    var asToken;
    if (config.appService) {
        console.warn(
            `[DEPRECATED] Use of config field 'appService' is deprecated.
            Remove this field from the config file to remove this warning.

            This release will use values from this config file. This will produce
            a fatal error in a later release.

            The new format looks like:
            homeserver:
                url: "https://home.server.url"
                domain: "home.server.url"

            The new locations for the missing fields are as follows:
            http.port - Passed as a CLI flag --port.
            appservice.token - Automatically generated.
            appservice.url - Passed as a CLI flag --url
            localpart - Passed as a CLI flag --localpart
            `
        );
        if (config.appService.localpart) {
            console.log("NOTICE: Using localpart from config file");
            reg.setSenderLocalpart(config.appService.localpart);
        }
        asToken = config.appService.appservice.token;
    }

    if (!reg.getSenderLocalpart()) {
        reg.setSenderLocalpart(IrcBridge.DEFAULT_LOCALPART);
    }
    reg.setId(AppServiceRegistration.generateToken());
    reg.setHomeserverToken(AppServiceRegistration.generateToken());
    reg.setAppServiceToken(asToken || AppServiceRegistration.generateToken());

    // Disable rate limiting to allow large numbers of requests when many IRC users
    // connect, for example on startup.
    reg.setRateLimited(false);

    let serverDomains = Object.keys(config.ircService.servers);
    serverDomains.sort().forEach(function(domain) {
        let server = _toServer(domain, config.ircService.servers[domain], config.homeserver.domain);
        server.getHardCodedRoomIds().sort().forEach(function(roomId) {
            reg.addRegexPattern("rooms", roomId, false);
        });
        // add an alias pattern for servers who want aliases exposed.
        if (server.createsDynamicAliases()) {
            reg.addRegexPattern("aliases", server.getAliasRegex(), true);
        }
        reg.addRegexPattern("users", server.getUserRegex(), true);
    });

    return reg;
});

var ircBridge;
module.exports.runBridge = Promise.coroutine(function*(port, config, reg, isDBInMemory) {
    // configure global stuff for the process
    if (config.ircService.logging) {
        logging.configure(config.ircService.logging);
        logging.setUncaughtExceptionLogger(log);
    }
    if (config.ircService.statsd.hostname) {
        stats.setEndpoint(config.ircService.statsd);
    }
    if (config.ircService.ident.enabled) {
        ident.configure(config.ircService.ident);
        ident.run();
    }

    // backwards compat for 1 release. TODO remove
    if (config.appService && !config.homeserver) {
        config.homeserver = config.appService.homeserver;
    }

    if (ircBridge) {
        log.warn('Bridge already running, destroying reference to existing bridge!');
    }

    // run the bridge
    ircBridge = new IrcBridge(config, reg);

    // Use in-memory DBs
    if (isDBInMemory) {
        ircBridge._bridge.opts.roomStore = new RoomBridgeStore(new Datastore());
        ircBridge._bridge.opts.userStore = new UserBridgeStore(new Datastore());
    }

    yield ircBridge.run(port);
});

module.exports.killBridge = function() {
    if (!ircBridge) {
        log.info('killBridge(): No bridge running');
        return Promise.resolve();
    }
    log.info('Killing bridge');
    return ircBridge.kill();
}
<commit_msg>Don't shadow err<commit_after>"use strict";
var Promise = require("bluebird");
var extend = require("extend");
var Datastore = require("nedb");
var fs = require("fs");

var AppServiceRegistration = require("matrix-appservice-bridge").AppServiceRegistration;
var RoomBridgeStore = require("matrix-appservice-bridge").RoomBridgeStore;
var UserBridgeStore = require("matrix-appservice-bridge").UserBridgeStore;

var IrcBridge = require("./bridge/IrcBridge.js");
var IrcServer = require("./irc/IrcServer.js");
var stats = require("./config/stats");
var ident = require("./irc/ident");
var logging = require("./logging");
var log = logging.get("main");

// Allow up to 1000 HTTP(S) sockets. Default is Infinity, which means that a slow HS coupled
// with lots of traffic could consume up to the fd ulimit on a process.
require("http").globalAgent.maxSockets = 1000;
require("https").globalAgent.maxSockets = 1000;

process.on("unhandledRejection", function(reason, promise) {
    log.error(reason ? reason.stack : "No reason given");
});

function enableCpuProfiling() {
    var profiler = require('v8-profiler');
    process.on('SIGUSR2', function() {
        log.warn("cpuprofile: Recevied SIGUSR2: starting cpu profiling");
        profiler.startProfiling("", true);
        setTimeout(function() {
            log.warn("cpuprofile: stopping cpu profiling");
            var profile = profiler.stopProfiling("");
            profile.export(function(err, res) {
                log.warn("cpuprofile: exported. Writing file. err="+err);
                if (err) {
                    return;
                }
                fs.writeFile(
                    new Date().toISOString().replace(/[.:TZ]/g, "") + ".cpuprofile",
                    res,
                    function(err2) {
                        if (err2) {
                            log.warn("cpuprofile: failed to write .cpuprofile - " + err2);
                        }
                    }
                );

            });
        }, 10 * 1000);
    });
}

enableCpuProfiling(); // can't do this and heap dumps at the same time since both want SIGUSR2

var _toServer = function(domain, serverConfig, homeserverDomain) {
    // set server config defaults
    if (serverConfig.dynamicChannels.visibility) {
        throw new Error(
            `[DEPRECATED] Use of the config field dynamicChannels.visibility
            is deprecated. Use dynamicChannels.published, dynamicChannels.joinRule
            and dynamicChannels.createAlias instead.`
        );
    }
    return new IrcServer(
        domain, extend(true, IrcServer.DEFAULT_CONFIG, serverConfig), homeserverDomain
    );
};

module.exports.generateRegistration = Promise.coroutine(function*(reg, config) {
    var asToken;
    if (config.appService) {
        console.warn(
            `[DEPRECATED] Use of config field 'appService' is deprecated.
            Remove this field from the config file to remove this warning.

            This release will use values from this config file. This will produce
            a fatal error in a later release.

            The new format looks like:
            homeserver:
                url: "https://home.server.url"
                domain: "home.server.url"

            The new locations for the missing fields are as follows:
            http.port - Passed as a CLI flag --port.
            appservice.token - Automatically generated.
            appservice.url - Passed as a CLI flag --url
            localpart - Passed as a CLI flag --localpart
            `
        );
        if (config.appService.localpart) {
            console.log("NOTICE: Using localpart from config file");
            reg.setSenderLocalpart(config.appService.localpart);
        }
        asToken = config.appService.appservice.token;
    }

    if (!reg.getSenderLocalpart()) {
        reg.setSenderLocalpart(IrcBridge.DEFAULT_LOCALPART);
    }
    reg.setId(AppServiceRegistration.generateToken());
    reg.setHomeserverToken(AppServiceRegistration.generateToken());
    reg.setAppServiceToken(asToken || AppServiceRegistration.generateToken());

    // Disable rate limiting to allow large numbers of requests when many IRC users
    // connect, for example on startup.
    reg.setRateLimited(false);

    let serverDomains = Object.keys(config.ircService.servers);
    serverDomains.sort().forEach(function(domain) {
        let server = _toServer(domain, config.ircService.servers[domain], config.homeserver.domain);
        server.getHardCodedRoomIds().sort().forEach(function(roomId) {
            reg.addRegexPattern("rooms", roomId, false);
        });
        // add an alias pattern for servers who want aliases exposed.
        if (server.createsDynamicAliases()) {
            reg.addRegexPattern("aliases", server.getAliasRegex(), true);
        }
        reg.addRegexPattern("users", server.getUserRegex(), true);
    });

    return reg;
});

var ircBridge;
module.exports.runBridge = Promise.coroutine(function*(port, config, reg, isDBInMemory) {
    // configure global stuff for the process
    if (config.ircService.logging) {
        logging.configure(config.ircService.logging);
        logging.setUncaughtExceptionLogger(log);
    }
    if (config.ircService.statsd.hostname) {
        stats.setEndpoint(config.ircService.statsd);
    }
    if (config.ircService.ident.enabled) {
        ident.configure(config.ircService.ident);
        ident.run();
    }

    // backwards compat for 1 release. TODO remove
    if (config.appService && !config.homeserver) {
        config.homeserver = config.appService.homeserver;
    }

    if (ircBridge) {
        log.warn('Bridge already running, destroying reference to existing bridge!');
    }

    // run the bridge
    ircBridge = new IrcBridge(config, reg);

    // Use in-memory DBs
    if (isDBInMemory) {
        ircBridge._bridge.opts.roomStore = new RoomBridgeStore(new Datastore());
        ircBridge._bridge.opts.userStore = new UserBridgeStore(new Datastore());
    }

    yield ircBridge.run(port);
});

module.exports.killBridge = function() {
    if (!ircBridge) {
        log.info('killBridge(): No bridge running');
        return Promise.resolve();
    }
    log.info('Killing bridge');
    return ircBridge.kill();
}
<|endoftext|>


<commit_before>/*
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * ndf command protocol
 *
 * list systems
 * list container systemid
 *
 *
 */

'use strict';

var cmdsrv = require('cmdsrv');
var netOut = require('./networkOut');
var auth = require('../../nfd-auth')();

var logger = require('winston');

module.exports = function(api) {
  var _server;

  var login = function(connection, username, password) {
    var out = netOut(connection, 'login ' + username);
    auth.login(username, password, function(err, result) {
      if (err) {logger.error(err); return out.stderr(err);}
      if (result.user) {
        connection.user = result.user;
      }
      out.response(result);
    });
  };

  var token = function(connection, token) {
    var out = netOut(connection, 'token ' + token);
    auth.userInfo(token, function(err, user) {
      if (err) {return out.stderr(err);}
      connection.user = user;
      out.response(connection.user);
    });
  };

  var authorized = function(connection, out) {
    if (!connection.user) {
      out.response({ok:false, err:'Unauthorized'});
    }
    return !!connection.user;
  };

  var githubLogin = function(connection, accessToken) {
    var out = netOut(connection, 'githublogin');
    auth.githubLogin({accessToken: accessToken}, function(err, result) {
      if (err) {logger.error(err); return out.stderr(err);}
      if (result.user) {
        connection.user = result.user;
      }
      out.response(result);
    });
  };

  var listSystems = function(out) {
    api.listSystems(function(err, systems) {
      logger.info('rsp: list systems');
      console.log(JSON.stringify(systems, null, 2));
      out.response(systems);
    });
  };

  var createSystem = function(out, name, namespace) {
    api.createSystem(name, namespace, function(err, system) {
      logger.info('rsp: create system');
      out.response(system);
    });
  };

  var getSystem = function(out, systemId) {
    api.getHeadSystem(systemId, function(err, system) {
      logger.info('rsp: get system ' + systemId);
      out.response(system);
    });
  };

  var getDeployed = function(out, systemId) {
    api.getDeployedSystem(systemId, function(err, system) {
      logger.info('rsp: get deployed ' + systemId);
      out.response(system);
    });
  };

  var putSystem = function(out, systemJson, user) {
    api.putSystem(user, systemJson, function(err) {
      logger.info('rsp: put system');
      if (err) {
        out.response({result: 'err', err: err});
      }
      else {
        out.response({result: 'ok'});
      }
    });
  };

  var deleteSystem = function(out, user, systemId) {
    api.deleteSystem(user, systemId, function(err) {
      logger.info('rsp: delete system');
      if (err) { out.response({result: 'err', err: err}); }
      else { out.response({result: 'ok'}); }
    });
  };

  var listContainers = function(out, systemId) {
    api.listContainers(systemId, function(err, containers) {
      logger.info('rsp: list containers ' + systemId);
      out.response(containers);
    });
  };

  var addContainer = function(out, containerJson, user, systemId) {
    api.addContainer(user, systemId, containerJson, function(err) {
      logger.info('rsp: add container');
      if (err) { out.response({result: 'err', err: err}); }
      else { out.response({result: 'ok'}); }
    });
  };

  var putContainer = function(out, containerJson, user, systemId) {
    api.putContainer(user, systemId, containerJson, function(err) {
      logger.info('rsp: put container');
      if (err) { out.response({result: 'err', err: err}); }
      else { out.response({result: 'ok'}); }
    });
  };

  var deleteContainer = function(out, user, systemId, containerId) {
    api.deleteContainer(user, systemId, containerId, function(err) {
      logger.info('rsp: delete container');
      if (err) { out.response({result: 'err', err: err}); }
      else { out.response({result: 'ok'}); }
    });
  };

  var buildContainer = function(out, user, systemId, containerId) {
    api.buildContainer(user, systemId, containerId, out, function(err, result) {
      logger.info('rsp: build container ' + systemId);
      out.response(result);
    });
  };

  var deploySystem = function(out, user, systemId, revisionId) {
    api.deploySystem(user, systemId, revisionId, out, function(err, result) {
      logger.info('rsp: deploy system ' + user + ' ' + systemId + ' ' + revisionId);
      out.response(result);
    });
  };

  var deployAll = function(out, systemId, revisionId) {
    api.deployAll(systemId, revisionId, out, function(err) {
      logger.info('rsp: deploy all' + systemId + ' ' + revisionId);
      if (err) { out.response({result: 'err', err: err}); }
      else { out.response({result: 'ok'}); }
    });
  };

  var listRevisions = function(out, systemId) {
    api.listRevisions(systemId, function(err, result) {
      logger.info('rsp: list revisions ' + systemId);
      out.response(result);
    });
  };

  var getRevision = function(out, systemId, revisionId) {
    api.getRevision(systemId, revisionId, function(err, result) {
      logger.info('rsp: get revision ' + systemId);
      out.response(result);
    });
  };

  var timeline = function(out, systemId, containerId, user) {
    api.timeline(systemId, containerId, user, function(err, result) {
      logger.info('rsp: timeline');
      out.response(result);
    });
  };

  var addToTimeline = function(out, timelineJson) {
    api.addToTimeline(timelineJson, function(err) {
      logger.info('rsp: add to timeline');
      if (err) { out.response({result: 'err', err: err}); }
      else { out.response({result: 'ok'}); }
    });
  };

  var quitConnection = function(connection, out) {
    logger.info('rsp: quit');
    out.response({bye: 'bye!'}, function() {
      connection.end();
    });
  };

  var handlers = {
                  login: login,
                  githubLogin: githubLogin,
                  token: token,
                  authorized: authorized,
                  list: { systems: listSystems,
                          containers: listContainers,
                          revisions: listRevisions,
                          timeline: timeline},
                  create: {system: createSystem},
                  get: {system: getSystem, revision: getRevision, deployed: getDeployed},
                  add: {container: addContainer, timeline: addToTimeline},
                  put: {system: putSystem, container: putContainer},
                  delete: {system: deleteSystem, container: deleteContainer},
                  build: { container: buildContainer},
                  deploy: { system: deploySystem, all: deployAll},
                  quit: quitConnection };

  /**
   * construct the server
   */
  var construct = function() {
    _server = new cmdsrv();

    var mode = 'command';
    var put = '';
    var lineTarget;
    var lineUser;
    var lineId;

    _server.on('login', function(connection, username, password){
      logger.info('rcv: login ' + username);
      handlers.login(connection, username, password);
    });

    _server.on('githublogin', function(connection, accessToken){
      logger.info('rcv: githublogin');
      handlers.githubLogin(connection, accessToken);
    });

    _server.on('token', function(connection, token){
      logger.info('rcv: token ' + token);
      handlers.token(connection, token);
    });

    _server.on('list', function(connection, target, systemId, containerId, user){
      var req = 'list ' + target;
      if (!handlers.authorized(connection, netOut(connection, req))) {return;}
      logger.info('rcv: ' + req);
      handlers.list[target](netOut(connection, req), systemId, containerId, user);
    });

    _server.on('create', function(connection, target, name, namespace){
      var req = 'create ' + target;
      if (!handlers.authorized(connection, netOut(connection, req))) {return;}
      logger.info('rcv: ' + req);
      handlers.create[target](netOut(connection, 'create ' + target), name, namespace);
    });

    _server.on('get', function(connection, target, systemId, otherId){
      var req = 'get ' + target;
      if (!handlers.authorized(connection, netOut(connection, req))) {return;}
      logger.info('rcv: ' + req);
      handlers.get[target](netOut(connection, 'get ' + target), systemId, otherId);
    });

    _server.on('delete', function(connection, target, user, systemId, containerId){
      var req = 'delete ' + target;
      if (!handlers.authorized(connection, netOut(connection, req))) {return;}
      logger.info('rcv: ' + req);
      handlers.delete[target](netOut(connection, 'delete ' + target), user, systemId, containerId);
    });

    _server.on('add', function(connection, target, user, id) {
      var req = 'add ' + target;
      if (!handlers.authorized(connection, netOut(connection, req))) {return;}
      logger.info('rcv: ' + req);

      mode = 'lineAdd';
      put = '';
      lineTarget = target;
      lineUser = user;
      lineId = id;
    });

    _server.on('put', function(connection, target, user, id){
      var req = 'put ' + target;
      if (!handlers.authorized(connection, netOut(connection, req))) {return;}
      logger.info('rcv: ' + req);

      mode = 'linePut';
      put = '';
      lineTarget = target;
      lineUser = user;
      lineId = id;
    });

    _server.on('build', function(connection, target, user, systemId, containerId){
      var req = 'build ' + target;
      if (!handlers.authorized(connection, netOut(connection, req))) {return;}
      logger.info('rcv: ' + req);
      handlers.build[target](netOut(connection, 'build ' + target), user, systemId, containerId);
    });

    _server.on('deploy', function(connection, target, user, systemId, revisionId){
      var req = 'deploy ' + target;
      if (!handlers.authorized(connection, netOut(connection, req))) {return;}
      logger.info('rcv: ' + req);
      handlers.deploy[target](netOut(connection, 'deploy ' + target), user, systemId, revisionId);
    });

    _server.on('quit', function(connection){
      logger.info('rcv: quit');
      handlers.quit(connection, netOut(connection, 'quit'));
    });

    _server.on('line', function(connection, line) {
      console.log('LINE: ' + line);

      if (mode === 'linePut') {
        if (line.indexOf('END') !== -1) {
          mode = 'command';
          handlers.put[lineTarget](netOut(connection, 'put ' + lineTarget), put, lineUser, lineId);
        }
        else {
          put += line;
        }
      }
      if (mode === 'lineAdd') {
        if (line.indexOf('END') !== -1) {
          mode = 'command';
          handlers.add[lineTarget](netOut(connection, 'add ' + lineTarget), put, lineUser, lineId);
        }
        else {
          put += line;
        }
      }
    });

    logger.info('starting protocol...');
    _server.start();
  };

  construct();
  return {
  };
};

<commit_msg>update to cater for nfd-auth changes<commit_after>/*
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * ndf command protocol
 *
 * list systems
 * list container systemid
 *
 *
 */

'use strict';

var cmdsrv = require('cmdsrv');
var netOut = require('./networkOut');
var auth = require('../../nfd-auth')();

var logger = require('winston');

module.exports = function(api) {
  var _server;

  var login = function(connection, username, password) {
    var out = netOut(connection, 'login ' + username);
    auth.login(username, password, function(err, result) {
      if (err) {logger.error(err); return out.stderr(err);}
      if (result.user) {
        connection.user = result.user;
      }
      out.response(result);
    });
  };

  var token = function(connection, token) {
    var out = netOut(connection, 'token ' + token);
    auth.userInfo(token, function(err, user) {
      if (err) {return out.stderr(err);}
      connection.user = user;
      out.response(connection.user);
    });
  };

  var authorized = function(connection, out) {
    if (!connection.user) {
      out.response({ok:false, err:'Unauthorized'});
    }
    return !!connection.user;
  };

  var githubLogin = function(connection, accessToken) {
    var out = netOut(connection, 'githublogin');
    auth.githubLogin(accessToken, function(err, result) {
      if (err) {logger.error(err); return out.stderr(err);}
      if (result.user) {
        connection.user = result.user;
      }
      out.response(result);
    });
  };

  var listSystems = function(out) {
    api.listSystems(function(err, systems) {
      logger.info('rsp: list systems');
      console.log(JSON.stringify(systems, null, 2));
      out.response(systems);
    });
  };

  var createSystem = function(out, name, namespace) {
    api.createSystem(name, namespace, function(err, system) {
      logger.info('rsp: create system');
      out.response(system);
    });
  };

  var getSystem = function(out, systemId) {
    api.getHeadSystem(systemId, function(err, system) {
      logger.info('rsp: get system ' + systemId);
      out.response(system);
    });
  };

  var getDeployed = function(out, systemId) {
    api.getDeployedSystem(systemId, function(err, system) {
      logger.info('rsp: get deployed ' + systemId);
      out.response(system);
    });
  };

  var putSystem = function(out, systemJson, user) {
    api.putSystem(user, systemJson, function(err) {
      logger.info('rsp: put system');
      if (err) {
        out.response({result: 'err', err: err});
      }
      else {
        out.response({result: 'ok'});
      }
    });
  };

  var deleteSystem = function(out, user, systemId) {
    api.deleteSystem(user, systemId, function(err) {
      logger.info('rsp: delete system');
      if (err) { out.response({result: 'err', err: err}); }
      else { out.response({result: 'ok'}); }
    });
  };

  var listContainers = function(out, systemId) {
    api.listContainers(systemId, function(err, containers) {
      logger.info('rsp: list containers ' + systemId);
      out.response(containers);
    });
  };

  var addContainer = function(out, containerJson, user, systemId) {
    api.addContainer(user, systemId, containerJson, function(err) {
      logger.info('rsp: add container');
      if (err) { out.response({result: 'err', err: err}); }
      else { out.response({result: 'ok'}); }
    });
  };

  var putContainer = function(out, containerJson, user, systemId) {
    api.putContainer(user, systemId, containerJson, function(err) {
      logger.info('rsp: put container');
      if (err) { out.response({result: 'err', err: err}); }
      else { out.response({result: 'ok'}); }
    });
  };

  var deleteContainer = function(out, user, systemId, containerId) {
    api.deleteContainer(user, systemId, containerId, function(err) {
      logger.info('rsp: delete container');
      if (err) { out.response({result: 'err', err: err}); }
      else { out.response({result: 'ok'}); }
    });
  };

  var buildContainer = function(out, user, systemId, containerId) {
    api.buildContainer(user, systemId, containerId, out, function(err, result) {
      logger.info('rsp: build container ' + systemId);
      out.response(result);
    });
  };

  var deploySystem = function(out, user, systemId, revisionId) {
    api.deploySystem(user, systemId, revisionId, out, function(err, result) {
      logger.info('rsp: deploy system ' + user + ' ' + systemId + ' ' + revisionId);
      out.response(result);
    });
  };

  var deployAll = function(out, systemId, revisionId) {
    api.deployAll(systemId, revisionId, out, function(err) {
      logger.info('rsp: deploy all' + systemId + ' ' + revisionId);
      if (err) { out.response({result: 'err', err: err}); }
      else { out.response({result: 'ok'}); }
    });
  };

  var listRevisions = function(out, systemId) {
    api.listRevisions(systemId, function(err, result) {
      logger.info('rsp: list revisions ' + systemId);
      out.response(result);
    });
  };

  var getRevision = function(out, systemId, revisionId) {
    api.getRevision(systemId, revisionId, function(err, result) {
      logger.info('rsp: get revision ' + systemId);
      out.response(result);
    });
  };

  var timeline = function(out, systemId, containerId, user) {
    api.timeline(systemId, containerId, user, function(err, result) {
      logger.info('rsp: timeline');
      out.response(result);
    });
  };

  var addToTimeline = function(out, timelineJson) {
    api.addToTimeline(timelineJson, function(err) {
      logger.info('rsp: add to timeline');
      if (err) { out.response({result: 'err', err: err}); }
      else { out.response({result: 'ok'}); }
    });
  };

  var quitConnection = function(connection, out) {
    logger.info('rsp: quit');
    out.response({bye: 'bye!'}, function() {
      connection.end();
    });
  };

  var handlers = {
                  login: login,
                  githubLogin: githubLogin,
                  token: token,
                  authorized: authorized,
                  list: { systems: listSystems,
                          containers: listContainers,
                          revisions: listRevisions,
                          timeline: timeline},
                  create: {system: createSystem},
                  get: {system: getSystem, revision: getRevision, deployed: getDeployed},
                  add: {container: addContainer, timeline: addToTimeline},
                  put: {system: putSystem, container: putContainer},
                  delete: {system: deleteSystem, container: deleteContainer},
                  build: { container: buildContainer},
                  deploy: { system: deploySystem, all: deployAll},
                  quit: quitConnection };

  /**
   * construct the server
   */
  var construct = function() {
    _server = new cmdsrv();

    var mode = 'command';
    var put = '';
    var lineTarget;
    var lineUser;
    var lineId;

    _server.on('login', function(connection, username, password){
      logger.info('rcv: login ' + username);
      handlers.login(connection, username, password);
    });

    _server.on('githublogin', function(connection, accessToken){
      logger.info('rcv: githublogin');
      handlers.githubLogin(connection, accessToken);
    });

    _server.on('token', function(connection, token){
      logger.info('rcv: token ' + token);
      handlers.token(connection, token);
    });

    _server.on('list', function(connection, target, systemId, containerId, user){
      var req = 'list ' + target;
      if (!handlers.authorized(connection, netOut(connection, req))) {return;}
      logger.info('rcv: ' + req);
      handlers.list[target](netOut(connection, req), systemId, containerId, user);
    });

    _server.on('create', function(connection, target, name, namespace){
      var req = 'create ' + target;
      if (!handlers.authorized(connection, netOut(connection, req))) {return;}
      logger.info('rcv: ' + req);
      handlers.create[target](netOut(connection, 'create ' + target), name, namespace);
    });

    _server.on('get', function(connection, target, systemId, otherId){
      var req = 'get ' + target;
      if (!handlers.authorized(connection, netOut(connection, req))) {return;}
      logger.info('rcv: ' + req);
      handlers.get[target](netOut(connection, 'get ' + target), systemId, otherId);
    });

    _server.on('delete', function(connection, target, user, systemId, containerId){
      var req = 'delete ' + target;
      if (!handlers.authorized(connection, netOut(connection, req))) {return;}
      logger.info('rcv: ' + req);
      handlers.delete[target](netOut(connection, 'delete ' + target), user, systemId, containerId);
    });

    _server.on('add', function(connection, target, user, id) {
      var req = 'add ' + target;
      if (!handlers.authorized(connection, netOut(connection, req))) {return;}
      logger.info('rcv: ' + req);

      mode = 'lineAdd';
      put = '';
      lineTarget = target;
      lineUser = user;
      lineId = id;
    });

    _server.on('put', function(connection, target, user, id){
      var req = 'put ' + target;
      if (!handlers.authorized(connection, netOut(connection, req))) {return;}
      logger.info('rcv: ' + req);

      mode = 'linePut';
      put = '';
      lineTarget = target;
      lineUser = user;
      lineId = id;
    });

    _server.on('build', function(connection, target, user, systemId, containerId){
      var req = 'build ' + target;
      if (!handlers.authorized(connection, netOut(connection, req))) {return;}
      logger.info('rcv: ' + req);
      handlers.build[target](netOut(connection, 'build ' + target), user, systemId, containerId);
    });

    _server.on('deploy', function(connection, target, user, systemId, revisionId){
      var req = 'deploy ' + target;
      if (!handlers.authorized(connection, netOut(connection, req))) {return;}
      logger.info('rcv: ' + req);
      handlers.deploy[target](netOut(connection, 'deploy ' + target), user, systemId, revisionId);
    });

    _server.on('quit', function(connection){
      logger.info('rcv: quit');
      handlers.quit(connection, netOut(connection, 'quit'));
    });

    _server.on('line', function(connection, line) {
      console.log('LINE: ' + line);

      if (mode === 'linePut') {
        if (line.indexOf('END') !== -1) {
          mode = 'command';
          handlers.put[lineTarget](netOut(connection, 'put ' + lineTarget), put, lineUser, lineId);
        }
        else {
          put += line;
        }
      }
      if (mode === 'lineAdd') {
        if (line.indexOf('END') !== -1) {
          mode = 'command';
          handlers.add[lineTarget](netOut(connection, 'add ' + lineTarget), put, lineUser, lineId);
        }
        else {
          put += line;
        }
      }
    });

    logger.info('starting protocol...');
    _server.start();
  };

  construct();
  return {
  };
};

<|endoftext|>


<commit_before>"use strict";

// TODO Externalize these to a conf file
var FRAMEWORKS = ['rtd-unit', 'mocha-web'],
    SOURCE_CODE_FILE_EXTENSIONS = ['js', 'coffee'],
    TESTS_DIR = '/tests';

var gaze = Npm.require('gaze'),
    _ = Npm.require('lodash'),
    SOURCE_CODE_FILE_EXTENSIONS_REGEX = '.(' + SOURCE_CODE_FILE_EXTENSIONS.join('|') + ')',
    GENERIC_TEST_REGEX = '-(' + FRAMEWORKS.join('|') + ')' + SOURCE_CODE_FILE_EXTENSIONS_REGEX,
    ABSOLUTE_TESTS_DIR = process.env.PWD + TESTS_DIR,
    FILE_WATCHER_MATCHING_PATTERN = _.map(SOURCE_CODE_FILE_EXTENSIONS, function (extension) {
        return '**/*.' + extension
    });

gaze(FILE_WATCHER_MATCHING_PATTERN, {cwd: ABSOLUTE_TESTS_DIR}, Meteor.bindEnvironment(function (err, watcher) {

    var extractFilename = function (file) {
            return file.substr(file.lastIndexOf('.') + 1);
        },
        extractPath = function (file) {
            var path = file.substr(ABSOLUTE_TESTS_DIR.length + 1);
            return path.substr(0, path.lastIndexOf('/') + 1);
        },
        addTestObject = function (filename, path) {
            if (filename.match(GENERIC_TEST_REGEX)) {
                MeteorTestRunnerTestFiles.insert({
                    _id: ABSOLUTE_TESTS_DIR + '/' + path + filename,
                    name: filename,
                    absolutePath: ABSOLUTE_TESTS_DIR + '/' + path + filename,
                    relativePath: path + filename,
                    path: path,
                    targetFramework: _.filter(FRAMEWORKS, function (framework) {
                        return filename.match('-' + framework + SOURCE_CODE_FILE_EXTENSIONS_REGEX);
                    })[0],
                    lastModified: Date.now()
                });
            }
        },
        addAllWatchedFiles = function () {
            watcher.relative(Meteor.bindEnvironment(function (err, files) {
                _.forIn(files, function (dirFiles, path) {
                    _.forEach(dirFiles, function (filename) {
                        addTestObject(filename, path);
                    })
                });
            }));
        },

        reset = function () {
            MeteorTestRunnerTestFiles.remove({});
            MeteorTestRunnerTestReports.remove({});
            MeteorTestRunnerAggregateReports.remove({});
            MeteorTestRunnerAggregateReports.insert({
                _id: 'result',
                name: 'Aggregate Result',
                result: 'pending'
            });
        },
        updateAggregateReports = function () {
            if (!MeteorTestRunnerTestReports.findOne({result: ''})) {
                MeteorTestRunnerAggregateReports.update('result', {$set: { result: MeteorTestRunnerTestReports.findOne({result: 'fail'}) ? 'fail' : 'pass'}});
            }
        };

    reset();
    addAllWatchedFiles();

    Meteor.methods({
        reset: function () {
            reset();
            addAllWatchedFiles();
        },
        postResult: function (options) {

            if (!options || !options.id || !options.name || !options.framework || !options.result) {
                throw new Error('id, name, framework and result are required fields.')
            }

            var result = {
                name: options.name,
                framework: options.framework,
                result: options.result
            };

            _.each([
                'timestamp',
                'time',
                'async',
                'timeOut',
                'pending',
                'errorMessage',
                'stackTrace',
                'ancestors'
            ], function (option) {
                if (options[option]) {
                    result[option] = options[option];
                }
            });

            var existingResult = MeteorTestRunnerTestReports.findOne(options.id);
            if (existingResult) {
                MeteorTestRunnerTestReports.update(options.id, {$set: result});
            } else {
                result._id = options.id;
                MeteorTestRunnerTestReports.insert(result);
            }

            updateAggregateReports();
        }
    });

    watcher.on('added', Meteor.bindEnvironment(function (file) {
        // For some reason, Gaze fires both added and changed events together when files change, but not always!
        // So here we check if we already have this test in the collection
        console.log('added', file);
        if (!MeteorTestRunnerTestFiles.findOne(file)) {
            console.log('added', file);
            addTestObject(extractFilename(file), extractPath(file));
        }
    }));

    watcher.on('changed', Meteor.bindEnvironment(function (file) {
        console.log('changed', file);
        MeteorTestRunnerTestFiles.update(file, { $set: {lastModified: Date.now()}});
    }));

    // FIXME This seems a little flaky in Gaze and doesn't always fire. May be linked to issue in added event
    watcher.on('deleted', Meteor.bindEnvironment(function (file) {
        console.log('deleted', file);
        MeteorTestRunnerTestFiles.remove(file);
    }));
}));
<commit_msg>fixed some path issues removed console logs added per-framework reset methods (with smarts) updates to reports also blank unsupplied fields<commit_after>"use strict";

console.log('METEOR TEST RUNNER STARTING')

// TODO Externalize these to a conf file
var FRAMEWORKS = ['rtd-unit', 'mocha-web'],
    SOURCE_CODE_FILE_EXTENSIONS = ['js', 'coffee'],
    TESTS_DIR = '/tests';

var gaze = Npm.require('gaze'),
    _ = Npm.require('lodash'),
    SOURCE_CODE_FILE_EXTENSIONS_REGEX = '.(' + SOURCE_CODE_FILE_EXTENSIONS.join('|') + ')',
    GENERIC_TEST_REGEX = '-(' + FRAMEWORKS.join('|') + ')' + SOURCE_CODE_FILE_EXTENSIONS_REGEX,
    ABSOLUTE_TESTS_DIR = process.env.PWD + TESTS_DIR,
    FILE_WATCHER_MATCHING_PATTERN = _.map(SOURCE_CODE_FILE_EXTENSIONS, function (extension) {
        return '**/*.' + extension
    });

gaze(FILE_WATCHER_MATCHING_PATTERN, {cwd: ABSOLUTE_TESTS_DIR}, Meteor.bindEnvironment(function (err, watcher) {

    var extractFilename = function (file) {
            return file.substr(file.lastIndexOf('/'), file.lastIndexOf('.') + 1);
        },
        extractPath = function (file) {
            var path = file.substr(ABSOLUTE_TESTS_DIR.length + 1);
            path = path.substr(0, path.lastIndexOf('/') + 1);
            if (path.indexOf('./') === 0) {
                path = path.substring(2);
            }
            return path;
        },
        addTestFile = function (filename, path) {
            if (filename.match(GENERIC_TEST_REGEX)) {
                var ret = MeteorTestRunnerTestFiles.insert({
                    _id: ABSOLUTE_TESTS_DIR + '/' + path + filename,
                    name: filename,
                    absolutePath: ABSOLUTE_TESTS_DIR + '/' + path + filename,
                    relativePath: path + filename,
                    path: path,
                    targetFramework: _.filter(FRAMEWORKS, function (framework) {
                        return filename.match('-' + framework + SOURCE_CODE_FILE_EXTENSIONS_REGEX);
                    })[0],
                    lastModified: Date.now()
                });
            }
        },
        addAllWatchedFiles = function () {
            watcher.relative(Meteor.bindEnvironment(function (err, files) {
                _.forIn(files, function (dirFiles, path) {
                    _.forEach(dirFiles, function (filename) {
                        if (path.indexOf('./') === 0) {
                            path = path.substring(2);
                        }
                        addTestFile(filename, path);
                    })
                });
            }));
        },

        reset = function () {
            MeteorTestRunnerTestFiles.remove({});
            MeteorTestRunnerTestReports.remove({});
            MeteorTestRunnerAggregateReports.remove({});
            MeteorTestRunnerAggregateReports.insert({
                _id: 'result',
                name: 'Aggregate Result',
                result: 'pending'
            });
        },
        updateAggregateReports = function () {
            if (!MeteorTestRunnerTestReports.findOne({result: ''})) {
                MeteorTestRunnerAggregateReports.update('result', {$set: { result: MeteorTestRunnerTestReports.findOne({result: 'fail'}) ? 'fail' : 'pass'}});
            }
        };

    Meteor.methods({
        reset: function () {
            reset();
            addAllWatchedFiles();
        },
        resetReports: function (options) {
            var query = {};
            if (options.framework) {
                query.framework = options.framework;
            }
            if (options.notIn) {
                query = _.assign(query, {_id: {$nin: options.notIn }});
            }
            MeteorTestRunnerTestReports.remove(query);
            updateAggregateReports();
        },
        postResult: function (options) {

            if (!options || !options.id || !options.name || !options.framework || !options.result) {
                throw new Error('id, name, framework and result are required fields.')
            }
            var result = {
                name: options.name,
                framework: options.framework,
                result: options.result
            };

            _.each([
                'timestamp',
                'time',
                'async',
                'timeOut',
                'pending',
                'failureType',
                'failureMessage',
                'failureStackTrace',
                'ancestors'
            ], function (option) {
                if (options[option]) {
                    result[option] = options[option];
                } else {
                    result[option] = '';
                }
            });

            var existingResult = MeteorTestRunnerTestReports.findOne(options.id);
            if (existingResult) {
                MeteorTestRunnerTestReports.update(options.id, {$set: result});
            } else {
                MeteorTestRunnerTestReports.insert(_.extend(result, {_id: options.id}));
            }
            updateAggregateReports();
        }
    });

    watcher.on('added', Meteor.bindEnvironment(function (file) {
        if (!MeteorTestRunnerTestFiles.findOne(file)) {
            addTestFile(extractFilename(file), extractPath(file));
        }
    }));

    watcher.on('changed', Meteor.bindEnvironment(function (file) {
        MeteorTestRunnerTestFiles.update(file, { $set: {lastModified: Date.now()}});
    }));

    watcher.on('deleted', Meteor.bindEnvironment(function (file) {
        MeteorTestRunnerTestFiles.remove(file);
    }));

    Meteor.call('reset');

}));
<|endoftext|>


<commit_before>/*
 * This Source Code is subject to the terms of the Mozilla Public License
 * version 2.0 (the "License"). You can obtain a copy of the License at
 * http://mozilla.org/MPL/2.0/.
 */

let {Services} = Cu.import("resource://gre/modules/Services.jsm");

let insertedElements = new WeakMap();

let observer = {
  get stringBundle()
  {
    // Randomize URI to work around bug 719376
    let uri = "chrome://enforceencryption/locale/pageinfo.properties?" + Math.random();
    let result = Services.strings.createBundle(uri);

    delete this.stringBundle;
    return (this.stringBundle = result);
  },

  get service()
  {
    let result = Cc["@mozilla.org/ssservice;1"].getService(Ci.nsISiteSecurityService);

    delete this.service;
    return (this.service = result);
  },

  applyToWindow: function(window)
  {
    let document = window.document;
    if (document.documentElement.getAttribute("windowtype") != "Browser:page-info")
      return;

    // Allow the page info window to handle the load event before continueing,
    // otherwise gWindow variable might be uninitialized.
    Services.tm.currentThread.dispatch(this._applyToWindow.bind(this, window, document),
        Ci.nsIEventTarget.DISPATCH_NORMAL);
  },

  _applyToWindow: function(window, document)
  {
    let content = window.gWindow;
    if (!content || content.location.protocol != "https:")
      return;

    let rows = document.getElementById("security-privacy-rows");
    if (!rows)
      return;

    function createElement(tagName, attributes, textContent)
    {
      let result = document.createElement(tagName);
      for (let key in attributes)
        result.setAttribute(key, attributes[key]);
      if (textContent)
        result.textContent = textContent;
      return result;
    }

    const idPrefix = "security-privacy-enforceencryption-";
    let row = createElement("row", {
      id: idPrefix + "row"
    });

    row.appendChild(createElement("label", {
      id: idPrefix + "label",
      class: "fieldLabel",
      control: idPrefix + "value"
    }, this.stringBundle.GetStringFromName("enforce.label")));

    let uri = Services.io.newURI(content.location.href, null, null);
    let isSecure = this.service.isSecureURI(this.service.HEADER_HSTS, uri, 0);
    let value = row.appendChild(createElement("checkbox", {
      id: idPrefix + "value",
      class: "fieldValue",
      checked: isSecure ? "true" : "false",
      label: this.stringBundle.GetStringFromName(isSecure ? "yes" : "no")
    }));
    value.addEventListener("command", this.toggleEncryption.bind(this, value, uri));

    rows.appendChild(row);
    insertedElements.set(window, row);
  },

  toggleEncryption: function(checkbox, uri)
  {
    if (checkbox.checked)
    {
      const secondsInYear = 365 * 24 * 60 * 60;
      const maxAge = 1000 * secondsInYear;   // sort of "forever"
      if ("unsafeProcessHeader" in this.service)
        // No need to check that the connection is trustworthy,
        // the header comes from the user interface.
        // http://dxr.mozilla.org/mozilla-central/source/security/manager/boot/src/nsSiteSecurityService.cpp#407
        this.service.unsafeProcessHeader(this.service.HEADER_HSTS, uri, "max-age=" + maxAge, 0);
      else
        this.service.processHeader(this.service.HEADER_HSTS, uri, "max-age=" + maxAge, 0);
    }
    else
      this.service.removeState(this.service.HEADER_HSTS, uri, 0);

    let isSecure = this.service.isSecureURI(this.service.HEADER_HSTS, uri, 0);
    checkbox.checked = isSecure;
    checkbox.label = this.stringBundle.GetStringFromName(isSecure ? "yes" : "no");
  },

  removeFromWindow: function(window)
  {
    let element = insertedElements.get(window);
    if (typeof element != "undefined" && element.parentNode)
      element.parentNode.removeChild(element);
  }
};

let {WindowObserver} = require("windowObserver");
new WindowObserver(observer, "load");
<commit_msg>Issue #5 - Use a more reliable way to determine opening window location<commit_after>/*
 * This Source Code is subject to the terms of the Mozilla Public License
 * version 2.0 (the "License"). You can obtain a copy of the License at
 * http://mozilla.org/MPL/2.0/.
 */

let {Services} = Cu.import("resource://gre/modules/Services.jsm");

let insertedElements = new WeakMap();

let observer = {
  get stringBundle()
  {
    // Randomize URI to work around bug 719376
    let uri = "chrome://enforceencryption/locale/pageinfo.properties?" + Math.random();
    let result = Services.strings.createBundle(uri);

    delete this.stringBundle;
    return (this.stringBundle = result);
  },

  get service()
  {
    let result = Cc["@mozilla.org/ssservice;1"].getService(Ci.nsISiteSecurityService);

    delete this.service;
    return (this.service = result);
  },

  applyToWindow: function(window)
  {
    let document = window.document;
    if (document.documentElement.getAttribute("windowtype") != "Browser:page-info")
      return;

    // Allow the page info window to handle the load event before continueing,
    // otherwise gWindow variable might be uninitialized.
    Services.tm.currentThread.dispatch(this._applyToWindow.bind(this, window, document),
        Ci.nsIEventTarget.DISPATCH_NORMAL);
  },

  _applyToWindow: function(window, document)
  {
    let uri = document.documentElement.getAttribute("relatedUrl");
    if (!uri)
    {
      Services.tm.currentThread.dispatch(this._applyToWindow.bind(this, window, document),
          Ci.nsIEventTarget.DISPATCH_NORMAL);
      return;
    }

    uri = Services.io.newURI(uri, null, null);
    if (!uri.schemeIs("https"))
      return;

    let rows = document.getElementById("security-privacy-rows");
    if (!rows)
      return;

    function createElement(tagName, attributes, textContent)
    {
      let result = document.createElement(tagName);
      for (let key in attributes)
        result.setAttribute(key, attributes[key]);
      if (textContent)
        result.textContent = textContent;
      return result;
    }

    const idPrefix = "security-privacy-enforceencryption-";
    let row = createElement("row", {
      id: idPrefix + "row"
    });

    row.appendChild(createElement("label", {
      id: idPrefix + "label",
      class: "fieldLabel",
      control: idPrefix + "value"
    }, this.stringBundle.GetStringFromName("enforce.label")));

    let isSecure = this.service.isSecureURI(this.service.HEADER_HSTS, uri, 0);
    let value = row.appendChild(createElement("checkbox", {
      id: idPrefix + "value",
      class: "fieldValue",
      checked: isSecure ? "true" : "false",
      label: this.stringBundle.GetStringFromName(isSecure ? "yes" : "no")
    }));
    value.addEventListener("command", this.toggleEncryption.bind(this, value, uri));

    rows.appendChild(row);
    insertedElements.set(window, row);
  },

  toggleEncryption: function(checkbox, uri)
  {
    if (checkbox.checked)
    {
      const secondsInYear = 365 * 24 * 60 * 60;
      const maxAge = 1000 * secondsInYear;   // sort of "forever"
      if ("unsafeProcessHeader" in this.service)
        // No need to check that the connection is trustworthy,
        // the header comes from the user interface.
        // http://dxr.mozilla.org/mozilla-central/source/security/manager/boot/src/nsSiteSecurityService.cpp#407
        this.service.unsafeProcessHeader(this.service.HEADER_HSTS, uri, "max-age=" + maxAge, 0);
      else
        this.service.processHeader(this.service.HEADER_HSTS, uri, "max-age=" + maxAge, 0);
    }
    else
      this.service.removeState(this.service.HEADER_HSTS, uri, 0);

    let isSecure = this.service.isSecureURI(this.service.HEADER_HSTS, uri, 0);
    checkbox.checked = isSecure;
    checkbox.label = this.stringBundle.GetStringFromName(isSecure ? "yes" : "no");
  },

  removeFromWindow: function(window)
  {
    let element = insertedElements.get(window);
    if (typeof element != "undefined" && element.parentNode)
      element.parentNode.removeChild(element);
  }
};

let {WindowObserver} = require("windowObserver");
new WindowObserver(observer, "load");
<|endoftext|>


<commit_before>/*!
 * Casper is a navigation utility for PhantomJS.
 *
 * Documentation: http://casperjs.org/
 * Repository:    http://github.com/casperjs/casperjs
 *
 * Copyright (c) 2011-2012 Nicolas Perriault
 *
 * Part of source code is Copyright Joyent, Inc. and other Node contributors.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*global CasperError, console, exports, phantom, patchRequire, require:true*/

var require = patchRequire(require);
var utils = require('utils');
var fs = require('fs');
var TestSuiteResult = require('tester').TestSuiteResult;

/**
 * Generates a value for 'classname' attribute of the JUnit XML report.
 *
 * Uses the (relative) file name of the current casper script without file
 * extension as classname.
 *
 * @param  String  classname
 * @return String
 */
function generateClassName(classname) {
    "use strict";
    classname = (classname || "").replace(phantom.casperPath, "").trim();
    var script = classname || phantom.casperScript || "";
    if (script.indexOf(fs.workingDirectory) === 0) {
        script = script.substring(fs.workingDirectory.length + 1);
    }
    if (script.indexOf('/') === 0) {
        script = script.substring(1, script.length);
    }
    if (~script.indexOf('.')) {
        script = script.substring(0, script.lastIndexOf('.'));
    }
    // If we have trimmed our string down to nothing, default to script name
    if (!script && phantom.casperScript) {
        script = phantom.casperScript;
    }
    return script || "unknown";
}

/**
 * Creates a XUnit instance
 *
 * @return XUnit
 */
exports.create = function create() {
    "use strict";
    return new XUnitExporter();
};

/**
 * JUnit XML (xUnit) exporter for test results.
 *
 */
function XUnitExporter() {
    "use strict";

    this.setupDocument();

    // Initialize state
    this.results = undefined;
    this.rendered = false;
}
exports.XUnitExporter = XUnitExporter;

/**
 * Retrieves generated XML object - actually an HTMLElement.
 *
 * @return HTMLElement
 */
XUnitExporter.prototype.getXML = function getXML() {
    "use strict";

    var self = this;

    if (!(this.results instanceof TestSuiteResult)) {
        throw new CasperError('Results not set, cannot get XML.');
    }
    this.results.forEach(function(result) {
        var suiteNode = utils.node('testsuite', {
            name: result.name,
            tests: result.assertions,
            failures: result.failed,
            errors: result.crashed,
            time: utils.ms2seconds(result.calculateDuration()),
            timestamp: (new Date()).toISOString(),
            'package': generateClassName(result.file)
        });
        // successful test cases
        result.passes.forEach(function(success) {
            var testCase = utils.node('testcase', {
                name: success.message || success.standard,
                classname: generateClassName(success.file),
                time: utils.ms2seconds(~~success.time)
            });
            suiteNode.appendChild(testCase);
        });
        // failed test cases
        result.failures.forEach(function(failure) {
            var testCase = utils.node('testcase', {
                name: failure.name || failure.message || failure.standard,
                classname: generateClassName(failure.file),
                time: utils.ms2seconds(~~failure.time)
            });
            var failureNode = utils.node('failure', {
                type: failure.type || "failure"
            });
            failureNode.appendChild(self._xmlDocument.createCDATASection(failure.message || "no message left"));
            if (failure.values && failure.values.error instanceof Error) {
                var errorNode = utils.node('error', {
                    type: utils.betterTypeOf(failure.values.error)
                });
                errorNode.appendChild(self._xmlDocument.createCDATASection(failure.values.error.stack));
                testCase.appendChild(errorNode);
            }
            testCase.appendChild(failureNode);
            suiteNode.appendChild(testCase);
        });
        // errors
        result.errors.forEach(function(error) {
            var errorNode = utils.node('error', {
                type: error.name
            });
            errorNode.appendChild(self._xmlDocument.createCDATASection(error.stack ? error.stack : error.message));
            suiteNode.appendChild(errorNode);
        });
        // warnings
        var warningNode = utils.node('system-out');
        warningNode.appendChild(self._xmlDocument.createCDATASection(result.warnings.join('\n')));
        suiteNode.appendChild(warningNode);
        this._xml.appendChild(suiteNode);
    }.bind(this));

    this._xml.setAttribute('time', utils.ms2seconds(this.results.calculateDuration()));

    this.rendered = true;

    return this._xmlDocument;
};

/**
 * Retrieves generated Xunit XML
 *
 * @return string
 */
XUnitExporter.prototype.getSerializedXML = function getSerializedXML() {
    "use strict";
    var serializer = new XMLSerializer(),
        document;

    if ( !this.rendered ) {
        document = this.getXML();
    }
    return '<?xml version="1.0" encoding="UTF-8"?>' + serializer.serializeToString(document);
};

/**
 * Sets test results.
 *
 * @param TestSuite  results
 */
XUnitExporter.prototype.setResults = function setResults(results) {
    "use strict";
    if (!(results instanceof TestSuiteResult)) {
        throw new CasperError('Invalid results type.');
    }
    this.results = results;

    // New results let's re-initialize
    this.setupDocument();
    this.rendered = false;

    return results;
};

/**
 * Initializes the XML to an empty document
 *
 * @return void
 */
XUnitExporter.prototype.setupDocument = function() {
    // Since we are outputting XML, let's do our own document type
    var documentType = document.implementation.createDocumentType("casperjs", "-//CasperJS//XUnit Test Results", "testsuites");

    this._xmlDocument = document.implementation.createDocument("", "", documentType);
    this._xml = this._xmlDocument.appendChild(this._xmlDocument.createElement("testsuites"));
};
<commit_msg>fix #1528, remove DOCTYPE from xml output<commit_after>/*!
 * Casper is a navigation utility for PhantomJS.
 *
 * Documentation: http://casperjs.org/
 * Repository:    http://github.com/casperjs/casperjs
 *
 * Copyright (c) 2011-2012 Nicolas Perriault
 *
 * Part of source code is Copyright Joyent, Inc. and other Node contributors.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*global CasperError, console, exports, phantom, patchRequire, require:true*/

var require = patchRequire(require);
var utils = require('utils');
var fs = require('fs');
var TestSuiteResult = require('tester').TestSuiteResult;

/**
 * Generates a value for 'classname' attribute of the JUnit XML report.
 *
 * Uses the (relative) file name of the current casper script without file
 * extension as classname.
 *
 * @param  String  classname
 * @return String
 */
function generateClassName(classname) {
    "use strict";
    classname = (classname || "").replace(phantom.casperPath, "").trim();
    var script = classname || phantom.casperScript || "";
    if (script.indexOf(fs.workingDirectory) === 0) {
        script = script.substring(fs.workingDirectory.length + 1);
    }
    if (script.indexOf('/') === 0) {
        script = script.substring(1, script.length);
    }
    if (~script.indexOf('.')) {
        script = script.substring(0, script.lastIndexOf('.'));
    }
    // If we have trimmed our string down to nothing, default to script name
    if (!script && phantom.casperScript) {
        script = phantom.casperScript;
    }
    return script || "unknown";
}

/**
 * Creates a XUnit instance
 *
 * @return XUnit
 */
exports.create = function create() {
    "use strict";
    return new XUnitExporter();
};

/**
 * JUnit XML (xUnit) exporter for test results.
 *
 */
function XUnitExporter() {
    "use strict";

    this.setupDocument();

    // Initialize state
    this.results = undefined;
    this.rendered = false;
}
exports.XUnitExporter = XUnitExporter;

/**
 * Retrieves generated XML object - actually an HTMLElement.
 *
 * @return HTMLElement
 */
XUnitExporter.prototype.getXML = function getXML() {
    "use strict";

    var self = this;

    if (!(this.results instanceof TestSuiteResult)) {
        throw new CasperError('Results not set, cannot get XML.');
    }
    this.results.forEach(function(result) {
        var suiteNode = utils.node('testsuite', {
            name: result.name,
            tests: result.assertions,
            failures: result.failed,
            errors: result.crashed,
            time: utils.ms2seconds(result.calculateDuration()),
            timestamp: (new Date()).toISOString(),
            'package': generateClassName(result.file)
        });
        // successful test cases
        result.passes.forEach(function(success) {
            var testCase = utils.node('testcase', {
                name: success.message || success.standard,
                classname: generateClassName(success.file),
                time: utils.ms2seconds(~~success.time)
            });
            suiteNode.appendChild(testCase);
        });
        // failed test cases
        result.failures.forEach(function(failure) {
            var testCase = utils.node('testcase', {
                name: failure.name || failure.message || failure.standard,
                classname: generateClassName(failure.file),
                time: utils.ms2seconds(~~failure.time)
            });
            var failureNode = utils.node('failure', {
                type: failure.type || "failure"
            });
            failureNode.appendChild(self._xmlDocument.createCDATASection(failure.message || "no message left"));
            if (failure.values && failure.values.error instanceof Error) {
                var errorNode = utils.node('error', {
                    type: utils.betterTypeOf(failure.values.error)
                });
                errorNode.appendChild(self._xmlDocument.createCDATASection(failure.values.error.stack));
                testCase.appendChild(errorNode);
            }
            testCase.appendChild(failureNode);
            suiteNode.appendChild(testCase);
        });
        // errors
        result.errors.forEach(function(error) {
            var errorNode = utils.node('error', {
                type: error.name
            });
            errorNode.appendChild(self._xmlDocument.createCDATASection(error.stack ? error.stack : error.message));
            suiteNode.appendChild(errorNode);
        });
        // warnings
        var warningNode = utils.node('system-out');
        warningNode.appendChild(self._xmlDocument.createCDATASection(result.warnings.join('\n')));
        suiteNode.appendChild(warningNode);
        this._xml.appendChild(suiteNode);
    }.bind(this));

    this._xml.setAttribute('time', utils.ms2seconds(this.results.calculateDuration()));

    this.rendered = true;

    return this._xmlDocument;
};

/**
 * Retrieves generated Xunit XML
 *
 * @return string
 */
XUnitExporter.prototype.getSerializedXML = function getSerializedXML() {
    "use strict";
    var serializer = new XMLSerializer(),
        document;

    if ( !this.rendered ) {
        document = this.getXML();
    }
    return '<?xml version="1.0" encoding="UTF-8"?>' + serializer.serializeToString(document);
};

/**
 * Sets test results.
 *
 * @param TestSuite  results
 */
XUnitExporter.prototype.setResults = function setResults(results) {
    "use strict";
    if (!(results instanceof TestSuiteResult)) {
        throw new CasperError('Invalid results type.');
    }
    this.results = results;

    // New results let's re-initialize
    this.setupDocument();
    this.rendered = false;

    return results;
};

/**
 * Initializes the XML to an empty document
 *
 * @return void
 */
XUnitExporter.prototype.setupDocument = function() {
    // Note that we do NOT use a documentType here, because validating
    // parsers try to fetch the (non-existing) DTD and fail #1528
    this._xmlDocument = document.implementation.createDocument("", "");
    this._xml = this._xmlDocument.appendChild(this._xmlDocument.createElement("testsuites"));
};
<|endoftext|>