const Lang = imports.lang; const Mainloop = imports.mainloop; const Main = imports.ui.main; const Meta = imports.gi.Meta; const Shell = imports.gi.Shell; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Extension = imports.misc.extensionUtils.getCurrentExtension(); const Convenience = Extension.imports.convenience; const Dialog = Extension.imports.dialog; const Menu = Extension.imports.menu; //const ConnectionMonitor = Extension.imports.cmonitor; function init() { return new FirewallSupport(); } const FirewallSupport = new Lang.Class({ Name: 'FirewallSupport', _init: function() { this.menu = new Menu.FirewallMenu(); //this.cmon = new ConnectionMonitor.ConnectionMonitor(); this.handler = null; }, _destroyHandler: function() { if (this.handler) { this.handler.destroy(); this.handler = null; } }, enable: function() { this._destroyHandler(); this.handler = new FirewallPromptHandler(); //this.cmon.install(); this.menu.install(); }, disable: function() { this.menu.destroy(); //this.cmon.remove(); this._destroyHandler(); } }); const FirewallPromptInterface = ' \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ '; const FirewallPromptHandler = new Lang.Class({ Name: 'FirewallPromptHandler', _init: function() { this._settings = Convenience.getSettings(); this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(FirewallPromptInterface, this); this._dbusImpl.export(Gio.DBus.system, '/com/subgraph/FirewallPrompt'); Gio.bus_own_name_on_connection(Gio.DBus.system, 'com.subgraph.FirewallPrompt', Gio.BusNameOwnerFlags.REPLACE, null, null); this._dialogs = new Object(); this._dialog = null; this._guids = new Array(); this._current_guid = null; this._timeoutId = null; this._initKeybindings(); this.RequestPendingPrompts(); }, destroy: function() { log("SGFW: Exited"); this._closeDialogs(); this._dbusImpl.unexport(); this._destroyKeybindings(); if (this._timeoutId !== null) { Mainloop.source_remove(this._timeoutId); this._timeoutId = null; } }, _initKeybindings: function() { this._keyBindings = new Array( "prompt-scope-previous" , "prompt-scope-next" , "prompt-rule-next" , "prompt-rule-previous" , "prompt-rule-allow" , "prompt-rule-deny" , "prompt-toggle-details" , "prompt-toggle-tlsguard" ); for (var i = 0 , ii = this._keyBindings.length; i < ii; i++) { Main.wm.addKeybinding(this._keyBindings[i], this._settings, Meta.KeyBindingFlags.NONE, Shell.ActionMode.ALL, Lang.bind(this, this._handleKeybinding, this._keyBindings[i])); } }, _handleKeybinding: function(a, b, c, d, binding) { if (this._dialog === null) { return false; } let fname = binding.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); }); let fname = "_on"+ fname[0].toUpperCase() + fname.substr(1); if (!( fname in this._dialog )) { log("SGFW: Invalid key binding (1)... " + fname); return true; } let fn = this._dialog[fname]; if (typeof fn !== "function") { log("SGFW: Invalid key binding (2)... " + fname + " " + (typeof fn)); return true; } Lang.bind(this._dialog, fn)(); return true; }, _destroyKeybindings: function() { for (var i = 0 , ii = this._keyBindings.length; i < ii; i++) { Main.wm.removeKeybinding(this._keyBindings[i]); } }, _closeDialogs: function() { log("SGFW: Closing all dialogs"); if (this._dialog !== null) { this._dialog.close(); this._dialog = null; } this._dialogs = new Object(); this._guids = new Array(); this._current_guid = null; }, RequestPendingPrompts: function() { try { let params = GLib.Variant.new("(s)", ["*"]); let result = Gio.DBus.system.call_sync("com.subgraph.Firewall", "/com/subgraph/Firewall", "com.subgraph.Firewall", "GetPendingRequests", params, null, Gio.DBusCallFlags.NONE, 500, null); log("SGFW: Get Pending Requests: " + result.deep_unpack()); } catch (err if err.matches(Gio.DBusError, Gio.DBusError.SERVICE_UNKNOWN)) { return; } catch (err) { log("SGFW: Fatal Error Requesting Pending Prompts: "+ err); } }, RequestPromptAsyncAsync: function(params) { try { if (this._dialog == null) { this._dialog = true; let guid = params.shift(); this._dialogs[guid] = params; this._guids.push(guid); this._createDialog(guid); } else { let guid = params.shift(); this._dialogs[guid] = params; this._guids.push(guid); this._updateDialogRemainingPrompts(); } log("SGFW: Async Prompt Requested " + params); } catch (err) { log("SGFW: Error on async prompt request: " + err); } }, RemovePromptAsync: function(params, invocation) { let [guid] = params; log("SGFW: Async Prompt Remove " + guid + " " + (guid in this._dialogs)); try { if (guid == this._current_guid) { this._dialog = null; this._current_guid = null; } if (guid in this._dialogs) { delete this._dialogs[guid]; for (let ii = (this._guids.length - 1); ii >= 0; ii--) { if (this._guids[ii] === guid) { this._guids.splice(ii,1); break; } } invocation.return_value(GLib.Variant.new('(b)', [true])); } else { invocation.return_value(GLib.Variant.new('(b)', [false])); } if (this._dialog !== null) { this._updateDialogRemainingPrompts(); } } catch (err) { log("SGFW: Error on async prompt remove: " + err); } try { if (this._timeoutId == null) { log("SGFW: Waiting to check for next dialog..."); this._timeoutId = Mainloop.timeout_add_seconds(1, Lang.bind(this, this._createNextDialogCallback)); } else { log("SGFW: Already waiting for next dialog..."); } } catch (err) { log("SGFW: Error while setting up next event display timeout: " + err); } }, AddRuleCallback: function(guid, timestamp, rule, scope) { log("SGFW: Adding rule for " + guid + " " + timestamp + ": " + rule + " (" + scope + ")"); try { let params = GLib.Variant.new("(usss)", [scope, rule, "*", guid]); let result = Gio.DBus.system.call_sync("com.subgraph.Firewall", "/com/subgraph/Firewall", "com.subgraph.Firewall", "AddRuleAsync", params, null, Gio.DBusCallFlags.NONE, 500, null); log("SGFW: Add Rule Async: " + result.deep_unpack()); // XXXX: If false prompt user about failure } catch (err) { log("SGFW: Fatal Error: " + err); } }, _createDialog: function(guid) { log("SGFW: Creating new prompt for: " + this._dialogs[guid]); try { let [app, icon, path, address, port, ip, origin, proto, uid, gid, user, group, pid, sandbox, tlsguard, timestamp, optstring, expanded, expert, action] = this._dialogs[guid]; this._current_guid = guid; this._dialog = new Dialog.PromptDialog(guid, timestamp, (pid >= 0), (sandbox != ""), tlsguard, Lang.bind(this, this.AddRuleCallback)); this._dialog.update(app, icon, path, address, port, ip, origin, uid, gid, user, group, pid, proto, tlsguard, optstring, sandbox, expanded, expert, action); this._updateDialogRemainingPrompts(); this._dialog.activate(); } catch(err) { log("SGFW: Error while creating dialog: " + err); } }, _createNextDialogCallback: function() { log("SGFW: Checking for next dialog..."); try { if (this._guids.length > 0 && this._current_guid === null) { log("SGFW: Opening next dialog: " + this._guids[0] + " (remaining: " + this._guids.length + ")"); this._createDialog(this._guids[0]); } Mainloop.source_remove(this._timeoutId); this._timeoutId = null; } catch (err) { log("SGFW: Error on creating next dialog callback: " + err); } }, _updateDialogRemainingPrompts: function() { if (this._dialog === null) { return; } try { let remaining = (this._guids.length - 1); if (remaining > 0) { this._dialog.updateRemainingPrompts(remaining); } } catch(err) { log("SGFW: Error while updating remaining dialogs count: " + err); } return; } /* TestPrompt: function(params, invocation) { log("SGFW: Test Prompt Requested"); this.RequestPromptAsync(["Firefox", "firefox", "/usr/bin/firefox-esr", "242.12.111.18", "443", "linux", "2342", "TCP", true, true], nil); } */ });