michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: const Ci = Components.interfaces; michael@0: const Cu = Components.utils; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.import("resource://gre/modules/AppsUtils.jsm"); michael@0: Cu.import("resource://gre/modules/PermissionsInstaller.jsm"); michael@0: Cu.import("resource://gre/modules/PermissionsTable.jsm"); michael@0: Cu.import("resource://gre/modules/PermissionSettings.jsm"); michael@0: michael@0: this.EXPORTED_SYMBOLS = ["SystemMessagePermissionsChecker", michael@0: "SystemMessagePermissionsTable"]; michael@0: michael@0: function debug(aStr) { michael@0: // dump("SystemMessagePermissionsChecker.jsm: " + aStr + "\n"); michael@0: } michael@0: michael@0: // This table maps system message to permission(s), indicating only michael@0: // the system messages granted by the page's permissions are allowed michael@0: // to be registered or sent to that page. Note the empty permission michael@0: // set means this type of system message is always permitted. michael@0: michael@0: this.SystemMessagePermissionsTable = { michael@0: "activity": { }, michael@0: "alarm": { michael@0: "alarms": [] michael@0: }, michael@0: "bluetooth-dialer-command": { michael@0: "telephony": [] michael@0: }, michael@0: "bluetooth-cancel": { michael@0: "bluetooth": [] michael@0: }, michael@0: "bluetooth-hid-status-changed": { michael@0: "bluetooth": [] michael@0: }, michael@0: "bluetooth-pairing-request": { michael@0: "bluetooth": [] michael@0: }, michael@0: "bluetooth-opp-transfer-complete": { michael@0: "bluetooth": [] michael@0: }, michael@0: "bluetooth-opp-update-progress": { michael@0: "bluetooth": [] michael@0: }, michael@0: "bluetooth-opp-receiving-file-confirmation": { michael@0: "bluetooth": [] michael@0: }, michael@0: "bluetooth-opp-transfer-start": { michael@0: "bluetooth": [] michael@0: }, michael@0: "connection": { }, michael@0: "dummy-system-message": { }, // for system message testing framework michael@0: "headset-button": { }, michael@0: "icc-stkcommand": { michael@0: "settings": ["read", "write"] michael@0: }, michael@0: "media-button": { }, michael@0: "networkstats-alarm": { michael@0: "networkstats-manage": [] michael@0: }, michael@0: "notification": { michael@0: "desktop-notification": [] michael@0: }, michael@0: "push": { michael@0: "push": [] michael@0: }, michael@0: "push-register": { michael@0: "push": [] michael@0: }, michael@0: "sms-delivery-success": { michael@0: "sms": [] michael@0: }, michael@0: "sms-read-success": { michael@0: "sms": [] michael@0: }, michael@0: "sms-received": { michael@0: "sms": [] michael@0: }, michael@0: "sms-sent": { michael@0: "sms": [] michael@0: }, michael@0: "telephony-new-call": { michael@0: "telephony": [] michael@0: }, michael@0: "telephony-call-ended": { michael@0: "telephony": [] michael@0: }, michael@0: "ussd-received": { michael@0: "mobileconnection": [] michael@0: }, michael@0: "wappush-received": { michael@0: "wappush": [] michael@0: }, michael@0: "cdma-info-rec-received": { michael@0: "mobileconnection": [] michael@0: }, michael@0: "nfc-manager-tech-discovered": { michael@0: "nfc-manager": [] michael@0: }, michael@0: "nfc-manager-tech-lost": { michael@0: "nfc-manager": [] michael@0: }, michael@0: "nfc-manager-send-file": { michael@0: "nfc-manager": [] michael@0: }, michael@0: "nfc-powerlevel-change": { michael@0: "settings": ["read", "write"] michael@0: }, michael@0: "wifip2p-pairing-request": { }, michael@0: "first-run-with-sim": { michael@0: "settings": ["read", "write"] michael@0: } michael@0: }; michael@0: michael@0: this.SystemMessagePermissionsChecker = { michael@0: /** michael@0: * Return all the needed permission names for the given system message. michael@0: * @param string aSysMsgName michael@0: * The system messsage name. michael@0: * @returns object michael@0: * Format: { permName (string): permNamesWithAccess (string array), ... } michael@0: * Ex, { "settings": ["settings-read", "settings-write"], ... }. michael@0: * Note: an empty object will be returned if it's always permitted. michael@0: * @returns null michael@0: * Return and report error when any unexpected error is ecountered. michael@0: * Ex, when the system message we want to search is not included. michael@0: **/ michael@0: getSystemMessagePermissions: function getSystemMessagePermissions(aSysMsgName) { michael@0: debug("getSystemMessagePermissions(): aSysMsgName: " + aSysMsgName); michael@0: michael@0: let permNames = SystemMessagePermissionsTable[aSysMsgName]; michael@0: if (permNames === undefined) { michael@0: debug("'" + aSysMsgName + "' is not associated with permissions. " + michael@0: "Please add them to the SystemMessagePermissionsTable."); michael@0: return null; michael@0: } michael@0: michael@0: let object = { }; michael@0: for (let permName in permNames) { michael@0: if (PermissionsTable[permName] === undefined) { michael@0: debug("'" + permName + "' for '" + aSysMsgName + "' is invalid. " + michael@0: "Please correct it in the SystemMessagePermissionsTable."); michael@0: return null; michael@0: } michael@0: michael@0: // Construct a new permission name array by adding the access suffixes. michael@0: let access = permNames[permName]; michael@0: if (!access || !Array.isArray(access)) { michael@0: debug("'" + permName + "' is not associated with access array. " + michael@0: "Please correct it in the SystemMessagePermissionsTable."); michael@0: return null; michael@0: } michael@0: object[permName] = appendAccessToPermName(permName, access); michael@0: } michael@0: return object michael@0: }, michael@0: michael@0: /** michael@0: * Check if the system message is permitted to be registered for the given michael@0: * app at start-up based on the permissions claimed in the app's manifest. michael@0: * @param string aSysMsgName michael@0: * The system messsage name. michael@0: * @param string aOrigin michael@0: * The app's origin. michael@0: * @param object aManifest michael@0: * The app's manifest. michael@0: * @returns bool michael@0: * Is permitted or not. michael@0: **/ michael@0: isSystemMessagePermittedToRegister: michael@0: function isSystemMessagePermittedToRegister(aSysMsgName, aOrigin, aManifest) { michael@0: debug("isSystemMessagePermittedToRegister(): " + michael@0: "aSysMsgName: " + aSysMsgName + ", " + michael@0: "aOrigin: " + aOrigin + ", " + michael@0: "aManifest: " + JSON.stringify(aManifest)); michael@0: michael@0: let permNames = this.getSystemMessagePermissions(aSysMsgName); michael@0: if (permNames === null) { michael@0: return false; michael@0: } michael@0: michael@0: // Check to see if the 'webapp' is app/privileged/certified. michael@0: let appStatus; michael@0: switch (AppsUtils.getAppManifestStatus(aManifest)) { michael@0: case Ci.nsIPrincipal.APP_STATUS_CERTIFIED: michael@0: appStatus = "certified"; michael@0: break; michael@0: case Ci.nsIPrincipal.APP_STATUS_PRIVILEGED: michael@0: appStatus = "privileged"; michael@0: break; michael@0: case Ci.nsIPrincipal.APP_STATUS_INSTALLED: michael@0: appStatus = "app"; michael@0: break; michael@0: default: michael@0: throw new Error("SystemMessagePermissionsChecker.jsm: " + michael@0: "Cannot decide the app's status. Install cancelled."); michael@0: break; michael@0: } michael@0: michael@0: let newManifest = new ManifestHelper(aManifest, aOrigin); michael@0: michael@0: for (let permName in permNames) { michael@0: // The app doesn't claim valid permissions for this sytem message. michael@0: if (!newManifest.permissions || !newManifest.permissions[permName]) { michael@0: debug("'" + aSysMsgName + "' isn't permitted by '" + permName + "'. " + michael@0: "Please add the permission for app: '" + aOrigin + "'."); michael@0: return false; michael@0: } michael@0: let permValue = PermissionsTable[permName][appStatus]; michael@0: if (permValue != Ci.nsIPermissionManager.PROMPT_ACTION && michael@0: permValue != Ci.nsIPermissionManager.ALLOW_ACTION) { michael@0: debug("'" + aSysMsgName + "' isn't permitted by '" + permName + "'. " + michael@0: "Please add the permission for app: '" + aOrigin + "'."); michael@0: return false; michael@0: } michael@0: michael@0: // Compare the expanded permission names between the ones in michael@0: // app's manifest and the ones needed for system message. michael@0: let expandedPermNames = michael@0: expandPermissions(permName, michael@0: newManifest.permissions[permName].access); michael@0: michael@0: let permNamesWithAccess = permNames[permName]; michael@0: michael@0: // Early return false as soon as any permission is not matched. michael@0: for (let idx in permNamesWithAccess) { michael@0: let index = expandedPermNames.indexOf(permNamesWithAccess[idx]); michael@0: if (index == -1) { michael@0: debug("'" + aSysMsgName + "' isn't permitted by '" + permName + "'. " + michael@0: "Please add the permission for app: '" + aOrigin + "'."); michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // All the permissions needed for this system message are matched. michael@0: return true; michael@0: }, michael@0: michael@0: /** michael@0: * Check if the system message is permitted to be sent to the given michael@0: * app's page at run-time based on the current app's permissions. michael@0: * @param string aSysMsgName michael@0: * The system messsage name. michael@0: * @param string aPageURL michael@0: * The app's page URL. michael@0: * @param string aManifestURL michael@0: * The app's manifest URL. michael@0: * @returns bool michael@0: * Is permitted or not. michael@0: **/ michael@0: isSystemMessagePermittedToSend: michael@0: function isSystemMessagePermittedToSend(aSysMsgName, aPageURL, aManifestURL) { michael@0: debug("isSystemMessagePermittedToSend(): " + michael@0: "aSysMsgName: " + aSysMsgName + ", " + michael@0: "aPageURL: " + aPageURL + ", " + michael@0: "aManifestURL: " + aManifestURL); michael@0: michael@0: let permNames = this.getSystemMessagePermissions(aSysMsgName); michael@0: if (permNames === null) { michael@0: return false; michael@0: } michael@0: michael@0: let pageURI = Services.io.newURI(aPageURL, null, null); michael@0: for (let permName in permNames) { michael@0: let permNamesWithAccess = permNames[permName]; michael@0: michael@0: // Early return false as soon as any permission is not matched. michael@0: for (let idx in permNamesWithAccess) { michael@0: if(PermissionSettingsModule.getPermission(permNamesWithAccess[idx], michael@0: aManifestURL, michael@0: pageURI.prePath, michael@0: false) != "allow") { michael@0: debug("'" + aSysMsgName + "' isn't permitted by '" + permName + "'. " + michael@0: "Please add the permission for app: '" + pageURI.prePath + "'."); michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // All the permissions needed for this system message are matched. michael@0: return true; michael@0: } michael@0: };