/* 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";

const { interfaces: Ci, utils: Cu } = Components;

Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Promise.jsm");

XPCOMUtils.defineLazyServiceGetter(this, "appsService",
                                   "@mozilla.org/AppsService;1",
                                   "nsIAppsService");

XPCOMUtils.defineLazyServiceGetter(this, "aceService",
                                   "@mozilla.org/secureelement/access-control/ace;1",
                                   "nsIAccessControlEnforcer");

XPCOMUtils.defineLazyModuleGetter(this, "SEUtils",
                                  "resource://gre/modules/SEUtils.jsm");

XPCOMUtils.defineLazyGetter(this, "SE", () => {
  let obj = {};
  Cu.import("resource://gre/modules/se_consts.js", obj);
  return obj;
});

var DEBUG = SE.DEBUG_SE;
function debug(aMsg) {
  if (DEBUG) {
    dump("-*- HCIEventTransaction: " + aMsg);
  }
}

/**
  * nsISystemMessagesConfigurator implementation.
  */
function HCIEventTransactionSystemMessageConfigurator() {
  debug("HCIEventTransactionSystemMessageConfigurator");
}

HCIEventTransactionSystemMessageConfigurator.prototype = {
  get mustShowRunningApp() {
    debug("mustShowRunningApp returning true");
    return true;
  },

  shouldDispatch: function shouldDispatch(aManifestURL, aPageURL, aType, aMessage, aExtra) {
    DEBUG && debug("message to dispatch: " + JSON.stringify(aMessage));
    debug("aManifest url: " + aManifestURL);

    if (!aMessage) {
      return Promise.resolve(false);
    }

    let appId = appsService.getAppLocalIdByManifestURL(aManifestURL);
    if (appId === Ci.nsIScriptSecurityManager.NO_APP_ID) {
      return Promise.resolve(false);
    }

    return new Promise((resolve, reject) => {
      appsService.getManifestFor(aManifestURL)
      .then((aManifest) => this._checkAppManifest(aMessage.origin, aMessage.aid, aManifest))
      .then(() => aceService.isAccessAllowed(appId, aMessage.origin, aMessage.aid))
      .then((allowed) => {
        debug("dispatching message: " + allowed);
        resolve(allowed);
      })
      .catch(() => {
        debug("not dispatching");
        resolve(false);
      });
    });
  },

  _checkAppManifest: function _checkAppManifest(aOrigin, aAid, aManifest) {
    DEBUG && debug("aManifest " + JSON.stringify(aManifest));

    // convert AID and Secure Element name to uppercased string for comparison
    // with manifest secure_element_access rules
    let aid = SEUtils.byteArrayToHexString(aAid);
    let seName = (aOrigin) ? aOrigin.toUpperCase() : "";

    let hciRules = aManifest["secure_element_access"] || [];
    let matchingRule = hciRules.find((rule) => {
      rule = rule.toUpperCase();
      if(rule === "*" || rule === (seName + "/" + aid)) {
        return true;
      }

      let isMatching = (match, element) => {
        if(match === "*") {
          return true;
        }
        if(match.charAt(match.length - 1) === '*') {
          return element.indexOf(match.substr(0,match.length - 1)) === 0;
        }

        return match === element;
      };

      return isMatching(rule.split('/')[0], seName) &&
             isMatching(rule.split('/')[1], aid);
    });

    return (matchingRule) ? Promise.resolve() : Promise.reject();
  },

  classID: Components.ID("{b501edd0-28bd-11e4-8c21-0800200c9a66}"),
  QueryInterface: XPCOMUtils.generateQI([Ci.nsISystemMessagesConfigurator])
}

this.NSGetFactory = XPCOMUtils.generateNSGetFactory([HCIEventTransactionSystemMessageConfigurator]);
