├── .eslintrc.yml ├── .github └── workflows │ └── CI.yml ├── .gitignore ├── CHANGELOG.md ├── COPYING ├── DOCUMENTATION.md ├── README.md ├── com.github.tchx84.Flatseal.json ├── data ├── com.github.tchx84.Flatseal.appdata.xml.in ├── com.github.tchx84.Flatseal.desktop.in ├── com.github.tchx84.Flatseal.gschema.xml ├── icons │ ├── com.github.tchx84.Flatseal-symbolic.svg │ ├── com.github.tchx84.Flatseal.Devel.svg │ ├── com.github.tchx84.Flatseal.Flatpak.svg │ ├── com.github.tchx84.Flatseal.Source.svg │ ├── com.github.tchx84.Flatseal.svg │ └── meson.build └── meson.build ├── help ├── C │ └── index.html └── meson.build ├── meson.build ├── po ├── LINGUAS ├── POTFILES ├── ar.po ├── bg_BG.po ├── ca.po ├── cs.po ├── da.po ├── de.po ├── el.po ├── es.po ├── fa.po ├── flatseal.pot ├── fr.po ├── he.po ├── hi.po ├── hu.po ├── id.po ├── it.po ├── meson.build ├── nb.po ├── nl.po ├── pl.po ├── pt_BR.po ├── ru.po ├── sv.po ├── ta.po ├── tr.po ├── uk.po └── zh_CN.po ├── screenshots └── en │ ├── 1.png │ ├── 2.png │ └── 3.png ├── src ├── application.js ├── com.github.tchx84.Flatseal.data.gresource.xml ├── com.github.tchx84.Flatseal.in ├── com.github.tchx84.Flatseal.src.gresource.xml ├── main.js ├── meson.build ├── models │ ├── applications.js │ ├── devices.js │ ├── features.js │ ├── filesystems.js │ ├── filesystemsOther.js │ ├── globalModel.js │ ├── info.js │ ├── overrideStatus.js │ ├── permissions.js │ ├── persistent.js │ ├── portals.js │ ├── sessionBus.js │ ├── settings.js │ ├── shared.js │ ├── sockets.js │ ├── systemBus.js │ ├── unsupported.js │ └── variables.js ├── style.css └── widgets │ ├── aboutDialog.js │ ├── aboutDialog.ui │ ├── appInfoViewer.js │ ├── appInfoViewer.ui │ ├── applicationRow.js │ ├── applicationRow.ui │ ├── busNameRow.js │ ├── busNameRow.ui │ ├── detailsButton.js │ ├── docsViewer.js │ ├── docsViewer.ui │ ├── globalInfoViewer.js │ ├── globalInfoViewer.ui │ ├── globalRow.js │ ├── globalRow.ui │ ├── menu.ui │ ├── overrideStatusIcon.js │ ├── overrideStatusIcon.ui │ ├── pathRow.js │ ├── pathRow.ui │ ├── pathsViewer.js │ ├── pathsViewer.ui │ ├── permissionEntryRow.js │ ├── permissionEntryRow.ui │ ├── permissionPortalRow.js │ ├── permissionPortalRow.ui │ ├── permissionSwitchRow.js │ ├── permissionSwitchRow.ui │ ├── relativePathRow.js │ ├── relativePathRow.ui │ ├── resetButton.js │ ├── shortcutsWindow.js │ ├── shortcutsWindow.ui │ ├── variableRow.js │ ├── variableRow.ui │ ├── window.js │ └── window.ui └── tests ├── content ├── .flatpak-info ├── .flatpak-info.new ├── .flatpak-info.old ├── custom │ └── app │ │ └── com.test.Extra │ │ └── current │ │ └── active │ │ └── metadata ├── extra │ └── app │ │ └── com.test.Extra │ │ └── current │ │ └── active │ │ └── metadata ├── global │ └── flatpak │ │ └── overrides │ │ ├── com.test.GlobalRestored │ │ └── global ├── globalNegated │ └── flatpak │ │ └── overrides │ │ └── global ├── globalResetMode │ └── flatpak │ │ └── overrides │ │ └── global ├── installations.d │ └── extra.conf ├── statuses │ └── flatpak │ │ └── overrides │ │ ├── com.test.Statuses │ │ └── global ├── system │ └── flatpak │ │ └── app │ │ ├── com.test.BaseApp │ │ └── current │ │ │ └── active │ │ │ └── metadata │ │ ├── com.test.Basic │ │ └── current │ │ │ └── active │ │ │ └── metadata │ │ ├── com.test.BasicNegated │ │ └── current │ │ │ └── active │ │ │ └── metadata │ │ ├── com.test.Bus │ │ └── current │ │ │ └── active │ │ │ └── metadata │ │ ├── com.test.Environment │ │ └── current │ │ │ └── active │ │ │ └── metadata │ │ ├── com.test.FilesystemWithMode │ │ └── current │ │ │ └── active │ │ │ └── metadata │ │ ├── com.test.Global │ │ └── current │ │ │ └── active │ │ │ └── metadata │ │ ├── com.test.GlobalRestored │ │ └── current │ │ │ └── active │ │ │ └── metadata │ │ ├── com.test.Increase │ │ └── current │ │ │ └── active │ │ │ └── metadata │ │ ├── com.test.Malformed │ │ └── current │ │ │ └── active │ │ │ └── metadata │ │ ├── com.test.Negation │ │ └── current │ │ │ └── active │ │ │ └── metadata │ │ ├── com.test.Old │ │ └── current │ │ │ └── active │ │ │ └── metadata │ │ ├── com.test.Overriden │ │ └── current │ │ │ └── active │ │ │ └── metadata │ │ ├── com.test.Reduce │ │ └── current │ │ │ └── active │ │ │ └── metadata │ │ ├── com.test.ResetMode │ │ └── current │ │ │ └── active │ │ │ └── metadata │ │ ├── com.test.Statuses │ │ └── current │ │ │ └── active │ │ │ └── metadata │ │ ├── com.test.TrailingSemicolon │ │ └── current │ │ │ └── active │ │ │ └── metadata │ │ ├── com.test.Unsupported │ │ └── current │ │ │ └── active │ │ │ └── metadata │ │ └── com.test.Variables │ │ └── current │ │ └── active │ │ └── metadata └── user │ └── flatpak │ └── overrides │ ├── com.test.Basic │ ├── com.test.BasicNegated │ ├── com.test.FilesystemWithMode │ ├── com.test.Malformed │ ├── com.test.Old │ ├── com.test.TrailingSemicolon │ ├── com.test.Unsupported │ └── com.test.Variables ├── meson.build ├── service.js └── src ├── testModels.js ├── testPathRow.js ├── testPathsViewer.js └── utils.js /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | env: 3 | es6: true 4 | extends: 'eslint:recommended' 5 | rules: 6 | accessor-pairs: error 7 | array-bracket-newline: 8 | - error 9 | - consistent 10 | array-bracket-spacing: 11 | - error 12 | - never 13 | array-callback-return: error 14 | arrow-parens: 15 | - error 16 | - as-needed 17 | arrow-spacing: error 18 | block-scoped-var: error 19 | block-spacing: error 20 | brace-style: 21 | - error 22 | - 1tbs 23 | class-methods-use-this: error 24 | comma-dangle: 25 | - error 26 | - always-multiline 27 | comma-spacing: 28 | - error 29 | - after: true 30 | before: false 31 | comma-style: 32 | - error 33 | - last 34 | complexity: error 35 | computed-property-spacing: 36 | - error 37 | - never 38 | consistent-return: error 39 | curly: 40 | - error 41 | - multi-or-nest 42 | - consistent 43 | default-case: error 44 | dot-location: 45 | - error 46 | - property 47 | eol-last: error 48 | eqeqeq: error 49 | for-direction: error 50 | func-call-spacing: error 51 | func-name-matching: error 52 | func-names: 53 | - error 54 | - never 55 | func-style: 56 | - error 57 | - declaration 58 | generator-star-spacing: error 59 | getter-return: error 60 | global-require: error 61 | guard-for-in: error 62 | handle-callback-err: error 63 | indent: 64 | - error 65 | - 4 66 | - MemberExpression: 'off' 67 | key-spacing: error 68 | keyword-spacing: 69 | - error 70 | - after: true 71 | before: true 72 | linebreak-style: 73 | - error 74 | - unix 75 | lines-around-comment: error 76 | lines-around-directive: error 77 | lines-between-class-members: error 78 | max-depth: error 79 | max-len: 80 | - error 81 | - code: 120 82 | comments: 80 83 | max-nested-callbacks: error 84 | max-statements-per-line: error 85 | new-parens: error 86 | newline-per-chained-call: error 87 | no-array-constructor: error 88 | no-await-in-loop: error 89 | no-caller: error 90 | no-catch-shadow: error 91 | no-confusing-arrow: error 92 | no-div-regex: error 93 | no-else-return: error 94 | no-empty: 95 | - error 96 | - allowEmptyCatch: true 97 | no-empty-function: error 98 | no-eq-null: error 99 | no-eval: error 100 | no-extend-native: error 101 | no-extra-bind: error 102 | no-extra-label: error 103 | no-extra-parens: 104 | - error 105 | - all 106 | - conditionalAssign: false 107 | returnAssign: false 108 | no-floating-decimal: error 109 | no-invalid-this: error 110 | no-iterator: error 111 | no-label-var: error 112 | no-labels: error 113 | no-lone-blocks: error 114 | no-lonely-if: error 115 | no-loop-func: error 116 | no-multi-assign: error 117 | no-multi-spaces: 118 | - error 119 | - ignoreEOLComments: true 120 | no-multiple-empty-lines: error 121 | no-native-reassign: error 122 | no-negated-condition: error 123 | no-negated-in-lhs: error 124 | no-nested-ternary: error 125 | no-new: error 126 | no-new-func: error 127 | no-new-object: error 128 | no-new-wrappers: error 129 | no-octal-escape: error 130 | no-param-reassign: error 131 | no-proto: error 132 | no-restricted-syntax: 133 | - error 134 | # Use ES6 classes rather than Lang.Class 135 | - "NewExpression > MemberExpression[object.name='Lang']\ 136 | [property.name='Class']" 137 | # Use arrow functions or Function.bind rather than Lang.bind 138 | - "CallExpression > MemberExpression[object.name='Lang']\ 139 | [property.name='bind']" 140 | # Use super rather than this.parent() 141 | - "CallExpression > MemberExpression[object.type='ThisExpression']\ 142 | [property.name='parent']" 143 | # Use GLib timeouts rather than the mainloop module 144 | - MemberExpression[object.name='imports'][property.name='mainloop'] 145 | no-return-assign: error 146 | no-return-await: error 147 | no-self-compare: error 148 | no-sequences: error 149 | no-shadow: error 150 | no-shadow-restricted-names: error 151 | no-spaced-func: error 152 | no-tabs: error 153 | no-template-curly-in-string: error 154 | no-throw-literal: error 155 | no-trailing-spaces: error 156 | no-undef-init: error 157 | no-undefined: error 158 | no-unmodified-loop-condition: error 159 | no-unneeded-ternary: error 160 | no-unused-expressions: error 161 | no-use-before-define: error 162 | no-useless-call: error 163 | no-useless-computed-key: error 164 | no-useless-concat: error 165 | no-useless-constructor: error 166 | no-useless-rename: error 167 | no-useless-return: error 168 | no-whitespace-before-property: error 169 | no-with: error 170 | nonblock-statement-body-position: 171 | - error 172 | - below 173 | object-curly-newline: 174 | - error 175 | - consistent: true 176 | object-curly-spacing: error 177 | object-shorthand: 'off' 178 | operator-assignment: error 179 | operator-linebreak: error 180 | padded-blocks: 181 | - error 182 | - never 183 | prefer-const: error 184 | prefer-destructuring: error 185 | prefer-numeric-literals: error 186 | prefer-promise-reject-errors: error 187 | prefer-rest-params: error 188 | prefer-spread: error 189 | prefer-template: error 190 | quote-props: 191 | - error 192 | - as-needed 193 | quotes: 194 | - error 195 | - single 196 | - avoidEscape: true 197 | radix: error 198 | require-await: error 199 | rest-spread-spacing: 200 | - error 201 | - never 202 | semi: error 203 | semi-spacing: 204 | - error 205 | - after: true 206 | before: false 207 | semi-style: 208 | - error 209 | - last 210 | space-before-blocks: error 211 | space-before-function-paren: 212 | - error 213 | - named: never 214 | anonymous: never 215 | asyncArrow: always 216 | space-in-parens: 217 | - error 218 | - never 219 | space-infix-ops: error 220 | space-unary-ops: error 221 | spaced-comment: 222 | - error 223 | - always 224 | strict: 225 | - error 226 | - never 227 | switch-colon-spacing: error 228 | symbol-description: error 229 | template-curly-spacing: error 230 | template-tag-spacing: error 231 | unicode-bom: 232 | - error 233 | - never 234 | valid-jsdoc: error 235 | wrap-iife: error 236 | wrap-regex: error 237 | yield-star-spacing: error 238 | yoda: 239 | - error 240 | - never 241 | # GJS, SpiderMonkey and Jasmine global objects 242 | globals: 243 | ARGV: false 244 | imports: false 245 | Intl: false 246 | log: false 247 | logError: false 248 | print: false 249 | printerr: false 250 | window: false 251 | _: false 252 | pkg: false 253 | describe: false 254 | beforeEach: false 255 | it: false 256 | expect: false 257 | spyOn: false 258 | beforeAll: false 259 | afterAll: false 260 | parserOptions: 261 | ecmaVersion: 2018 262 | -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [master] 4 | pull_request: 5 | name: CI 6 | jobs: 7 | testing: 8 | runs-on: ubuntu-latest 9 | container: 10 | image: ghcr.io/andyholmes/flatter/gnome:48 11 | options: --privileged 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | with: 16 | submodules: true 17 | - name: Build 18 | uses: andyholmes/flatter@main 19 | with: 20 | files: com.github.tchx84.Flatseal.json 21 | run-tests: true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .flatpak-builder/ 2 | build-dir/ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flatseal  2 | 3 | 4 | 5 | Flatseal is a graphical utility to review and modify permissions from your [Flatpak](https://flatpak.org/) applications. 6 | 7 | ## Usage 8 | 9 | Simply launch Flatseal, select an application and modify its permissions. Restart the application after making the changes. If anything goes wrong just press the reset button. 10 | 11 | For more details please visit the [documentation](./DOCUMENTATION.md) page. 12 | 13 | ## Get it 14 | 15 | [](https://flathub.org/apps/details/com.github.tchx84.Flatseal) 16 | 17 | ## Build it yourself 18 | 19 | ``` 20 | git clone https://github.com/tchx84/Flatseal.git 21 | cd Flatseal 22 | flatpak --user install org.gnome.{Platform,Sdk}//48 23 | flatpak-builder --user --force-clean --install build com.github.tchx84.Flatseal.json 24 | flatpak --user run --branch=master com.github.tchx84.Flatseal 25 | ``` 26 | 27 | Or just use [Builder](https://flathub.org/apps/details/org.gnome.Builder) 28 | 29 | ## Contribute 30 | 31 | If you are interested in contributing to this utility just send a pull request to [this](https://github.com/tchx84/Flatseal) repo. 32 | 33 | ## Disclaimer 34 | 35 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 36 | 37 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the [GNU General Public License](COPYING) for more details. 38 | -------------------------------------------------------------------------------- /com.github.tchx84.Flatseal.json: -------------------------------------------------------------------------------- 1 | { 2 | "app-id": "com.github.tchx84.Flatseal", 3 | "runtime": "org.gnome.Platform", 4 | "runtime-version": "48", 5 | "sdk": "org.gnome.Sdk", 6 | "separate-locales": false, 7 | "command": "com.github.tchx84.Flatseal", 8 | "finish-args": [ 9 | "--share=ipc", 10 | "--socket=fallback-x11", 11 | "--socket=wayland", 12 | "--device=dri", 13 | "--filesystem=/var/lib/flatpak/app:ro", 14 | "--filesystem=xdg-data/flatpak/app:ro", 15 | "--filesystem=xdg-data/flatpak/overrides:create", 16 | "--talk-name=org.gnome.Software", 17 | "--talk-name=org.freedesktop.impl.portal.PermissionStore" 18 | ], 19 | "cleanup": [ 20 | "/include", 21 | "/lib/pkgconfig", 22 | "/man", 23 | "/share/doc", 24 | "/share/gtk-doc", 25 | "/share/man", 26 | "/share/pkgconfig", 27 | "*.la", 28 | "*.a" 29 | ], 30 | "modules": [ 31 | { 32 | "name": "jasmine-gjs", 33 | "buildsystem": "meson", 34 | "cleanup": ["*"], 35 | "sources": [ 36 | { 37 | "type": "git", 38 | "url": "https://github.com/ptomato/jasmine-gjs.git", 39 | "commit": "f6a42dbdbf4d0790358cd17aac09b2d66a22c38c" 40 | } 41 | ] 42 | }, 43 | { 44 | "name": "flatseal", 45 | "buildsystem": "meson", 46 | "sources": [ 47 | { 48 | "type": "git", 49 | "path": ".", 50 | "branch": "HEAD" 51 | } 52 | ] 53 | } 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /data/com.github.tchx84.Flatseal.appdata.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Flatseal 5 | com.github.tchx84.Flatseal 6 | com.github.tchx84.Flatseal.desktop 7 | CC0-1.0 8 | GPL-3.0-or-later 9 | 10 | Manage Flatpak permissions 11 | 12 | 13 | Flatseal is a graphical utility to review and modify permissions from your Flatpak applications. 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | The Flatseal main window 22 | https://raw.githubusercontent.com/tchx84/flatseal/master/screenshots/en/1.png 23 | 24 | 25 | Flatseal showing filesystem permissions 26 | https://raw.githubusercontent.com/tchx84/flatseal/master/screenshots/en/2.png 27 | 28 | 29 | Flatseal showing global overrides 30 | https://raw.githubusercontent.com/tchx84/flatseal/master/screenshots/en/3.png 31 | 32 | 33 | martin.abente.lahaye@gmail.com 34 | 35 | Martin Abente Lahaye 36 | 37 | Martin Abente Lahaye 38 | 39 | https://github.com/tchx84/flatseal 40 | https://github.com/tchx84/flatseal/issues 41 | 42 | mobile 43 | 44 | 45 | #9af0cf 46 | #0c8d5c 47 | 48 | 49 | -------------------------------------------------------------------------------- /data/com.github.tchx84.Flatseal.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | # TRANSLATORS: Don't translate this text 3 | Name=Flatseal 4 | Exec=com.github.tchx84.Flatseal 5 | Terminal=false 6 | Type=Application 7 | Categories=Utility; 8 | StartupNotify=true 9 | Comment=Manage Flatpak permissions 10 | # TRANSLATORS: Don't translate this text 11 | Icon=com.github.tchx84.Flatseal 12 | # Translators: Do NOT translate or transliterate this text (these are enum types)! 13 | X-Purism-FormFactor=Workstation;Mobile; 14 | Keywords=seal;sandbox;override; 15 | -------------------------------------------------------------------------------- /data/com.github.tchx84.Flatseal.gschema.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 1024 6 | 7 | 8 | 768 9 | 10 | 11 | false 12 | 13 | 14 | "" 15 | Application ID that was last selected 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /data/icons/com.github.tchx84.Flatseal-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /data/icons/com.github.tchx84.Flatseal.Flatpak.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 46 | 51 | 52 | 54 | 55 | 57 | image/svg+xml 58 | 60 | 62 | 63 | 65 | 67 | 69 | 71 | 73 | 75 | 77 | 78 | 79 | 80 | 85 | 91 | 95 | 100 | 104 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /data/icons/com.github.tchx84.Flatseal.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /data/icons/meson.build: -------------------------------------------------------------------------------- 1 | install_data('com.github.tchx84.Flatseal.svg', 2 | install_dir: join_paths(get_option('datadir'), 'icons/hicolor/scalable/apps') 3 | ) 4 | 5 | install_data('com.github.tchx84.Flatseal-symbolic.svg', 6 | install_dir: join_paths(get_option('datadir'), 'icons/hicolor/symbolic/apps') 7 | ) 8 | 9 | install_data('com.github.tchx84.Flatseal.Flatpak.svg', 10 | install_dir: join_paths(get_option('datadir'), 'icons/hicolor/scalable/apps') 11 | ) 12 | -------------------------------------------------------------------------------- /data/meson.build: -------------------------------------------------------------------------------- 1 | desktop_file = i18n.merge_file( 2 | input: 'com.github.tchx84.Flatseal.desktop.in', 3 | output: 'com.github.tchx84.Flatseal.desktop', 4 | type: 'desktop', 5 | po_dir: '../po', 6 | install: true, 7 | install_dir: join_paths(get_option('datadir'), 'applications') 8 | ) 9 | 10 | desktop_utils = find_program('desktop-file-validate', required: false) 11 | if desktop_utils.found() 12 | test('Validate desktop file', desktop_utils, 13 | args: [desktop_file] 14 | ) 15 | endif 16 | 17 | appstream_file = i18n.merge_file( 18 | input: 'com.github.tchx84.Flatseal.appdata.xml.in', 19 | output: 'com.github.tchx84.Flatseal.appdata.xml', 20 | po_dir: '../po', 21 | install: true, 22 | install_dir: join_paths(get_option('datadir'), 'appdata') 23 | ) 24 | 25 | appstreamcli = find_program('appstreamcli', required: false) 26 | if appstreamcli.found() 27 | test('Validate appstream file', appstreamcli, 28 | args: ['validate', '--no-net', '--explain', appstream_file] 29 | ) 30 | endif 31 | 32 | install_data( 33 | 'com.github.tchx84.Flatseal.gschema.xml', 34 | install_dir: join_paths(get_option('datadir'), 'glib-2.0', 'schemas') 35 | ) 36 | 37 | compile_schemas = find_program('glib-compile-schemas', required: false) 38 | if compile_schemas.found() 39 | test('Validate schema file', compile_schemas, 40 | args: ['--strict', '--dry-run', meson.current_source_dir()] 41 | ) 42 | endif 43 | 44 | subdir('icons') 45 | -------------------------------------------------------------------------------- /help/meson.build: -------------------------------------------------------------------------------- 1 | docs = [ 2 | 'C/index.html', 3 | ] 4 | 5 | install_data(docs, install_dir: join_paths(get_option('datadir'), 'help', 'C', 'flatseal')) 6 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('flatseal', 2 | version: '2.3.1', 3 | meson_version: '>= 0.59.0', 4 | default_options: [ 'warning_level=2', 5 | ], 6 | ) 7 | 8 | i18n = import('i18n') 9 | 10 | 11 | subdir('data') 12 | subdir('src') 13 | subdir('po') 14 | subdir('tests') 15 | subdir('help') 16 | 17 | gnome.post_install( 18 | gtk_update_icon_cache: true, 19 | glib_compile_schemas: true, 20 | update_desktop_database: true, 21 | ) 22 | -------------------------------------------------------------------------------- /po/LINGUAS: -------------------------------------------------------------------------------- 1 | ar 2 | bg_BG 3 | cs 4 | da 5 | de 6 | es 7 | el 8 | fa 9 | he 10 | hi 11 | hu 12 | id 13 | it 14 | nb 15 | nl 16 | pt_BR 17 | sv 18 | pl 19 | ru 20 | ca 21 | tr 22 | fr 23 | zh_CN 24 | ta 25 | uk 26 | -------------------------------------------------------------------------------- /po/POTFILES: -------------------------------------------------------------------------------- 1 | data/com.github.tchx84.Flatseal.desktop.in 2 | data/com.github.tchx84.Flatseal.appdata.xml.in 3 | data/com.github.tchx84.Flatseal.gschema.xml 4 | src/application.js 5 | src/main.js 6 | src/models/applications.js 7 | src/models/sessionBus.js 8 | src/models/systemBus.js 9 | src/models/devices.js 10 | src/models/features.js 11 | src/models/filesystems.js 12 | src/models/filesystemsOther.js 13 | src/models/info.js 14 | src/models/permissions.js 15 | src/models/persistent.js 16 | src/models/portals.js 17 | src/models/shared.js 18 | src/models/sockets.js 19 | src/models/unsupported.js 20 | src/models/variables.js 21 | src/widgets/aboutDialog.js 22 | src/widgets/aboutDialog.ui 23 | src/widgets/appInfoViewer.js 24 | src/widgets/appInfoViewer.ui 25 | src/widgets/applicationRow.js 26 | src/widgets/applicationRow.ui 27 | src/widgets/busNameRow.js 28 | src/widgets/busNameRow.ui 29 | src/widgets/detailsButton.js 30 | src/widgets/globalRow.ui 31 | src/widgets/globalInfoViewer.ui 32 | src/widgets/menu.ui 33 | src/widgets/overrideStatusIcon.js 34 | src/widgets/pathRow.js 35 | src/widgets/pathRow.ui 36 | src/widgets/pathsViewer.js 37 | src/widgets/pathsViewer.ui 38 | src/widgets/permissionEntryRow.js 39 | src/widgets/permissionEntryRow.ui 40 | src/widgets/permissionPortalRow.js 41 | src/widgets/permissionPortalRow.ui 42 | src/widgets/permissionSwitchRow.js 43 | src/widgets/permissionSwitchRow.ui 44 | src/widgets/relativePathRow.js 45 | src/widgets/relativePathRow.ui 46 | src/widgets/resetButton.js 47 | src/widgets/shortcutsWindow.ui 48 | src/widgets/variableRow.js 49 | src/widgets/variableRow.ui 50 | src/widgets/window.js 51 | src/widgets/window.ui 52 | -------------------------------------------------------------------------------- /po/meson.build: -------------------------------------------------------------------------------- 1 | i18n.gettext('flatseal', preset: 'glib') 2 | -------------------------------------------------------------------------------- /screenshots/en/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tchx84/Flatseal/c518eaa5992245e18b3f1659b857a795f1399eb8/screenshots/en/1.png -------------------------------------------------------------------------------- /screenshots/en/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tchx84/Flatseal/c518eaa5992245e18b3f1659b857a795f1399eb8/screenshots/en/2.png -------------------------------------------------------------------------------- /screenshots/en/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tchx84/Flatseal/c518eaa5992245e18b3f1659b857a795f1399eb8/screenshots/en/3.png -------------------------------------------------------------------------------- /src/application.js: -------------------------------------------------------------------------------- 1 | /* exported FlatsealApplication */ 2 | /* eslint class-methods-use-this: */ 3 | 4 | /* application.js 5 | * 6 | * Copyright 2020 Martin Abente Lahaye 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | const {GObject, Gtk, Gio, Adw} = imports.gi; 23 | 24 | const {FlatsealWindow} = imports.widgets.window; 25 | const {showAboutDialog} = imports.widgets.aboutDialog; 26 | const {FlatsealDocsViewer} = imports.widgets.docsViewer; 27 | const {FlatsealShortcutsWindow} = imports.widgets.shortcutsWindow; 28 | 29 | var FlatsealApplication = GObject.registerClass({ 30 | GTypeName: 'FlatsealApplication', 31 | }, class FlatsealApplication extends Adw.Application { 32 | _init() { 33 | super._init({ 34 | application_id: 'com.github.tchx84.Flatseal', 35 | flags: Gio.ApplicationFlags.FLAGS_NONE, 36 | resource_base_path: '/com/github/tchx84/Flatseal/', 37 | }); 38 | 39 | this._window = null; 40 | } 41 | 42 | _displayHelp() { 43 | const launcher = new Gtk.UriLauncher(); 44 | launcher.uri = 'https://github.com/tchx84/flatseal'; 45 | launcher.launch(this._window, null, this._doDisplayHelp); 46 | } 47 | 48 | _doDisplayHelp(launcher, res) { 49 | try { 50 | launcher.launch_finish(res); 51 | } catch (err) { 52 | logError(err); 53 | } 54 | } 55 | 56 | _displayDocumentation() { 57 | const viewer = new FlatsealDocsViewer(this._window); 58 | viewer.present(); 59 | } 60 | 61 | _displayAbout() { 62 | showAboutDialog(this._window); 63 | } 64 | 65 | _displayShortcuts() { 66 | const dialog = new FlatsealShortcutsWindow({transient_for: this._window}); 67 | dialog.present(); 68 | } 69 | 70 | _quit() { 71 | this._window._shutdown(); 72 | this.quit(); 73 | } 74 | 75 | _setupActions() { 76 | const help_action = new Gio.SimpleAction({name: 'help', state: null}); 77 | help_action.connect('activate', this._displayHelp.bind(this)); 78 | 79 | const documentation_action = new Gio.SimpleAction({name: 'documentation', state: null}); 80 | documentation_action.connect('activate', this._displayDocumentation.bind(this)); 81 | 82 | const shortcuts_action = new Gio.SimpleAction({name: 'shortcuts', state: null}); 83 | shortcuts_action.connect('activate', this._displayShortcuts.bind(this)); 84 | 85 | const about_action = new Gio.SimpleAction({name: 'about', state: null}); 86 | about_action.connect('activate', this._displayAbout.bind(this)); 87 | 88 | const quit_action = new Gio.SimpleAction({name: 'quit', state: null}); 89 | quit_action.connect('activate', this._quit.bind(this)); 90 | 91 | this.add_action(help_action); 92 | this.add_action(documentation_action); 93 | this.add_action(shortcuts_action); 94 | this.add_action(about_action); 95 | this.add_action(quit_action); 96 | 97 | this.set_accels_for_action('app.documentation', ['F1']); 98 | this.set_accels_for_action('app.shortcuts', ['question']); 99 | this.set_accels_for_action('app.quit', ['q']); 100 | this.set_accels_for_action('window.close', ['w']); 101 | } 102 | 103 | vfunc_activate() { 104 | if (this._window === null) 105 | this._window = new FlatsealWindow(this); 106 | 107 | this._window.present(); 108 | } 109 | 110 | vfunc_startup() { 111 | super.vfunc_startup(); 112 | this._setupActions(); 113 | } 114 | }); 115 | -------------------------------------------------------------------------------- /src/com.github.tchx84.Flatseal.data.gresource.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | style.css 5 | widgets/aboutDialog.ui 6 | widgets/appInfoViewer.ui 7 | widgets/applicationRow.ui 8 | widgets/busNameRow.ui 9 | widgets/docsViewer.ui 10 | widgets/globalInfoViewer.ui 11 | widgets/globalRow.ui 12 | widgets/menu.ui 13 | widgets/overrideStatusIcon.ui 14 | widgets/pathRow.ui 15 | widgets/pathsViewer.ui 16 | widgets/permissionEntryRow.ui 17 | widgets/permissionPortalRow.ui 18 | widgets/permissionSwitchRow.ui 19 | widgets/relativePathRow.ui 20 | widgets/shortcutsWindow.ui 21 | widgets/variableRow.ui 22 | widgets/window.ui 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/com.github.tchx84.Flatseal.in: -------------------------------------------------------------------------------- 1 | #!@GJS@ 2 | imports.package.init({ 3 | name: "@PACKAGE_NAME@", 4 | version: "@PACKAGE_VERSION@", 5 | prefix: "@prefix@", 6 | libdir: "@libdir@", 7 | datadir: "@datadir@", 8 | }); 9 | imports.package.run(imports.main); 10 | -------------------------------------------------------------------------------- /src/com.github.tchx84.Flatseal.src.gresource.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | application.js 5 | main.js 6 | widgets/aboutDialog.js 7 | widgets/appInfoViewer.js 8 | widgets/applicationRow.js 9 | widgets/busNameRow.js 10 | widgets/detailsButton.js 11 | widgets/docsViewer.js 12 | widgets/globalInfoViewer.js 13 | widgets/globalRow.js 14 | widgets/overrideStatusIcon.js 15 | widgets/pathRow.js 16 | widgets/pathsViewer.js 17 | widgets/permissionEntryRow.js 18 | widgets/permissionPortalRow.js 19 | widgets/permissionSwitchRow.js 20 | widgets/relativePathRow.js 21 | widgets/resetButton.js 22 | widgets/shortcutsWindow.js 23 | widgets/variableRow.js 24 | widgets/window.js 25 | models/applications.js 26 | models/globalModel.js 27 | models/info.js 28 | models/overrideStatus.js 29 | models/unsupported.js 30 | models/persistent.js 31 | models/permissions.js 32 | models/devices.js 33 | models/features.js 34 | models/filesystems.js 35 | models/filesystemsOther.js 36 | models/portals.js 37 | models/sessionBus.js 38 | models/settings.js 39 | models/shared.js 40 | models/sockets.js 41 | models/systemBus.js 42 | models/variables.js 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | /* exported main */ 2 | 3 | /* main.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | pkg.initGettext(); 22 | pkg.initFormat(); 23 | pkg.require({ 24 | Gio: '2.0', 25 | Gtk: '4.0', 26 | Adw: '1', 27 | WebKit: '6.0', 28 | }); 29 | 30 | const {FlatsealApplication} = imports.application; 31 | 32 | function main(argv) { 33 | const application = new FlatsealApplication(); 34 | return application.run(argv); 35 | } 36 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | pkgdatadir = join_paths(get_option('datadir'), meson.project_name()) 2 | gnome = import('gnome') 3 | 4 | dependency('appstream', version : '>= 1.0') 5 | dependency('libadwaita-1', version : '>= 1.5') 6 | dependency('webkitgtk-6.0', version : '>= 2.40') 7 | 8 | src_res = gnome.compile_resources('com.github.tchx84.Flatseal.src', 9 | 'com.github.tchx84.Flatseal.src.gresource.xml', 10 | gresource_bundle: true, 11 | install: true, 12 | install_dir: pkgdatadir, 13 | ) 14 | 15 | data_res = gnome.compile_resources('com.github.tchx84.Flatseal.data', 16 | 'com.github.tchx84.Flatseal.data.gresource.xml', 17 | gresource_bundle: true, 18 | install: true, 19 | install_dir: pkgdatadir, 20 | ) 21 | 22 | bin_conf = configuration_data() 23 | bin_conf.set('GJS', find_program('gjs').full_path()) 24 | bin_conf.set('PACKAGE_VERSION', meson.project_version()) 25 | bin_conf.set('PACKAGE_NAME', meson.project_name()) 26 | bin_conf.set('prefix', get_option('prefix')) 27 | bin_conf.set('libdir', join_paths(get_option('prefix'), get_option('libdir'))) 28 | bin_conf.set('datadir', join_paths(get_option('prefix'), get_option('datadir'))) 29 | 30 | configure_file( 31 | input: 'com.github.tchx84.Flatseal.in', 32 | output: 'com.github.tchx84.Flatseal', 33 | configuration: bin_conf, 34 | install: true, 35 | install_dir: get_option('bindir') 36 | ) 37 | -------------------------------------------------------------------------------- /src/models/devices.js: -------------------------------------------------------------------------------- 1 | /* exported FlatpakDevicesModel */ 2 | 3 | /* devices.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject} = imports.gi; 22 | 23 | const {FlatpakSharedModel} = imports.models.shared; 24 | 25 | 26 | var FlatpakDevicesModel = GObject.registerClass({ 27 | GTypeName: 'FlatpakDevicesModel', 28 | }, class FlatpakDevicesModel extends FlatpakSharedModel { 29 | _init() { 30 | super._init({}); 31 | } 32 | 33 | getPermissions() { 34 | return { 35 | 'devices-dri': { 36 | supported: this._info.supports('0.4.0'), 37 | description: _('GPU acceleration'), 38 | option: 'dri', 39 | value: this.constructor.getDefault(), 40 | example: 'device=dri', 41 | }, 42 | 'devices-input': { 43 | supported: this._info.supports('1.15.6'), 44 | description: _('Input devices'), 45 | option: 'input', 46 | value: this.constructor.getDefault(), 47 | example: 'device=input', 48 | }, 49 | 'devices-kvm': { 50 | supported: this._info.supports('0.6.12'), 51 | description: _('Virtualization'), 52 | option: 'kvm', 53 | value: this.constructor.getDefault(), 54 | example: 'device=kvm', 55 | }, 56 | 'devices-shm': { 57 | supported: this._info.supports('1.6.1'), 58 | description: _('Shared memory'), 59 | option: 'shm', 60 | value: this.constructor.getDefault(), 61 | example: 'device=shm', 62 | }, 63 | 'devices-usb': { 64 | supported: this._info.supports('1.15.11'), 65 | description: _('USB devices'), 66 | option: 'usb', 67 | value: this.constructor.getDefault(), 68 | example: 'device=usb', 69 | }, 70 | 'devices-all': { 71 | supported: this._info.supports('0.6.6'), 72 | description: _('All devices (e.g. webcam)'), 73 | option: 'all', 74 | value: this.constructor.getDefault(), 75 | example: 'device=all', 76 | }, 77 | }; 78 | } 79 | 80 | static getGroup() { 81 | return 'Context'; 82 | } 83 | 84 | static getKey() { 85 | return 'devices'; 86 | } 87 | 88 | static getStyle() { 89 | return 'devices'; 90 | } 91 | 92 | static getTitle() { 93 | return 'Device'; 94 | } 95 | 96 | static getDescription() { 97 | return _('List of devices available in the sandbox'); 98 | } 99 | }); 100 | -------------------------------------------------------------------------------- /src/models/features.js: -------------------------------------------------------------------------------- 1 | /* exported FlatpakFeaturesModel */ 2 | 3 | /* features.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject} = imports.gi; 22 | 23 | const {FlatpakSharedModel} = imports.models.shared; 24 | 25 | 26 | var FlatpakFeaturesModel = GObject.registerClass({ 27 | GTypeName: 'FlatpakFeaturesModel', 28 | }, class FlatpakFeaturesModel extends FlatpakSharedModel { 29 | _init() { 30 | super._init({}); 31 | } 32 | 33 | getPermissions() { 34 | return { 35 | 'features-devel': { 36 | supported: this._info.supports('0.6.10'), 37 | description: _('Development syscalls (e.g. ptrace)'), 38 | option: 'devel', 39 | value: this.constructor.getDefault(), 40 | example: 'allow=devel', 41 | }, 42 | 'features-multiarch': { 43 | supported: this._info.supports('0.6.12'), 44 | description: _('Programs from other architectures'), 45 | option: 'multiarch', 46 | value: this.constructor.getDefault(), 47 | example: 'allow=multiarch', 48 | }, 49 | 'features-bluetooth': { 50 | supported: this._info.supports('0.11.8'), 51 | description: _('Bluetooth'), 52 | option: 'bluetooth', 53 | value: this.constructor.getDefault(), 54 | example: 'allow=bluetooth', 55 | }, 56 | 'features-canbus': { 57 | supported: this._info.supports('1.0.3'), 58 | description: _('Controller Area Network bus'), 59 | option: 'canbus', 60 | value: this.constructor.getDefault(), 61 | example: 'allow=canbus', 62 | }, 63 | 'features-per-app-dev-shm': { 64 | supported: this._info.supports('1.11.1'), 65 | description: _('Application Shared Memory'), 66 | option: 'per-app-dev-shm', 67 | value: this.constructor.getDefault(), 68 | example: 'allow=per-app-dev-shm', 69 | }, 70 | }; 71 | } 72 | 73 | static getGroup() { 74 | return 'Context'; 75 | } 76 | 77 | static getKey() { 78 | return 'features'; 79 | } 80 | 81 | static getStyle() { 82 | return 'features'; 83 | } 84 | 85 | static getTitle() { 86 | return 'Allow'; 87 | } 88 | 89 | static getDescription() { 90 | return _('List of features available to the application'); 91 | } 92 | }); 93 | -------------------------------------------------------------------------------- /src/models/filesystems.js: -------------------------------------------------------------------------------- 1 | /* exported FlatpakFilesystemsModel getDefault */ 2 | 3 | /* filesystems.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject} = imports.gi; 22 | 23 | const {FlatpakSharedModel} = imports.models.shared; 24 | 25 | 26 | var FlatpakFilesystemsModel = GObject.registerClass({ 27 | GTypeName: 'FlatpakFilesystemsModel', 28 | }, class FlatpakFilesystemsModel extends FlatpakSharedModel { 29 | _init() { 30 | super._init({}); 31 | } 32 | 33 | getPermissions() { 34 | return { 35 | 'filesystems-host': { 36 | supported: this._info.supports('0.4.0'), 37 | description: _('All system files'), 38 | option: 'host', 39 | value: this.constructor.getDefault(), 40 | example: 'filesystem=host', 41 | }, 42 | 'filesystems-host-os': { 43 | supported: this._info.supports('1.7.1'), 44 | description: _('All system libraries, executables and static data'), 45 | option: 'host-os', 46 | value: this.constructor.getDefault(), 47 | example: 'filesystem=host-os', 48 | }, 49 | 'filesystems-host-etc': { 50 | supported: this._info.supports('1.7.1'), 51 | description: _('All system configurations'), 52 | option: 'host-etc', 53 | value: this.constructor.getDefault(), 54 | example: 'filesystem=host-etc', 55 | }, 56 | 'filesystems-home': { 57 | supported: this._info.supports('0.4.0'), 58 | description: _('All user files'), 59 | option: 'home', 60 | value: this.constructor.getDefault(), 61 | example: 'filesystem=home', 62 | }, 63 | }; 64 | } 65 | 66 | static getGroup() { 67 | return 'Context'; 68 | } 69 | 70 | static getKey() { 71 | return 'filesystems'; 72 | } 73 | 74 | static getStyle() { 75 | return 'filesystems'; 76 | } 77 | 78 | static getTitle() { 79 | return 'Filesystem'; 80 | } 81 | 82 | static getDescription() { 83 | return _('List of filesystem subsets available to the application'); 84 | } 85 | 86 | get overrides() { 87 | return this._overrides; 88 | } 89 | 90 | get globals() { 91 | return this._globals; 92 | } 93 | 94 | get originals() { 95 | return this._originals; 96 | } 97 | }); 98 | 99 | var getDefault = (function() { 100 | let instance; 101 | return function() { 102 | if (typeof instance === 'undefined') 103 | instance = new FlatpakFilesystemsModel(); 104 | return instance; 105 | }; 106 | }()); 107 | -------------------------------------------------------------------------------- /src/models/globalModel.js: -------------------------------------------------------------------------------- 1 | /* exported isGlobalOverride */ 2 | 3 | /* globalModel.js 4 | * 5 | * Copyright 2022 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | function isGlobalOverride(appId) { 22 | return appId === 'global'; 23 | } 24 | -------------------------------------------------------------------------------- /src/models/info.js: -------------------------------------------------------------------------------- 1 | /* exported FlatpakInfoModel getDefault */ 2 | 3 | /* info.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject, GLib} = imports.gi; 22 | 23 | 24 | var FlatpakInfoModel = GObject.registerClass({ 25 | GTypeName: 'FlatpakInfoModel', 26 | }, class FlatpakInfoModel extends GObject.Object { 27 | _init() { 28 | super._init({}); 29 | this._version = null; 30 | } 31 | 32 | static _getInfoPath() { 33 | const path = GLib.getenv('FLATPAK_INFO_PATH'); 34 | if (path) 35 | return path; 36 | 37 | return GLib.build_filenamev([ 38 | GLib.DIR_SEPARATOR_S, '.flatpak-info', 39 | ]); 40 | } 41 | 42 | _parseVersion() { 43 | const keyFile = new GLib.KeyFile(); 44 | 45 | try { 46 | keyFile.load_from_file(this.constructor._getInfoPath(), GLib.KeyFileFlags.NONE); 47 | return keyFile.get_value('Instance', 'flatpak-version'); 48 | } catch (err) { 49 | return null; 50 | } 51 | } 52 | 53 | getVersion() { 54 | if (this._version === null) 55 | this._version = this._parseVersion(); 56 | 57 | return this._version; 58 | } 59 | 60 | supports(target) { 61 | const version = this.getVersion(); 62 | 63 | if (version === null) 64 | return true; 65 | 66 | const versions = version.split('.'); 67 | const targets = target.split('.'); 68 | const components = Math.max(versions.length, targets.length); 69 | 70 | for (let index = 0; index < components; index++) { 71 | const _version = parseInt(versions[index] || 0, 10); 72 | const _target = parseInt(targets[index] || 0, 10); 73 | 74 | if (_version < _target) 75 | return false; 76 | if (_version > _target) 77 | return true; 78 | } 79 | 80 | return true; 81 | } 82 | 83 | reload() { 84 | this._version = this._parseVersion(); 85 | } 86 | }); 87 | 88 | 89 | var getDefault = (function() { 90 | let instance; 91 | return function() { 92 | if (typeof instance === 'undefined') 93 | instance = new FlatpakInfoModel(); 94 | return instance; 95 | }; 96 | }()); 97 | -------------------------------------------------------------------------------- /src/models/overrideStatus.js: -------------------------------------------------------------------------------- 1 | /* exported FlatsealOverrideStatus */ 2 | 3 | /* overrideStatus.js 4 | * 5 | * Copyright 2022 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | var FlatsealOverrideStatus = { 22 | ORIGINAL: 'original', 23 | GLOBAL: 'global', 24 | USER: 'user', 25 | }; 26 | -------------------------------------------------------------------------------- /src/models/persistent.js: -------------------------------------------------------------------------------- 1 | /* exported FlatpakPersistentModel getDefault */ 2 | 3 | /* persistent.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject} = imports.gi; 22 | 23 | const {FlatpakSharedModel} = imports.models.shared; 24 | 25 | 26 | var FlatpakPersistentModel = GObject.registerClass({ 27 | GTypeName: 'FlatpakPersistentModel', 28 | }, class FlatpakPersistentModel extends FlatpakSharedModel { 29 | _init() { 30 | super._init({}); 31 | } 32 | 33 | getPermissions() { 34 | return { 35 | persistent: { 36 | supported: this._info.supports('0.4.0'), 37 | description: _('Files'), 38 | option: null, 39 | value: this.constructor.getDefault(), 40 | example: _('e.g. .thunderbird'), 41 | }, 42 | }; 43 | } 44 | 45 | static getGroup() { 46 | return 'Context'; 47 | } 48 | 49 | static getKey() { 50 | return 'persistent'; 51 | } 52 | 53 | static getStyle() { 54 | return 'persistent'; 55 | } 56 | 57 | static getTitle() { 58 | return 'Persistent'; 59 | } 60 | 61 | static getDescription() { 62 | return _('List of homedir-relative paths created in the sandbox'); 63 | } 64 | 65 | static getType() { 66 | return 'relativePath'; 67 | } 68 | 69 | static getDefault() { 70 | return ''; 71 | } 72 | 73 | getOptions() { // eslint-disable-line class-methods-use-this 74 | return null; 75 | } 76 | 77 | updateFromProxyProperty(property, value) { 78 | const originals = new Set([...this._originals, ...this._globals]); 79 | 80 | const overrides = new Set(this.constructor.deserialize(value) 81 | .filter(p => p.length !== 0) 82 | .filter(p => !originals.has(p))); 83 | 84 | this._overrides = overrides; 85 | } 86 | 87 | updateStatusProperty(proxy) { 88 | const values = proxy.persistent 89 | .split(';') 90 | .filter(p => p.length !== 0) 91 | .map(p => this._getStatusForPermission(p)); 92 | 93 | proxy.set_property('persistent-status', this.constructor.serialize(values)); 94 | } 95 | 96 | updateProxyProperty(proxy) { 97 | const paths = new Set([...this._originals, ...this._globals, ...this._overrides]); 98 | 99 | const persistent = [...paths].join(';'); 100 | 101 | proxy.set_property('persistent', persistent); 102 | } 103 | 104 | loadFromKeyFile(group, key, value, overrides, global) { 105 | if (value.length === 0) 106 | return; 107 | const set = this._findProperSet(overrides, global); 108 | set.add(value); 109 | } 110 | 111 | isOriginal(value) { 112 | const originals = new Set([...this._originals, ...this._globals]); 113 | return originals.has(value); 114 | } 115 | }); 116 | 117 | var getDefault = (function() { 118 | let instance; 119 | return function() { 120 | if (typeof instance === 'undefined') 121 | instance = new FlatpakPersistentModel(); 122 | return instance; 123 | }; 124 | }()); 125 | -------------------------------------------------------------------------------- /src/models/sessionBus.js: -------------------------------------------------------------------------------- 1 | /* exported FlatpakSessionBusModel */ 2 | 3 | /* sessionBus.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject} = imports.gi; 22 | 23 | const {FlatpakSharedModel} = imports.models.shared; 24 | const {FlatsealOverrideStatus} = imports.models.overrideStatus; 25 | 26 | 27 | var FlatpakSessionBusModel = GObject.registerClass({ 28 | GTypeName: 'FlatpakSessionBusModel', 29 | }, class FlatpakSessionBusModel extends FlatpakSharedModel { 30 | _init() { 31 | super._init({}); 32 | } 33 | 34 | getPermissions() { 35 | return { 36 | 'session-talk': { 37 | supported: this._info.supports('0.4.0'), 38 | description: _('Talks'), 39 | option: 'talk', 40 | value: this.constructor.getDefault(), 41 | example: 'e.g. org.freedesktop.Notifications', 42 | }, 43 | 'session-own': { 44 | supported: this._info.supports('0.4.0'), 45 | description: _('Owns'), 46 | option: 'own', 47 | value: this.constructor.getDefault(), 48 | example: 'e.g. org.gnome.Contacts.SearchProvider', 49 | }, 50 | }; 51 | } 52 | 53 | static getDefault() { 54 | return ''; 55 | } 56 | 57 | static getType() { 58 | return 'bus'; 59 | } 60 | 61 | static getGroup() { 62 | return 'Session Bus Policy'; 63 | } 64 | 65 | static getKey() { 66 | return null; 67 | } 68 | 69 | static getPrefix() { 70 | return 'session'; 71 | } 72 | 73 | static getStyle() { 74 | return 'session'; 75 | } 76 | 77 | static getTitle() { 78 | return 'Session Bus'; 79 | } 80 | 81 | static getDescription() { 82 | return _('List of well-known names on the session bus'); 83 | } 84 | 85 | getOptions() { // eslint-disable-line class-methods-use-this 86 | return null; 87 | } 88 | 89 | updateFromProxyProperty(property, value) { 90 | const prefix = this.constructor.getPrefix(); 91 | const option = property.replace(`${prefix}-`, ''); 92 | 93 | const originals = {...this._originals, ...this._globals}; 94 | const values = this.constructor.deserialize(value); 95 | 96 | /* Reset overrides on Talk since it's the first to update */ 97 | const overrides = option === 'talk' ? {} : this._overrides; 98 | const missing = option === 'talk' ? {} : this._missing; 99 | 100 | values 101 | .filter(n => n.length !== 0) 102 | .filter(n => !(n in originals) || originals[n] !== option) 103 | .forEach(n => { 104 | overrides[n] = option; 105 | }); 106 | 107 | Object.keys(originals) 108 | .filter(n => originals[n] === option) 109 | .filter(n => values.indexOf(n) === -1) 110 | .forEach(n => { 111 | missing[n] = 'none'; 112 | }); 113 | 114 | /* Preserve previously negated overrides */ 115 | Object.keys(this._overrides) 116 | .filter(n => this._overrides[n] === 'none') 117 | .filter(n => values.indexOf(n) === -1) 118 | .forEach(n => { 119 | missing[n] = 'none'; 120 | }); 121 | 122 | this._overrides = overrides; 123 | this._missing = missing; 124 | 125 | /* Add missing ones after Own since it's the last to update */ 126 | if (option !== 'own') 127 | return; 128 | 129 | this._overrides = {...this._missing, ...this._overrides}; 130 | } 131 | 132 | _getStatusForPermission(name) { 133 | let status = FlatsealOverrideStatus.ORIGINAL; 134 | 135 | if (name in this._globals) 136 | status = FlatsealOverrideStatus.GLOBAL; 137 | if (name in this._overrides) 138 | status = FlatsealOverrideStatus.USER; 139 | 140 | return status; 141 | } 142 | 143 | updateStatusProperty(proxy) { 144 | const prefix = this.constructor.getPrefix(); 145 | 146 | const talk_values = proxy[`${prefix}-talk`] 147 | .split(';') 148 | .filter(n => n.length !== 0) 149 | .map(n => this._getStatusForPermission(n)); 150 | 151 | const own_values = proxy[`${prefix}-own`] 152 | .split(';') 153 | .filter(n => n.length !== 0) 154 | .map(n => this._getStatusForPermission(n)); 155 | 156 | proxy.set_property(`${prefix}-talk-status`, talk_values.join(';')); 157 | proxy.set_property(`${prefix}-own-status`, own_values.join(';')); 158 | } 159 | 160 | updateProxyProperty(proxy) { 161 | const options = {talk: [], own: [], none: []}; 162 | const values = {...this._originals, ...this._globals, ...this._overrides}; 163 | 164 | Object.entries(values).forEach(([name, option]) => { 165 | if (!(option in options)) 166 | return; 167 | options[option].push(name); 168 | }); 169 | 170 | const prefix = this.constructor.getPrefix(); 171 | proxy.set_property(`${prefix}-talk`, this.constructor.serialize(options['talk'])); 172 | proxy.set_property(`${prefix}-own`, this.constructor.serialize(options['own'])); 173 | } 174 | 175 | loadFromKeyFile(group, name, option, overrides, global) { 176 | const dictionary = this._findProperSet(overrides, global); 177 | dictionary[name] = option; 178 | } 179 | 180 | saveToKeyFile(keyFile) { 181 | const group = this.constructor.getGroup(); 182 | Object.entries(this._overrides).forEach(([key, value]) => { 183 | keyFile.set_value(group, key, value); 184 | }); 185 | } 186 | 187 | reset() { 188 | this._overrides = {}; 189 | this._globals = {}; 190 | this._originals = {}; 191 | this._missing = {}; 192 | } 193 | }); 194 | -------------------------------------------------------------------------------- /src/models/settings.js: -------------------------------------------------------------------------------- 1 | /* exported FlatsealSettingsModel getDefault */ 2 | 3 | /* settings.js 4 | * 5 | * Copyright 2021 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject, Gio} = imports.gi; 22 | 23 | 24 | var FlatsealSettingsModel = GObject.registerClass({ 25 | GTypeName: 'FlatsealSettingsModel', 26 | }, class FlatsealSettingsModel extends GObject.Object { 27 | _init() { 28 | super._init({}); 29 | this._settings = new Gio.Settings({schema_id: 'com.github.tchx84.Flatseal'}); 30 | } 31 | 32 | restoreWindowState(window) { 33 | window.default_width = this._settings.get_int('window-width'); 34 | window.default_height = this._settings.get_int('window-height'); 35 | if (this._settings.get_boolean('window-maximized')) 36 | window.maximize(); 37 | } 38 | 39 | saveWindowState(window) { 40 | if (!window.maximized) { 41 | const [width, height] = window.get_default_size(); 42 | this._settings.set_int('window-width', width); 43 | this._settings.set_int('window-height', height); 44 | } 45 | this._settings.set_boolean('window-maximized', window.maximized); 46 | } 47 | 48 | getSelectedAppId() { 49 | return this._settings.get_string('selected-app-id'); 50 | } 51 | 52 | setSelectedAppId(appId) { 53 | this._settings.set_string('selected-app-id', appId); 54 | } 55 | }); 56 | -------------------------------------------------------------------------------- /src/models/shared.js: -------------------------------------------------------------------------------- 1 | /* exported FlatpakSharedModel */ 2 | 3 | /* shared.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject} = imports.gi; 22 | const {info} = imports.models; 23 | const {FlatsealOverrideStatus} = imports.models.overrideStatus; 24 | 25 | 26 | var FlatpakSharedModel = GObject.registerClass({ 27 | GTypeName: 'FlatpakSharedModel', 28 | }, class FlatpakSharedModel extends GObject.Object { 29 | _init() { 30 | super._init({}); 31 | this._info = info.getDefault(); 32 | this.reset(); 33 | } 34 | 35 | getPermissions() { 36 | return { 37 | 'shared-network': { 38 | supported: this._info.supports('0.4.0'), 39 | description: _('Network'), 40 | option: 'network', 41 | value: this.constructor.getDefault(), 42 | example: 'share=network', 43 | }, 44 | 'shared-ipc': { 45 | supported: this._info.supports('0.4.0'), 46 | description: _('Inter-process communications'), 47 | option: 'ipc', 48 | value: this.constructor.getDefault(), 49 | example: 'share=ipc', 50 | }, 51 | }; 52 | } 53 | 54 | static getGroup() { 55 | return 'Context'; 56 | } 57 | 58 | static getKey() { 59 | return 'shared'; 60 | } 61 | 62 | static getType() { 63 | return 'state'; 64 | } 65 | 66 | static getDefault() { 67 | return false; 68 | } 69 | 70 | static getStyle() { 71 | return 'shared'; 72 | } 73 | 74 | static getTitle() { 75 | return 'Share'; 76 | } 77 | 78 | static getDescription() { 79 | return _('List of subsystems shared with the host system'); 80 | } 81 | 82 | static serialize(values) { 83 | return values.join(';'); 84 | } 85 | 86 | static deserialize(value) { 87 | return value.split(';'); 88 | } 89 | 90 | getOptions() { 91 | return Object.entries(this.getPermissions()) 92 | .map(([, permission]) => permission.option); 93 | } 94 | 95 | updateFromProxyProperty(property, value) { 96 | const permission = this.getPermissions()[property]; 97 | const {option} = permission; 98 | const override = value ? option : `!${option}`; 99 | 100 | /* Determine if this value is an actual override */ 101 | 102 | /* Preserve previously negated overrides */ 103 | const matchesDefault = value === permission.value && !this._overrides.has(override); 104 | 105 | const fromOriginals = this._originals.has(override); 106 | const fromGlobals = this._globals.has(override); 107 | 108 | const seenInOriginals = this.constructor._isOverriden(this._originals, override); 109 | const seenInGlobals = this.constructor._isOverriden(this._globals, override); 110 | 111 | /* Assume it isn't */ 112 | this._overrides.delete(option); 113 | this._overrides.delete(`!${option}`); 114 | 115 | /* Ignore if it came from originals or from globals */ 116 | if (fromOriginals && !seenInGlobals || fromGlobals) 117 | return; 118 | 119 | /* Ignore if it's just the default value */ 120 | if (matchesDefault && !seenInGlobals && !seenInOriginals) 121 | return; 122 | 123 | /* It's an override */ 124 | this._overrides.add(override); 125 | } 126 | 127 | static _isOverriden(set, permission) { 128 | const option = permission.replace('!', ''); 129 | return set.has(option) || set.has(`!${option}`); 130 | } 131 | 132 | _getStatusForPermission(permission) { 133 | let status = FlatsealOverrideStatus.ORIGINAL; 134 | 135 | if (this._globals.has(permission) || this._globals.has(`!${permission}`)) 136 | status = FlatsealOverrideStatus.GLOBAL; 137 | if (this._overrides.has(permission) || this._overrides.has(`!${permission}`)) 138 | status = FlatsealOverrideStatus.USER; 139 | 140 | return status; 141 | } 142 | 143 | updateStatusProperty(proxy) { 144 | Object.entries(this.getPermissions()).forEach(([property, permission]) => { 145 | const {option} = permission; 146 | const statusProperty = `${property}-status`; 147 | const status = this._getStatusForPermission(option); 148 | 149 | proxy.set_property(statusProperty, status); 150 | }); 151 | } 152 | 153 | updateProxyProperty(proxy) { 154 | const originals = [...this._originals] 155 | .filter(o => !this.constructor._isOverriden(this._globals, o)) 156 | .filter(o => !this.constructor._isOverriden(this._overrides, o)); 157 | 158 | const globals = [...this._globals] 159 | .filter(g => !this.constructor._isOverriden(this._overrides, g)); 160 | 161 | const permissions = new Set([...originals, ...globals, ...this._overrides]); 162 | 163 | Object.entries(this.getPermissions()).forEach(([property, permission]) => { 164 | let value = this.constructor.getDefault(); 165 | 166 | const {option} = permission; 167 | if (permissions.has(option)) 168 | value = true; 169 | if (permissions.has(`!${option}`)) 170 | value = false; 171 | 172 | proxy.set_property(property, value); 173 | }); 174 | } 175 | 176 | _findProperSet(overrides, global) { 177 | if (overrides && global) 178 | return this._globals; 179 | if (overrides && !global) 180 | return this._overrides; 181 | return this._originals; 182 | } 183 | 184 | loadFromKeyFile(group, key, value, overrides, global) { 185 | const set = this._findProperSet(overrides, global); 186 | set.add(value); 187 | } 188 | 189 | saveToKeyFile(keyFile) { 190 | const group = this.constructor.getGroup(); 191 | const key = this.constructor.getKey(); 192 | 193 | this._overrides.forEach(value => { 194 | let _value = value; 195 | 196 | try { 197 | const existing = keyFile.get_value(group, key); 198 | _value = `${value};${existing}`; 199 | } catch (err) { 200 | _value = `${value}`; 201 | } 202 | 203 | keyFile.set_value(group, key, _value); 204 | }); 205 | } 206 | 207 | reset() { 208 | this._overrides = new Set(); 209 | this._globals = new Set(); 210 | this._originals = new Set(); 211 | } 212 | }); 213 | -------------------------------------------------------------------------------- /src/models/sockets.js: -------------------------------------------------------------------------------- 1 | /* exported FlatpakSocketsModel */ 2 | 3 | /* sockets.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject} = imports.gi; 22 | 23 | const {FlatpakSharedModel} = imports.models.shared; 24 | 25 | 26 | var FlatpakSocketsModel = GObject.registerClass({ 27 | GTypeName: 'FlatpakSocketsModel', 28 | }, class FlatpakSocketsModel extends FlatpakSharedModel { 29 | _init() { 30 | super._init({}); 31 | } 32 | 33 | getPermissions() { 34 | return { 35 | 'sockets-x11': { 36 | supported: this._info.supports('0.4.0'), 37 | description: _('X11 windowing system'), 38 | option: 'x11', 39 | value: this.constructor.getDefault(), 40 | example: 'socket=x11', 41 | }, 42 | 'sockets-wayland': { 43 | supported: this._info.supports('0.4.0'), 44 | description: _('Wayland windowing system'), 45 | option: 'wayland', 46 | value: this.constructor.getDefault(), 47 | example: 'socket=wayland', 48 | }, 49 | 'sockets-fallback-x11': { 50 | supported: this._info.supports('0.11.1'), 51 | description: _('Fallback to X11 windowing system'), 52 | option: 'fallback-x11', 53 | value: this.constructor.getDefault(), 54 | example: 'socket=fallback-x11', 55 | }, 56 | 'sockets-pulseaudio': { 57 | supported: this._info.supports('0.4.0'), 58 | description: _('PulseAudio sound server'), 59 | option: 'pulseaudio', 60 | value: this.constructor.getDefault(), 61 | example: 'socket=pulseaudio', 62 | }, 63 | 'sockets-session-bus': { 64 | supported: this._info.supports('0.4.0'), 65 | description: _('D-Bus session bus'), 66 | option: 'session-bus', 67 | value: this.constructor.getDefault(), 68 | example: 'socket=session-bus', 69 | }, 70 | 'sockets-system-bus': { 71 | supported: this._info.supports('0.4.0'), 72 | description: _('D-Bus system bus'), 73 | option: 'system-bus', 74 | value: this.constructor.getDefault(), 75 | example: 'socket=system-bus', 76 | }, 77 | 'sockets-ssh-auth': { 78 | supported: this._info.supports('0.99.1'), 79 | description: _('Secure Shell agent'), 80 | option: 'ssh-auth', 81 | value: this.constructor.getDefault(), 82 | example: 'socket=ssh-auth', 83 | }, 84 | 'sockets-pcsc': { 85 | supported: this._info.supports('1.3.2'), 86 | description: _('Smart cards'), 87 | option: 'pcsc', 88 | value: this.constructor.getDefault(), 89 | example: 'socket=pcsc', 90 | }, 91 | 'sockets-cups': { 92 | supported: this._info.supports('1.5.2'), 93 | description: _('Printing system'), 94 | option: 'cups', 95 | value: this.constructor.getDefault(), 96 | example: 'socket=cups', 97 | }, 98 | 'sockets-gpg-agent': { 99 | supported: this._info.supports('1.14.0'), 100 | description: _('GPG-Agent directories'), 101 | option: 'gpg-agent', 102 | value: this.constructor.getDefault(), 103 | example: 'socket=gpg-agent', 104 | }, 105 | 'sockets-inherit-wayland-socket': { 106 | supported: this._info.supports('1.15.7'), 107 | description: _('Inherit Wayland socket'), 108 | option: 'inherit-wayland-socket', 109 | value: this.constructor.getDefault(), 110 | example: 'socket=inherit-wayland-socket', 111 | }, 112 | }; 113 | } 114 | 115 | static getGroup() { 116 | return 'Context'; 117 | } 118 | 119 | static getKey() { 120 | return 'sockets'; 121 | } 122 | 123 | static getStyle() { 124 | return 'sockets'; 125 | } 126 | 127 | static getTitle() { 128 | return 'Socket'; 129 | } 130 | 131 | static getDescription() { 132 | return _('List of well-known sockets available in the sandbox'); 133 | } 134 | }); 135 | -------------------------------------------------------------------------------- /src/models/systemBus.js: -------------------------------------------------------------------------------- 1 | /* exported FlatpakSystemBusModel */ 2 | 3 | /* sessionBus.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject} = imports.gi; 22 | 23 | const {FlatpakSessionBusModel} = imports.models.sessionBus; 24 | 25 | 26 | var FlatpakSystemBusModel = GObject.registerClass({ 27 | GTypeName: 'FlatpakSystemBusModel', 28 | }, class FlatpakSystemBusModel extends FlatpakSessionBusModel { 29 | _init() { 30 | super._init({}); 31 | } 32 | 33 | getPermissions() { 34 | return { 35 | 'system-talk': { 36 | supported: this._info.supports('0.4.0'), 37 | description: _('Talks'), 38 | option: 'talk', 39 | value: this.constructor.getDefault(), 40 | example: 'e.g. org.freedesktop.Accounts', 41 | }, 42 | 'system-own': { 43 | supported: this._info.supports('0.4.0'), 44 | description: _('Owns'), 45 | option: 'own', 46 | value: this.constructor.getDefault(), 47 | example: 'e.g. org.freedesktop.GeoClue2', 48 | }, 49 | }; 50 | } 51 | 52 | static getGroup() { 53 | return 'System Bus Policy'; 54 | } 55 | 56 | static getKey() { 57 | return null; 58 | } 59 | 60 | static getPrefix() { 61 | return 'system'; 62 | } 63 | 64 | static getStyle() { 65 | return 'system'; 66 | } 67 | 68 | static getTitle() { 69 | return 'System Bus'; 70 | } 71 | 72 | static getDescription() { 73 | return _('List of well-known names on the system bus'); 74 | } 75 | }); 76 | -------------------------------------------------------------------------------- /src/models/unsupported.js: -------------------------------------------------------------------------------- 1 | /* exported FlatpakUnsupportedModel */ 2 | /* eslint class-methods-use-this: */ 3 | 4 | /* unsupported.js 5 | * 6 | * Copyright 2020 Martin Abente Lahaye 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | const {GObject} = imports.gi; 23 | 24 | 25 | var FlatpakUnsupportedModel = GObject.registerClass({ 26 | GTypeName: 'FlatpakUnsupportedModel', 27 | }, class FlatpakUnsupportedModel extends GObject.Object { 28 | _init() { 29 | super._init(); 30 | this.reset(); 31 | } 32 | 33 | getPermissions() { 34 | return {}; 35 | } 36 | 37 | static getGroup() { 38 | return 'unsupported'; 39 | } 40 | 41 | static getKey() { 42 | return null; 43 | } 44 | 45 | getOptions() { // eslint-disable-line class-methods-use-this 46 | return null; 47 | } 48 | 49 | updateFromProxyProperty() { // eslint-disable-line class-methods-use-this 50 | return false; 51 | } 52 | 53 | updateStatusProperty() { 54 | 55 | /* does not apply to this backend */ 56 | } 57 | 58 | updateProxyProperty() { // eslint-disable-line class-methods-use-this 59 | return false; 60 | } 61 | 62 | loadFromKeyFile(group, key, value) { 63 | this._permissions.add([group, key, value]); 64 | return true; 65 | } 66 | 67 | saveToKeyFile(keyFile) { 68 | this._permissions.forEach(([group, key, value]) => { 69 | let _value; 70 | 71 | try { 72 | const existing = keyFile.get_value(group, key); 73 | _value = `${value};${existing}`; 74 | } catch (err) { 75 | _value = `${value}`; 76 | } 77 | 78 | keyFile.set_value(group, key, _value); 79 | }); 80 | } 81 | 82 | reset() { 83 | this._permissions = new Set(); 84 | } 85 | 86 | isEmpty() { 87 | return this._permissions.size === 0; 88 | } 89 | }); 90 | -------------------------------------------------------------------------------- /src/models/variables.js: -------------------------------------------------------------------------------- 1 | /* exported FlatpakVariablesModel */ 2 | 3 | /* variables.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject} = imports.gi; 22 | 23 | const {FlatpakSharedModel} = imports.models.shared; 24 | const {FlatsealOverrideStatus} = imports.models.overrideStatus; 25 | 26 | var VAR_REGEXP = /^\w+=[\S ]+$/; 27 | 28 | var FlatpakVariablesModel = GObject.registerClass({ 29 | GTypeName: 'FlatpakVariablesModel', 30 | }, class FlatpakVariablesModel extends FlatpakSharedModel { 31 | _init() { 32 | super._init({}); 33 | this.reset(); 34 | } 35 | 36 | getPermissions() { 37 | return { 38 | variables: { 39 | supported: this._info.supports('0.4.0'), 40 | description: _('Variables'), 41 | option: null, 42 | value: this.constructor.getDefault(), 43 | example: _('e.g. GTK_DEBUG=interactive'), 44 | }, 45 | }; 46 | } 47 | 48 | static getDefault() { 49 | return ''; 50 | } 51 | 52 | static getType() { 53 | return 'variable'; 54 | } 55 | 56 | static getGroup() { 57 | return 'Environment'; 58 | } 59 | 60 | static getKey() { 61 | return null; 62 | } 63 | 64 | static getStyle() { 65 | return 'environment'; 66 | } 67 | 68 | static getTitle() { 69 | return 'Environment'; 70 | } 71 | 72 | static getDescription() { 73 | return _('List of variables exported to the application'); 74 | } 75 | 76 | static deserialize(value) { 77 | return value 78 | .split(/(?=;[\w\s]+=)/) 79 | .map(v => v.replace(/^;/, '')); 80 | } 81 | 82 | getOptions() { // eslint-disable-line class-methods-use-this 83 | return null; 84 | } 85 | 86 | updateFromProxyProperty(property, value) { 87 | const overrides = {}; 88 | const variables = {}; 89 | const originals = {...this._originals, ...this._globals}; 90 | 91 | this.constructor.deserialize(value) 92 | .filter(v => this._expression.test(v)) 93 | .map(v => v.split(/[=](.*)/s)) 94 | .forEach(([k, v]) => { 95 | variables[k] = v; 96 | }); 97 | 98 | Object.entries(variables) 99 | .filter(([k, v]) => !(k in originals) || originals[k] !== v) 100 | .forEach(([k, v]) => { 101 | overrides[k] = v; 102 | }); 103 | 104 | Object.keys(originals) 105 | .filter(k => !(k in variables)) 106 | .filter(k => originals[k] !== '') 107 | .forEach(k => { 108 | overrides[k] = ''; 109 | }); 110 | 111 | /* Preserve previously negated overrides */ 112 | Object.keys(this._overrides) 113 | .filter(k => !(k in variables)) 114 | .filter(k => this._overrides[k] === '') 115 | .forEach(k => { 116 | overrides[k] = ''; 117 | }); 118 | 119 | this._overrides = overrides; 120 | } 121 | 122 | _getStatusForPermission(variable) { 123 | const [key] = variable.split(/[=](.*)/s); 124 | 125 | let status = FlatsealOverrideStatus.ORIGINAL; 126 | if (key in this._globals) 127 | status = FlatsealOverrideStatus.GLOBAL; 128 | if (key in this._overrides) 129 | status = FlatsealOverrideStatus.USER; 130 | 131 | return status; 132 | } 133 | 134 | updateStatusProperty(proxy) { 135 | const values = this.constructor.deserialize(proxy.variables) 136 | .filter(v => v.length !== 0) 137 | .map(v => this._getStatusForPermission(v)); 138 | 139 | proxy.set_property('variables-status', values.join(';')); 140 | } 141 | 142 | updateProxyProperty(proxy) { 143 | let variables = {...this._originals, ...this._globals, ...this._overrides}; 144 | 145 | variables = Object.entries(variables) 146 | .filter(([, value]) => value.length !== 0) 147 | .map(([key, value]) => `${key}=${value}`); 148 | 149 | proxy.set_property('variables', this.constructor.serialize(variables)); 150 | } 151 | 152 | loadFromKeyFile(group, key, value, overrides, global) { 153 | if (!overrides && value.length === 0) 154 | return; 155 | 156 | const dict = this._findProperSet(overrides, global); 157 | dict[key] = value; 158 | } 159 | 160 | saveToKeyFile(keyFile) { 161 | const group = this.constructor.getGroup(); 162 | Object.entries(this._overrides).forEach(([key, value]) => { 163 | keyFile.set_value(group, key, value); 164 | }); 165 | } 166 | 167 | reset() { 168 | this._overrides = {}; 169 | this._globals = {}; 170 | this._originals = {}; 171 | this._expression = new RegExp(VAR_REGEXP); 172 | } 173 | }); 174 | -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | .navigation-sidebar row { 2 | padding: 3px 0px; 3 | } 4 | 5 | .permissions clamp { 6 | margin-bottom: 25px; 7 | } 8 | 9 | row .status { 10 | padding: 8px; 11 | background-color: transparent; 12 | background-image: -gtk-icontheme("dialog-warning-symbolic"); 13 | background-repeat: no-repeat; 14 | background-position: center; 15 | background-size: 16px; 16 | } 17 | row .status.user { 18 | color: @accent_color; 19 | } 20 | row .status.global { 21 | color: @insensitive_fg_color; 22 | } 23 | 24 | row .content .bus .info, 25 | row .content .variable .info, 26 | row .content .path .info { 27 | padding: 8px; 28 | color: @accent_color; 29 | background-color: transparent; 30 | background-image: -gtk-icontheme("dialog-question-symbolic"); 31 | background-repeat: no-repeat; 32 | background-position: center; 33 | background-size: 16px; 34 | } 35 | 36 | row .content .relative.path .info, 37 | row .content .bus .info, 38 | row .content .variable .info { 39 | background-image: -gtk-icontheme("emblem-ok-symbolic"); 40 | } 41 | 42 | row .content .bus.not-valid .info, 43 | row .content .variable.not-valid .info, 44 | row .content .path.not-valid .info { 45 | color: @warning_color; 46 | background-image: -gtk-icontheme("dialog-warning-symbolic"); 47 | } 48 | 49 | row .content entry, 50 | row .content .status, 51 | row .content button { 52 | margin-left: 8px; 53 | } 54 | 55 | row .content .path popover treeview { 56 | color: @insensitive_fg_color; 57 | font-size: small; 58 | background-color: transparent; 59 | } 60 | row .content .path popover treeview:selected { 61 | background-color: @theme_bg_color; 62 | } 63 | 64 | .permissions .app-info { 65 | margin-top: 15px; 66 | } 67 | .permissions .app-info.compact { 68 | margin-top: 5px; 69 | margin-bottom: 5px; 70 | } 71 | 72 | .permissions .app-info .icon { 73 | margin: 25px 25px 25px 0px; 74 | } 75 | .permissions .app-info.compact .icon { 76 | margin: 5px 0px 5px 0px; 77 | } 78 | 79 | .permissions .app-info .name { 80 | font-size: xx-large; 81 | font-weight: 800; 82 | margin-bottom: 5px; 83 | } 84 | 85 | .permissions .app-info .author { 86 | margin-bottom: 15px; 87 | } 88 | .permissions .app-info.compact .author { 89 | margin-bottom: 25px; 90 | } 91 | 92 | .permissions .app-info .description { 93 | color: @insensitive_fg_color; 94 | margin-bottom: 5px; 95 | } 96 | 97 | .permissions .app-info .value { 98 | margin-bottom: 5px; 99 | } 100 | 101 | .reset-button image { 102 | color: @warning_color; 103 | } 104 | -------------------------------------------------------------------------------- /src/widgets/aboutDialog.js: -------------------------------------------------------------------------------- 1 | /* exported showAboutDialog */ 2 | 3 | /* aboutDialog.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {Gtk} = imports.gi; 22 | const ABOUT_RESOURCE = '/com/github/tchx84/Flatseal/widgets/aboutDialog.ui'; 23 | 24 | function showAboutDialog(window) { 25 | const builder = Gtk.Builder.new_from_resource(ABOUT_RESOURCE); 26 | const dialog = builder.get_object('about-window'); 27 | dialog.present(window); 28 | } 29 | -------------------------------------------------------------------------------- /src/widgets/aboutDialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Flatseal 5 | 2.3.1 6 | © 2020-2025 Martin Abente Lahaye 7 | https://github.com/tchx84/Flatseal/issues/new 8 | Martin Abente Lahaye 9 | Martin Abente Lahaye 10 | Bilal Elmoussaoui 11 | Adrien Plazas 12 | Kevin Degeling 13 | Mufeed Ali 14 | Leorize 15 | Christopher Davis 16 | Lctrs 17 | Will Thompson 18 | Jonas Johan Solsvik 19 | Maximiliano Sandoval 20 | Ferenc 21 | rusty-snake 22 | Guilhem Morer 23 | Fiana Fortressia 24 | Hubert Figuière 25 | Daniel Rusek 26 | Heimen Stoffels 27 | Tim Rieck 28 | Óvári 29 | Milo Casagrande 30 | Kukuh Syafaat 31 | Åke Engelbrektson 32 | Bartłomiej Garbiec 33 | Ícar Nin Solana 34 | BigmenPixel0 35 | Ümit Solmaz 36 | Exponentactivity 37 | Eric Zhang 38 | Georgi Georgiev 39 | Sabri Ünal 40 | Mathieu Bousquet 41 | Yaron Shahrabani 42 | K.B.Dharun Krishna 43 | Dingzhong Chen 44 | Philipp Kiemle 45 | Vovkiv 46 | Nikolai Eugen Sandvik 47 | Aleksandr Melman 48 | Lucas Serrano 49 | Benedek Dévényi 50 | Athanasios Karachalios 51 | Scrambled777 52 | Sunniva Løvstad 53 | Dynamiclinking 54 | Ahmed Mohammed 55 | Danial Behzadi 56 | Limemane 57 | Tobias Bernard 58 | J.P. MacDonald 59 | TheEvilSkeleton 60 | com.github.tchx84.Flatseal 61 | gpl-3-0 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/widgets/appInfoViewer.js: -------------------------------------------------------------------------------- 1 | /* exported FlatsealAppInfoViewer */ 2 | /* eslint accessor-pairs: */ 3 | 4 | /* appInfoViewer.js 5 | * 6 | * Copyright 2020 Martin Abente Lahaye 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | const {GObject, GLib, Gtk} = imports.gi; 23 | 24 | const {applications} = imports.models; 25 | 26 | const _propFlags = GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT; 27 | 28 | const styles = { 29 | NORMAL: 'normal', 30 | COMPACT: 'compact', 31 | }; 32 | 33 | var FlatsealAppInfoViewer = GObject.registerClass({ 34 | GTypeName: 'FlatsealAppInfoViewer', 35 | Template: 'resource:///com/github/tchx84/Flatseal/widgets/appInfoViewer.ui', 36 | InternalChildren: ['icon', 'name', 'author', 'version', 'released', 'runtime'], 37 | Properties: { 38 | compact: GObject.ParamSpec.boolean( 39 | 'compact', 40 | 'compact', 41 | 'compact', 42 | _propFlags, false), 43 | }, 44 | }, class FlatsealAppInfoViewer extends Gtk.Box { 45 | _init() { 46 | super._init(); 47 | this._appId = ''; 48 | this._compact = false; 49 | this._applications = applications.getDefault(); 50 | this._validator = new RegExp(/^(\d+)-(\d+)-(\d+)$/); 51 | } 52 | 53 | _getFormattedDate(string) { 54 | if (!this._validator.test(string)) 55 | return string; 56 | 57 | const [, year, month, day] = string.match(this._validator); 58 | const date = GLib.DateTime.new(GLib.TimeZone.new_local(), year, month, day, 0, 0, 0); 59 | 60 | /* TRANSLATORS: , */ 61 | return date.format(_('%B %e, %Y')); 62 | } 63 | 64 | _setup() { 65 | const appdata = this._applications.getAppDataForAppId(this._appId); 66 | const desktop = this._applications.getDesktopForAppData(appdata); 67 | 68 | this._name.set_label(appdata.name); 69 | this._author.set_label(appdata.author); 70 | this._version.set_label(appdata.version); 71 | this._released.set_label(this._getFormattedDate(appdata.date)); 72 | 73 | this._icon.set_from_icon_name(desktop.icon); 74 | 75 | const metadata = this._applications.getMetadataForAppId(this._appId); 76 | this._runtime.set_label(metadata.runtime); 77 | } 78 | 79 | set appId(id) { 80 | this._appId = id; 81 | this._setup(); 82 | } 83 | 84 | set compact(value) { 85 | if (typeof this._icon === 'undefined') 86 | return; 87 | if (this._compact === value) 88 | return; 89 | 90 | const orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; 91 | const alignment = value ? Gtk.Align.CENTER : Gtk.Align.START; 92 | 93 | this.set_orientation(orientation); 94 | this._icon.halign = alignment; 95 | this._name.halign = alignment; 96 | this._author.halign = alignment; 97 | this.halign = alignment; 98 | 99 | const style = value ? styles.COMPACT : styles.NORMAL; 100 | const context = this.get_style_context(); 101 | 102 | if (context.has_class(styles.NORMAL)) 103 | context.remove_class(styles.NORMAL); 104 | if (context.has_class(styles.COMPACT)) 105 | context.remove_class(styles.COMPACT); 106 | 107 | context.add_class(style); 108 | 109 | this._compact = value; 110 | } 111 | }); 112 | -------------------------------------------------------------------------------- /src/widgets/appInfoViewer.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 128 7 | 11 | 12 | 13 | 14 | 15 | center 16 | vertical 17 | 18 | 19 | start 20 | end 21 | 25 | 26 | 27 | 28 | 29 | start 30 | end 31 | 35 | 36 | 37 | 38 | 39 | 20 40 | 41 | 42 | True 43 | 44 | 45 | start 46 | Version 47 | 50 | 51 | 0 52 | 1 53 | 54 | 55 | 56 | 57 | 58 | start 59 | Last Updated 60 | 63 | 64 | 0 65 | 2 66 | 67 | 68 | 69 | 70 | 71 | start 72 | Runtime 73 | 76 | 77 | 0 78 | 3 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | True 87 | 88 | 89 | start 90 | end 91 | 94 | 95 | 0 96 | 1 97 | 98 | 99 | 100 | 101 | 102 | start 103 | end 104 | 107 | 108 | 0 109 | 2 110 | 111 | 112 | 113 | 114 | 115 | start 116 | end 117 | 120 | 121 | 0 122 | 3 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 133 | 134 | 135 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /src/widgets/applicationRow.js: -------------------------------------------------------------------------------- 1 | /* exported FlatsealApplicationRow */ 2 | 3 | /* applicationRow.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GLib, GObject, Adw} = imports.gi; 22 | 23 | 24 | var FlatsealApplicationRow = GObject.registerClass({ 25 | GTypeName: 'FlatsealApplicationRow', 26 | Template: 'resource:///com/github/tchx84/Flatseal/widgets/applicationRow.ui', 27 | InternalChildren: ['icon'], 28 | }, class FlatsealApplicationRow extends Adw.ActionRow { 29 | _init(appId, appName, appIconName) { 30 | super._init(); 31 | this._icon.set_from_icon_name(appIconName); 32 | this.set_title(GLib.markup_escape_text(appName, -1)); 33 | this.set_subtitle(appId); 34 | } 35 | 36 | get appId() { 37 | return this.get_subtitle(); 38 | } 39 | 40 | get appName() { 41 | return this.get_title(); 42 | } 43 | }); 44 | -------------------------------------------------------------------------------- /src/widgets/applicationRow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 1 6 | True 7 | 8 | 9 | center 10 | center 11 | 48 12 | com.github.tchx84.Flatseal 13 | 17 | 18 | 19 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/widgets/busNameRow.js: -------------------------------------------------------------------------------- 1 | /* exported FlatsealBusNameRow */ 2 | 3 | /* busNameRow.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject, Gtk} = imports.gi; 22 | 23 | const {FlatsealOverrideStatusIcon} = imports.widgets.overrideStatusIcon; 24 | 25 | const _propFlags = GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT; 26 | 27 | /* https://dbus.freedesktop.org/doc/dbus-specification.html */ 28 | const EXP = /^(([A-Z]|[a-z]|[0-9]|_|-)+)(\.(([A-Z]|[a-z]|[0-9]|_|-)+))+(\.\*){0,1}$/; 29 | 30 | var validity = { 31 | VALID: 'valid', 32 | NOTVALID: 'not-valid', 33 | }; 34 | 35 | const _notValidMsg = _('This is not a valid option'); 36 | 37 | 38 | var FlatsealBusNameRow = GObject.registerClass({ 39 | GTypeName: 'FlatsealBusNameRow', 40 | Template: 'resource:///com/github/tchx84/Flatseal/widgets/busNameRow.ui', 41 | InternalChildren: ['entry', 'button', 'image', 'statusBox'], 42 | Properties: { 43 | text: GObject.ParamSpec.string( 44 | 'text', 45 | 'text', 46 | 'text', 47 | _propFlags, ''), 48 | }, 49 | Signals: { 50 | 'remove-requested': {}, 51 | }, 52 | }, class FlatsealBusNameRow extends Gtk.Box { 53 | _init() { 54 | super._init({}); 55 | this._setup(); 56 | } 57 | 58 | _setup() { 59 | this._expression = new RegExp(EXP); 60 | 61 | this._entry.connect('notify::text', this._changed.bind(this)); 62 | this._button.connect('clicked', this._remove.bind(this)); 63 | 64 | this._statusIcon = new FlatsealOverrideStatusIcon(); 65 | this._statusBox.append(this._statusIcon); 66 | 67 | this._validate(); 68 | } 69 | 70 | _remove() { 71 | this.emit('remove-requested'); 72 | } 73 | 74 | _changed() { 75 | this._validate(); 76 | this.notify('text'); 77 | } 78 | 79 | _validate() { 80 | const context = this.get_style_context(); 81 | 82 | if (context.has_class(validity.VALID)) 83 | context.remove_class(validity.VALID); 84 | else if (context.has_class(validity.NOTVALID)) 85 | context.remove_class(validity.NOTVALID); 86 | 87 | if (this._expression.test(this.text)) { 88 | context.add_class(validity.VALID); 89 | this._image.set_tooltip_text(''); 90 | } else { 91 | context.add_class(validity.NOTVALID); 92 | this._image.set_tooltip_text(_notValidMsg); 93 | } 94 | } 95 | 96 | get text() { 97 | if (!this._entry) 98 | return ''; 99 | return this._entry.get_text(); 100 | } 101 | 102 | set text(text) { 103 | if (this.text === text) 104 | return; 105 | this._entry.set_text(text); 106 | } 107 | 108 | get status() { 109 | return this._statusIcon.status; 110 | } 111 | 112 | set status(status) { 113 | this._statusIcon.status = status; 114 | } 115 | }); 116 | -------------------------------------------------------------------------------- /src/widgets/busNameRow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 5 | 6 | 7 | True 8 | center 9 | 12 | 13 | 14 | 15 | 16 | True 17 | True 18 | center 19 | 20 | 21 | 22 | 23 | center 24 | center 25 | vertical 26 | 27 | 28 | 29 | 30 | center 31 | window-close-symbolic 32 | 35 | 36 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/widgets/detailsButton.js: -------------------------------------------------------------------------------- 1 | /* exported FlatsealDetailsButton */ 2 | 3 | /* detailsButton.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {Gio, GObject, Gtk, GLib} = imports.gi; 22 | 23 | const GSActivateIface = ` 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | `; 34 | 35 | const DBListNamesIface = ` 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | `; 44 | 45 | 46 | var FlatsealDetailsButton = GObject.registerClass({ 47 | GTypeName: 'FlatsealDetailsButton', 48 | }, class FlatsealDetailsButton extends Gtk.Button { 49 | _init(permissions) { 50 | super._init(); 51 | this._setup(permissions); 52 | } 53 | 54 | _setup(permissions) { 55 | this._proxy = null; 56 | this._permissions = permissions; 57 | this._foundManager = false; 58 | 59 | this.set_use_underline(true); 60 | this.set_label(_('_Show Details')); 61 | 62 | this.connect('clicked', this._clicked.bind(this)); 63 | this._checkSoftwareManager(); 64 | } 65 | 66 | _checkSoftwareManager() { 67 | try { 68 | const DBListNamesProxy = Gio.DBusProxy.makeProxyWrapper(DBListNamesIface); 69 | const proxy = new DBListNamesProxy( 70 | Gio.DBus.session, 'org.freedesktop.DBus', '/org/freedesktop/DBus'); 71 | proxy.ListActivatableNamesRemote(([services], err) => { 72 | this._foundManager = !err && services.indexOf('org.gnome.Software') !== -1; 73 | this._update(); 74 | }); 75 | } catch (err) { 76 | this._foundManager = false; 77 | this._update(); 78 | } 79 | } 80 | 81 | _clicked() { 82 | try { 83 | const GSProxy = Gio.DBusProxy.makeProxyWrapper(GSActivateIface); 84 | this._proxy = new GSProxy( 85 | Gio.DBus.session, 86 | 'org.gnome.Software', 87 | '/org/gnome/Software', 88 | this._launchSoftwareManager.bind(this)); 89 | } catch (err) { 90 | logError(err); 91 | } 92 | } 93 | 94 | _launchSoftwareManager() { 95 | try { 96 | const args = GLib.Variant.new('(ss)', [this._permissions.appId, '']); 97 | this._proxy.ActivateRemote('details', [args], null); 98 | } catch (err) { 99 | logError(err); 100 | } 101 | } 102 | 103 | _update() { 104 | this.sensitive = this._foundManager && this._permissions.appId; 105 | 106 | if (this.sensitive) 107 | this.set_tooltip_text(_('Show application in a software manager')); 108 | else 109 | this.set_tooltip_text(_('No software manager found')); 110 | } 111 | 112 | enable() { 113 | this._update(); 114 | } 115 | 116 | disable() { 117 | this.sensitive = false; 118 | this.set_tooltip_text(''); 119 | } 120 | }); 121 | -------------------------------------------------------------------------------- /src/widgets/docsViewer.js: -------------------------------------------------------------------------------- 1 | /* exported FlatsealDocsViewer */ 2 | 3 | /* docsViewer.js 4 | * 5 | * Copyright 2021 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GLib, Gtk, GObject, Adw, WebKit} = imports.gi; 22 | const {WebView} = imports.gi.WebKit; // eslint-disable-line no-unused-vars 23 | 24 | const MAX_RESULTS = 10; 25 | 26 | var FlatsealDocsViewer = GObject.registerClass({ 27 | GTypeName: 'FlatsealDocsViewer', 28 | Template: 'resource:///com/github/tchx84/Flatseal/widgets/docsViewer.ui', 29 | InternalChildren: [ 30 | 'webview', 31 | 'backButton', 32 | 'forwardButton', 33 | 'previousButton', 34 | 'nextButton', 35 | 'searchButton', 36 | 'searchBar', 37 | 'searchEntry', 38 | ], 39 | Signals: { 40 | close: { 41 | flags: GObject.SignalFlags.RUN_LAST | GObject.SignalFlags.ACTION, 42 | }, 43 | }, 44 | }, class FlatsealDocsViewer extends Adw.Window { 45 | _init(parent) { 46 | super._init({}); 47 | this._setup(parent); 48 | } 49 | 50 | _setup(parent) { 51 | const [width, height] = parent.get_default_size(); 52 | this.default_width = width; 53 | this.default_height = height; 54 | this.transient_for = parent; 55 | this.application = parent.application; 56 | 57 | const path = GLib.build_filenamev([ 58 | imports.package.datadir, 59 | 'help', 60 | 'C', 61 | 'flatseal', 62 | 'index.html', 63 | ]); 64 | 65 | this._webview.load_uri(`file://${path}`); 66 | 67 | /* Force it to use browser history with inner anchors */ 68 | this._webview.connect('notify::uri', this._loadUri.bind(this)); 69 | 70 | /* Use system web browser for external urls */ 71 | this._webview.connect('decide-policy', this._decidePolicy.bind(this)); 72 | 73 | /* Update navigation buttons on every history change */ 74 | this._webview.connect_after('load-changed', this._updateNavigation.bind(this)); 75 | this._backButton.connect('clicked', this._goBack.bind(this)); 76 | this._forwardButton.connect('clicked', this._goForward.bind(this)); 77 | 78 | this._searchEntry.connect('search-changed', this._resetSearch.bind(this)); 79 | this._searchEntry.connect('stop-search', this._cancelSearch.bind(this)); 80 | this._searchEntry.connect('next-match', this._searchNext.bind(this)); 81 | this._searchEntry.connect('previous-match', this._searchPrevious.bind(this)); 82 | 83 | this._previousButton.connect('clicked', this._searchPrevious.bind(this)); 84 | this._nextButton.connect('clicked', this._searchNext.bind(this)); 85 | 86 | this._searchButton.connect('toggled', this._toggleSearchWithButton.bind(this)); 87 | this._searchButton.bind_property( 88 | 'active', 89 | this._searchBar, 90 | 'search-mode-enabled', 91 | GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE); 92 | 93 | this._searchBar.connect( 94 | 'notify::search-mode-enabled', this._enableSearchController.bind(this)); 95 | 96 | this._searchBar.connect_entry(this._searchEntry); 97 | this._searchBar.set_key_capture_widget(this); 98 | } 99 | 100 | _loadUri() { 101 | this._webview.load_uri(this._webview.uri); 102 | } 103 | 104 | _decidePolicy(webview, decision, type) { // eslint-disable-line class-methods-use-this 105 | if (type !== WebKit.PolicyDecisionType.NAVIGATION_ACTION) 106 | return false; 107 | 108 | const action = decision.get_navigation_action(); 109 | const uri = action.get_request().get_uri(); 110 | 111 | if (!uri.startsWith('file')) { 112 | const launcher = new Gtk.UriLauncher(); 113 | launcher.uri = uri; 114 | launcher.launch(this, null, this._openUri); 115 | 116 | decision.ignore(); 117 | return true; 118 | } 119 | 120 | return false; 121 | } 122 | 123 | _openUri(launcher, res) { // eslint-disable-line class-methods-use-this 124 | try { 125 | launcher.launch_finish(res); 126 | } catch (err) { 127 | logError(err); 128 | } 129 | } 130 | 131 | _updateNavigation() { 132 | this._backButton.sensitive = this._webview.can_go_back(); 133 | this._forwardButton.sensitive = this._webview.can_go_forward(); 134 | } 135 | 136 | _goBack() { 137 | this._webview.go_back(); 138 | } 139 | 140 | _goForward() { 141 | this._webview.go_forward(); 142 | } 143 | 144 | _toggleSearchWithButton() { 145 | if (this._searchButton.active) 146 | this._searchEntry.grab_focus(); 147 | else 148 | this._searchButton.grab_focus(); 149 | } 150 | 151 | _enableSearchController() { 152 | if (this._searchBar.search_mode_enabled) 153 | this._findController = this._webview.get_find_controller(); 154 | else 155 | this._findController.search_finish(); 156 | } 157 | 158 | _cancelSearch() { 159 | if (this._searchEntry.get_text() === '') 160 | this._searchBar.search_mode_enabled = false; 161 | 162 | this._searchEntry.set_text(''); 163 | } 164 | 165 | _resetSearch() { 166 | this._findController.search( 167 | this._searchEntry.text, 168 | WebKit.FindOptions.CASE_INSENSITIVE | WebKit.FindOptions.WRAP_AROUND, 169 | MAX_RESULTS); 170 | } 171 | 172 | _searchPrevious() { 173 | this._findController.search_previous(); 174 | } 175 | 176 | _searchNext() { 177 | this._findController.search_next(); 178 | } 179 | }); 180 | -------------------------------------------------------------------------------- /src/widgets/docsViewer.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | True 5 | Flatseal 6 | True 7 | 8 | 9 | 10 | 11 | Escape 12 | action(window.close) 13 | 14 | 15 | 16 | 17 | 18 | 19 | vertical 20 | 21 | 22 | 23 | 24 | go-previous-symbolic 25 | 26 | 27 | 28 | 29 | go-next-symbolic 30 | 31 | 32 | 33 | 34 | system-search-symbolic 35 | 36 | 37 | global 38 | 39 | 40 | <Control>F 41 | activate 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 6 57 | 58 | 59 | True 60 | 61 | 62 | 63 | 64 | go-up-symbolic 65 | 66 | 67 | 68 | 69 | go-down-symbolic 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | True 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/widgets/globalInfoViewer.js: -------------------------------------------------------------------------------- 1 | /* exported FlatsealGlobalInfoViewer */ 2 | /* eslint accessor-pairs: */ 3 | 4 | /* globalInfoViewer.js 5 | * 6 | * Copyright 2022 Martin Abente Lahaye 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | const {GObject, Gtk} = imports.gi; 23 | 24 | const _propFlags = GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT; 25 | const {info, permissions, portals} = imports.models; 26 | 27 | const styles = { 28 | NORMAL: 'normal', 29 | COMPACT: 'compact', 30 | }; 31 | 32 | var FlatsealGlobalInfoViewer = GObject.registerClass({ 33 | GTypeName: 'FlatsealGlobalInfoViewer', 34 | Template: 'resource:///com/github/tchx84/Flatseal/widgets/globalInfoViewer.ui', 35 | InternalChildren: ['icon', 'title', 'description', 'flatpak', 'portal', 'overrides'], 36 | Properties: { 37 | compact: GObject.ParamSpec.boolean( 38 | 'compact', 39 | 'compact', 40 | 'compact', 41 | _propFlags, false), 42 | }, 43 | }, class FlatsealGlobalInfoViewer extends Gtk.Box { 44 | _init() { 45 | super._init(); 46 | this._compact = false; 47 | this._setup(); 48 | } 49 | 50 | _setup() { 51 | this.constructor._setIfAvailable(this._flatpak, info.getDefault().getVersion()); 52 | this.constructor._setIfAvailable(this._portal, portals.getDefault().getVersion()); 53 | this.constructor._setIfAvailable(this._overrides, permissions.getDefault()._getBaseOverridesPath()); 54 | } 55 | 56 | static _setIfAvailable(label, value) { 57 | if (!value) 58 | return; 59 | label.set_label(value.toString()); 60 | } 61 | 62 | set compact(value) { 63 | if (typeof this._icon === 'undefined') 64 | return; 65 | if (this._compact === value) 66 | return; 67 | 68 | const orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; 69 | const alignment = value ? Gtk.Align.CENTER : Gtk.Align.START; 70 | 71 | this.set_orientation(orientation); 72 | this._icon.halign = alignment; 73 | this._title.halign = alignment; 74 | this._description.halign = alignment; 75 | this.halign = alignment; 76 | 77 | const style = value ? styles.COMPACT : styles.NORMAL; 78 | const context = this.get_style_context(); 79 | 80 | if (context.has_class(styles.NORMAL)) 81 | context.remove_class(styles.NORMAL); 82 | if (context.has_class(styles.COMPACT)) 83 | context.remove_class(styles.COMPACT); 84 | 85 | context.add_class(style); 86 | 87 | this._compact = value; 88 | } 89 | }); 90 | -------------------------------------------------------------------------------- /src/widgets/globalInfoViewer.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 128 7 | com.github.tchx84.Flatseal.Flatpak 8 | 12 | 13 | 14 | 15 | 16 | center 17 | vertical 18 | 19 | 20 | start 21 | All Applications 22 | end 23 | 27 | 28 | 29 | 30 | 31 | start 32 | Changes that apply to all Flatpak applications 33 | end 34 | 38 | 39 | 40 | 41 | 42 | True 43 | center 44 | 20 45 | 46 | 47 | center 48 | vertical 49 | 50 | 51 | True 52 | 53 | 54 | start 55 | Flatpak Version 56 | 59 | 60 | 0 61 | 1 62 | 63 | 64 | 65 | 66 | 67 | start 68 | Portal Version 69 | 72 | 73 | 0 74 | 2 75 | 76 | 77 | 78 | 79 | 80 | start 81 | Changes Path 82 | 85 | 86 | 0 87 | 3 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | center 98 | vertical 99 | 100 | 101 | True 102 | 103 | 104 | start 105 | Unknown 106 | end 107 | 110 | 111 | 0 112 | 1 113 | 114 | 115 | 116 | 117 | 118 | start 119 | Unknown 120 | end 121 | 124 | 125 | 0 126 | 2 127 | 128 | 129 | 130 | 131 | 132 | start 133 | Unknown 134 | end 135 | 138 | 139 | 0 140 | 3 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 153 | 154 | 155 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /src/widgets/globalRow.js: -------------------------------------------------------------------------------- 1 | /* exported FlatsealGlobalRow */ 2 | 3 | /* globalRow.js 4 | * 5 | * Copyright 2022 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject, Adw} = imports.gi; 22 | 23 | 24 | var FlatsealGlobalRow = GObject.registerClass({ 25 | GTypeName: 'FlatsealGlobalRow', 26 | Template: 'resource:///com/github/tchx84/Flatseal/widgets/globalRow.ui', 27 | InternalChildren: ['icon'], 28 | }, class FlatsealGlobalRow extends Adw.ActionRow { 29 | _init() { 30 | super._init(); 31 | } 32 | 33 | get appId() { 34 | return this.get_subtitle(); 35 | } 36 | 37 | get appName() { 38 | return this.get_title(); 39 | } 40 | }); 41 | -------------------------------------------------------------------------------- /src/widgets/globalRow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 1 6 | True 7 | All Applications 8 | global 9 | 10 | 11 | center 12 | center 13 | 48 14 | com.github.tchx84.Flatseal.Flatpak 15 | 19 | 20 | 21 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/widgets/menu.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | app.help 7 | _Help 8 | 9 | 10 | app.documentation 11 | _Documentation 12 | 13 | 14 | app.shortcuts 15 | _Keyboard Shortcuts 16 | 17 | 18 | app.about 19 | _About Flatseal 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/widgets/overrideStatusIcon.js: -------------------------------------------------------------------------------- 1 | /* exported FlatsealOverrideStatusIcon OverrideStatus */ 2 | 3 | /* overrideStatusIcon.js 4 | * 5 | * Copyright 2022 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject, Gtk} = imports.gi; 22 | const {FlatsealOverrideStatus} = imports.models.overrideStatus; 23 | 24 | const OverrideStatusDescription = { 25 | global: _('Changed globally'), 26 | user: _('Changed by the user'), 27 | }; 28 | 29 | var FlatsealOverrideStatusIcon = GObject.registerClass({ 30 | GTypeName: 'FlatsealOverrideStatusIcon', 31 | Template: 'resource:///com/github/tchx84/Flatseal/widgets/overrideStatusIcon.ui', 32 | Properties: { 33 | status: GObject.ParamSpec.string( 34 | 'status', 35 | 'status', 36 | 'status', 37 | GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, 38 | FlatsealOverrideStatus.ORIGINAL, 39 | ), 40 | }, 41 | }, class FlatsealOverrideStatusIcon extends Gtk.Image { 42 | _init() { 43 | super._init({}); 44 | this._status = FlatsealOverrideStatus.ORIGINAL; 45 | } 46 | 47 | set status(status) { 48 | if (this._status === status) 49 | return; 50 | 51 | const context = this.get_style_context(); 52 | if (context.has_class(FlatsealOverrideStatus.USER)) 53 | context.remove_class(FlatsealOverrideStatus.USER); 54 | else if (context.has_class(FlatsealOverrideStatus.GLOBAL)) 55 | context.remove_class(FlatsealOverrideStatus.GLOBAL); 56 | 57 | this._status = status; 58 | if (status === FlatsealOverrideStatus.ORIGINAL) { 59 | this.set_tooltip_text(''); 60 | this.visible = false; 61 | return; 62 | } 63 | 64 | this.set_tooltip_text(OverrideStatusDescription[status]); 65 | this.visible = true; 66 | context.add_class(status); 67 | } 68 | 69 | get status() { 70 | return this._status; 71 | } 72 | }); 73 | -------------------------------------------------------------------------------- /src/widgets/overrideStatusIcon.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/widgets/pathRow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | store 10 | True 11 | True 12 | 13 | 14 | 12 15 | 16 | 17 | center 18 | 21 | 22 | 23 | 24 | 25 | True 26 | True 27 | center 28 | completion 29 | 30 | 31 | 32 | 33 | center 34 | center 35 | 36 | 37 | 38 | 39 | center 40 | window-close-symbolic 41 | 44 | 45 | 46 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/widgets/pathsViewer.js: -------------------------------------------------------------------------------- 1 | /* exported FlatsealPathsViewer */ 2 | 3 | /* pathsViewer.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject, Gtk} = imports.gi; 22 | const {FlatsealOverrideStatus} = imports.models.overrideStatus; 23 | 24 | const _propFlags = GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT; 25 | 26 | 27 | var FlatsealPathsViewer = GObject.registerClass({ 28 | GTypeName: 'FlatsealPathsViewer', 29 | Template: 'resource:///com/github/tchx84/Flatseal/widgets/pathsViewer.ui', 30 | Properties: { 31 | text: GObject.ParamSpec.string( 32 | 'text', 33 | 'text', 34 | 'text', 35 | _propFlags, ''), 36 | status: GObject.ParamSpec.string( 37 | 'status', 38 | 'status', 39 | 'status', 40 | _propFlags, 41 | FlatsealOverrideStatus.ORIGINAL), 42 | }, 43 | }, class FlatsealPathsViewer extends Gtk.Box { 44 | _init(serializeFunc, deserializeFunc, rowClass) { 45 | this._serializeFunc = serializeFunc; 46 | this._deserializeFunc = deserializeFunc; 47 | this._status = FlatsealOverrideStatus.ORIGINAL; 48 | super._init({}); 49 | this._rowClass = rowClass; 50 | } 51 | 52 | _changed() { 53 | this.notify('text'); 54 | } 55 | 56 | _remove(row) { 57 | this.remove(row); 58 | if (this.get_first_child() === null) 59 | this.visible = false; 60 | 61 | this._changed(); 62 | } 63 | 64 | _update(text) { 65 | for (const row of Array.from(this)) 66 | this.remove(row); 67 | 68 | this.visible = false; 69 | const paths = this._deserializeFunc(text); 70 | 71 | paths.forEach(path => { 72 | if (path.length === 0) 73 | return; 74 | 75 | this.add(path); 76 | }); 77 | 78 | this._updateStatus(); 79 | } 80 | 81 | _updateStatus() { 82 | const statuses = this._status 83 | .split(';') 84 | .filter(s => s.length !== 0); 85 | const rows = Array.from(this).reverse(); 86 | 87 | /* if it's still out of sync, bail out */ 88 | if (statuses.length !== rows.length) 89 | return; 90 | 91 | rows.forEach((row, index) => { 92 | row.status = statuses[index]; 93 | }); 94 | } 95 | 96 | set text(text) { 97 | if (text === '') { 98 | this._update(text); 99 | return; 100 | } 101 | 102 | this._update(text); 103 | this.notify('text'); 104 | } 105 | 106 | get text() { 107 | return this._serializeFunc(Array.from(this) 108 | .map(row => row.text) 109 | .reverse(), 110 | ); 111 | } 112 | 113 | set status(status) { 114 | this._status = status; 115 | this._updateStatus(); 116 | } 117 | 118 | get status() { 119 | return this._status; 120 | } 121 | 122 | add(path) { 123 | const row = new this._rowClass(); 124 | row.text = path; 125 | row.connect('remove-requested', this._remove.bind(this, row)); 126 | row.connect('notify::text', this._changed.bind(this)); 127 | 128 | this.prepend(row); 129 | this.visible = true; 130 | } 131 | }); 132 | -------------------------------------------------------------------------------- /src/widgets/pathsViewer.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | vertical 5 | False 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/widgets/permissionEntryRow.js: -------------------------------------------------------------------------------- 1 | /* exported FlatsealPermissionEntryRow */ 2 | 3 | /* permissionEntryRow.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject, Adw} = imports.gi; 22 | 23 | const {FlatsealPathsViewer} = imports.widgets.pathsViewer; 24 | 25 | 26 | var FlatsealPermissionEntryRow = GObject.registerClass({ 27 | GTypeName: 'FlatsealPermissionEntryRow', 28 | Template: 'resource:///com/github/tchx84/Flatseal/widgets/permissionEntryRow.ui', 29 | InternalChildren: [ 30 | 'description', 31 | 'permission', 32 | 'box', 33 | 'button', 34 | ], 35 | }, class FlatsealpermissionEntryRow extends Adw.PreferencesRow { 36 | _init(description, permission, content, serializeFunc, deserializeFunc, rowClass, iconName) { 37 | super._init({}); 38 | this._description.set_text(description); 39 | this._permission.set_text(permission); 40 | 41 | this._content = new FlatsealPathsViewer(serializeFunc, deserializeFunc, rowClass); 42 | this._content.text = content; 43 | this._box.append(this._content); 44 | this._content.bind_property('visible', this._box, 'visible', GObject.BindingFlags.SYNC_CREATE); 45 | 46 | this._button.icon_name = iconName; 47 | 48 | this._button.connect('clicked', this._add.bind(this)); 49 | this.connect('notify::sensitive', this._update.bind(this)); 50 | } 51 | 52 | _add() { 53 | this._content.add(''); 54 | } 55 | 56 | _update() { 57 | if (this.sensitive === false) 58 | this.set_tooltip_text(_('Not supported by the installed version of Flatpak')); 59 | else 60 | this.set_tooltip_text(''); 61 | } 62 | 63 | get content() { 64 | return this._content; 65 | } 66 | 67 | get status() { 68 | return this._content; 69 | } 70 | 71 | get supported() { 72 | return this.sensitive; 73 | } 74 | 75 | set supported(supported) { 76 | this.sensitive = supported; 77 | this._update(); 78 | } 79 | }); 80 | -------------------------------------------------------------------------------- /src/widgets/permissionEntryRow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | False 5 | False 6 | 7 | 8 | vertical 9 | 10 | 11 | True 12 | 13 | 14 | start 15 | center 16 | vertical 17 | True 18 | True 19 | 20 | 21 | True 22 | start 23 | end 24 | end 25 | True 26 | 29 | 30 | 31 | 32 | 33 | True 34 | start 35 | start 36 | end 37 | True 38 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | end 48 | center 49 | folder-new-symbolic 50 | 53 | 54 | 55 | 58 | 59 | 60 | 61 | 62 | start 63 | vertical 64 | 67 | 68 | 69 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/widgets/permissionPortalRow.js: -------------------------------------------------------------------------------- 1 | /* exported FlatsealPermissionPortalRow */ 2 | 3 | /* permissionPortalRow.js 4 | * 5 | * Copyright 2021 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GLib, GObject, Adw} = imports.gi; 22 | 23 | const {getDefault, FlatpakPortalState} = imports.models.portals; 24 | 25 | const _propFlags = GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT; 26 | 27 | 28 | var FlatsealPermissionPortalRow = GObject.registerClass({ 29 | GTypeName: 'FlatsealPermissionPortalRow', 30 | Template: 'resource:///com/github/tchx84/Flatseal/widgets/permissionPortalRow.ui', 31 | InternalChildren: ['stateSwitch', 'unsetButton'], 32 | Properties: { 33 | state: GObject.ParamSpec.int( 34 | 'state', 35 | 'state', 36 | 'state', 37 | _propFlags, 38 | FlatpakPortalState.UNKNOWN, 39 | FlatpakPortalState.ALLOWED, 40 | FlatpakPortalState.UNKNOWN), 41 | }, 42 | }, class FlatsealPermissionPortalRow extends Adw.ActionRow { 43 | _init(description, permission, content, table, id) { 44 | super._init({}); 45 | 46 | this.set_title(description); 47 | this.set_subtitle(permission); 48 | this._state = content; 49 | 50 | this._table = table; 51 | this._id = id; 52 | 53 | this._portals = getDefault(); 54 | this._unsetButton.connect('clicked', this._unsetClicked.bind(this)); 55 | this._stateHandlerId = this._stateSwitch.connect('state-set', this._stateSwitched.bind(this)); 56 | } 57 | 58 | _stateSwitched() { 59 | if (this._stateSwitch.active) 60 | this._state = FlatpakPortalState.ALLOWED; 61 | else 62 | this._state = FlatpakPortalState.DISALLOWED; 63 | 64 | this._updateWidget(); 65 | this.notify('state'); 66 | } 67 | 68 | _unsetClicked() { 69 | this._state = FlatpakPortalState.UNSET; 70 | this._updateWidget(); 71 | this.notify('state'); 72 | } 73 | 74 | _updateWidget() { 75 | GObject.signal_handler_block(this._stateSwitch, this._stateHandlerId); 76 | 77 | if (this._state === FlatpakPortalState.UNSUPPORTED) { 78 | this._stateSwitch.active = false; 79 | this._unsetButton.visible = false; 80 | this.sensitive = false; 81 | this.set_tooltip_text(this._portals.getUnsupportedReason(this._table, this._id)); 82 | } else if (this._state === FlatpakPortalState.UNSET) { 83 | this._stateSwitch.active = false; 84 | this._unsetButton.visible = false; 85 | this.sensitive = true; 86 | this.set_tooltip_text(''); 87 | } else if (this._state === FlatpakPortalState.DISALLOWED) { 88 | this._stateSwitch.active = false; 89 | this._unsetButton.visible = true; 90 | this.sensitive = true; 91 | this.set_tooltip_text(''); 92 | } else if (this._state === FlatpakPortalState.ALLOWED) { 93 | this._stateSwitch.active = true; 94 | this._unsetButton.visible = true; 95 | this.sensitive = true; 96 | this.set_tooltip_text(''); 97 | } 98 | 99 | GObject.signal_handler_unblock(this._stateSwitch, this._stateHandlerId); 100 | } 101 | 102 | get content() { 103 | return this; 104 | } 105 | 106 | set state(value) { 107 | if (value === this._state || typeof this._state === 'undefined') 108 | return; 109 | 110 | this._state = value; 111 | GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, this._updateWidget.bind(this)); 112 | } 113 | 114 | get state() { 115 | return this._state; 116 | } 117 | 118 | get supported() { 119 | return this.sensitive; 120 | } 121 | 122 | set supported(supported) { 123 | this.sensitive = supported; 124 | this._updateWidget(); 125 | } 126 | }); 127 | -------------------------------------------------------------------------------- /src/widgets/permissionPortalRow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | False 5 | stateSwitch 6 | 7 | 8 | 6 9 | 10 | 11 | Unset 12 | center 13 | edit-clear-all-symbolic 14 | 17 | 18 | 19 | 20 | 21 | center 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/widgets/permissionSwitchRow.js: -------------------------------------------------------------------------------- 1 | /* exported FlatsealPermissionSwitchRow */ 2 | 3 | /* permissionSwitchRow.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject, Adw} = imports.gi; 22 | const {FlatsealOverrideStatusIcon} = imports.widgets.overrideStatusIcon; 23 | 24 | 25 | var FlatsealPermissionSwitchRow = GObject.registerClass({ 26 | GTypeName: 'FlatsealPermissionSwitchRow', 27 | Template: 'resource:///com/github/tchx84/Flatseal/widgets/permissionSwitchRow.ui', 28 | InternalChildren: ['content', 'statusBox'], 29 | }, class FlatsealPermissionSwitchRow extends Adw.ActionRow { 30 | _init(description, permission, content) { 31 | super._init({}); 32 | this.set_title(description); 33 | this.set_subtitle(permission); 34 | this._content.set_active(content); 35 | this.connect('notify::sensitive', this._update.bind(this)); 36 | 37 | this._statusIcon = new FlatsealOverrideStatusIcon(); 38 | this._statusBox.append(this._statusIcon); 39 | } 40 | 41 | _update() { 42 | if (this.sensitive === false) 43 | this.set_tooltip_text(_('Not supported by the installed version of Flatpak')); 44 | else 45 | this.set_tooltip_text(''); 46 | } 47 | 48 | get content() { 49 | return this._content; 50 | } 51 | 52 | get status() { 53 | return this._statusIcon; 54 | } 55 | 56 | get supported() { 57 | return this.sensitive; 58 | } 59 | 60 | set supported(supported) { 61 | this.sensitive = supported; 62 | this._update(); 63 | } 64 | }); 65 | -------------------------------------------------------------------------------- /src/widgets/permissionSwitchRow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | False 5 | content 6 | 7 | 8 | center 9 | 10 | 11 | 12 | 13 | center 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/widgets/relativePathRow.js: -------------------------------------------------------------------------------- 1 | /* exported FlatsealRelativePathRow */ 2 | 3 | /* relativePathRow.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject, Gtk} = imports.gi; 22 | 23 | const {persistent} = imports.models; 24 | const {FlatsealOverrideStatusIcon} = imports.widgets.overrideStatusIcon; 25 | 26 | const _propFlags = GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT; 27 | 28 | var validity = { 29 | VALID: 'valid', 30 | NOTVALID: 'not-valid', 31 | }; 32 | 33 | const _notValidMsg = _('This is not a valid option'); 34 | 35 | 36 | var FlatsealRelativePathRow = GObject.registerClass({ 37 | GTypeName: 'FlatsealRelativePathRow', 38 | Template: 'resource:///com/github/tchx84/Flatseal/widgets/relativePathRow.ui', 39 | InternalChildren: ['entry', 'button', 'image', 'statusBox'], 40 | Properties: { 41 | text: GObject.ParamSpec.string( 42 | 'text', 43 | 'text', 44 | 'text', 45 | _propFlags, ''), 46 | }, 47 | Signals: { 48 | 'remove-requested': {}, 49 | }, 50 | }, class FlatsealRelativePathRow extends Gtk.Box { 51 | _init() { 52 | super._init({}); 53 | this._setup(); 54 | } 55 | 56 | _setup() { 57 | this._expression = new RegExp(/^[^\n]*$/); 58 | 59 | this._entry.connect('notify::text', this._changed.bind(this)); 60 | this._button.connect('clicked', this._remove.bind(this)); 61 | 62 | this._statusIcon = new FlatsealOverrideStatusIcon(); 63 | this._statusBox.append(this._statusIcon); 64 | 65 | this._validate(); 66 | } 67 | 68 | _remove() { 69 | this.emit('remove-requested'); 70 | } 71 | 72 | _changed() { 73 | this._update(); 74 | this._validate(); 75 | this.notify('text'); 76 | } 77 | 78 | _update() { 79 | const model = persistent.getDefault(); 80 | this.sensitive = !model.isOriginal(this.text); 81 | 82 | let tooltip = ''; 83 | if (!this.sensitive) 84 | tooltip = _('Default paths can\'t be removed'); 85 | 86 | this.set_tooltip_text(tooltip); 87 | } 88 | 89 | _validate() { 90 | const context = this.get_style_context(); 91 | 92 | if (context.has_class(validity.VALID)) 93 | context.remove_class(validity.VALID); 94 | else if (context.has_class(validity.NOTVALID)) 95 | context.remove_class(validity.NOTVALID); 96 | 97 | if (this._expression.test(this.text)) { 98 | context.add_class(validity.VALID); 99 | this._image.set_tooltip_text(''); 100 | } else { 101 | context.add_class(validity.NOTVALID); 102 | this._image.set_tooltip_text(_notValidMsg); 103 | } 104 | } 105 | 106 | get text() { 107 | if (!this._entry) 108 | return ''; 109 | return this._entry.get_text(); 110 | } 111 | 112 | set text(text) { 113 | if (this.text === text) 114 | return; 115 | this._entry.set_text(text); 116 | } 117 | 118 | get status() { 119 | return this._statusIcon.status; 120 | } 121 | 122 | set status(status) { 123 | this._statusIcon.status = status; 124 | } 125 | }); 126 | -------------------------------------------------------------------------------- /src/widgets/relativePathRow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 5 | 6 | 7 | True 8 | center 9 | 12 | 13 | 14 | 15 | 16 | True 17 | True 18 | center 19 | 20 | 21 | 22 | 23 | center 24 | center 25 | 26 | 27 | 28 | 29 | center 30 | window-close-symbolic 31 | 34 | 35 | 36 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/widgets/resetButton.js: -------------------------------------------------------------------------------- 1 | /* exported FlatsealResetButton */ 2 | 3 | /* resetButton.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject, Gtk, Adw} = imports.gi; 22 | 23 | 24 | var FlatsealResetButton = GObject.registerClass({ 25 | GTypeName: 'FlatsealResetButton', 26 | }, class FlatsealResetButton extends Gtk.Button { 27 | _init(permissions) { 28 | super._init(); 29 | this._setup(permissions); 30 | } 31 | 32 | /* XXX Can't move this a .ui file for some reason */ 33 | _setup(permissions) { 34 | this._permissions = permissions; 35 | this._permissions.connect('changed', this._update.bind(this)); 36 | 37 | this.label = _('_Reset'); 38 | this.use_underline = true; 39 | this.sensitive = false; 40 | 41 | this.add_css_class('reset-button'); 42 | 43 | this.connect('clicked', this._clicked.bind(this)); 44 | } 45 | 46 | _clicked() { 47 | this._permissions.reset(); 48 | } 49 | 50 | _update(widget, overriden, unsupported) { 51 | this.sensitive = overriden; 52 | if (unsupported) { 53 | const content = new Adw.ButtonContent(); 54 | content.icon_name = 'dialog-warning-symbolic'; 55 | content.label = _('_Reset'); 56 | content.use_underline = true; 57 | this.set_child(content); 58 | 59 | /* XXX this makes the button look very inconsistent */ 60 | this.remove_css_class('image-text-button'); 61 | } else { 62 | this.label = _('_Reset'); 63 | } 64 | 65 | let text = _('No changes made to this application'); 66 | 67 | if (overriden) 68 | text = _('Reset this application permissions'); 69 | if (unsupported) 70 | text += _(', including changes not made with Flatseal'); 71 | 72 | this.set_tooltip_text(text); 73 | } 74 | }); 75 | -------------------------------------------------------------------------------- /src/widgets/shortcutsWindow.js: -------------------------------------------------------------------------------- 1 | /* exported FlatsealShortcutsWindow */ 2 | 3 | /* shortcutsWindow.js 4 | * 5 | * Copyright 2021 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject, Gtk} = imports.gi; 22 | 23 | 24 | var FlatsealShortcutsWindow = GObject.registerClass({ 25 | GTypeName: 'FlatsealShortcutsWindow', 26 | Template: 'resource:///com/github/tchx84/Flatseal/widgets/shortcutsWindow.ui', 27 | }, class FlatsealShortcutsWindow extends Gtk.ShortcutsWindow { 28 | }); 29 | -------------------------------------------------------------------------------- /src/widgets/shortcutsWindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 6 | 7 | shortcuts 8 | 12 9 | 10 | 11 | General 12 | 13 | 14 | <alt> 15 | Show mnemonics 16 | 17 | 18 | 19 | 20 | F1 21 | Show documentation 22 | 23 | 24 | 25 | 26 | F10 27 | Show menu 28 | 29 | 30 | 31 | 32 | <ctrl>question 33 | Keyboard shortcuts 34 | 35 | 36 | 37 | 38 | <ctrl>Q 39 | Quit 40 | 41 | 42 | 43 | 44 | 45 | 46 | Navigation 47 | 48 | 49 | Left 50 | Move left 51 | 52 | 53 | 54 | 55 | Up 56 | Move up 57 | 58 | 59 | 60 | 61 | Right 62 | Move right 63 | 64 | 65 | 66 | 67 | Down 68 | Move down 69 | 70 | 71 | 72 | 73 | 74 | 75 | Applications 76 | 77 | 78 | <ctrl>F 79 | Find 80 | 81 | 82 | 83 | 84 | 85 | 86 | Permissions 87 | 88 | 89 | space 90 | Toggle 91 | 92 | 93 | 94 | 95 | 96 | 97 | Documentation 98 | 99 | 100 | <ctrl>F 101 | Find 102 | 103 | 104 | 105 | 106 | <ctrl>G 107 | Find next 108 | 109 | 110 | 111 | 112 | <Control><Shift>G 113 | Find previous 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /src/widgets/variableRow.js: -------------------------------------------------------------------------------- 1 | /* exported FlatsealVariableRow */ 2 | 3 | /* variableRow.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {GObject, Gtk} = imports.gi; 22 | 23 | const {FlatsealOverrideStatusIcon} = imports.widgets.overrideStatusIcon; 24 | const {VAR_REGEXP} = imports.models.variables; 25 | 26 | const _propFlags = GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT; 27 | 28 | var validity = { 29 | VALID: 'valid', 30 | NOTVALID: 'not-valid', 31 | }; 32 | 33 | const _notValidMsg = _('This is not a valid option'); 34 | 35 | 36 | var FlatsealVariableRow = GObject.registerClass({ 37 | GTypeName: 'FlatsealVariableRow', 38 | Template: 'resource:///com/github/tchx84/Flatseal/widgets/variableRow.ui', 39 | InternalChildren: ['entry', 'button', 'image', 'statusBox'], 40 | Properties: { 41 | text: GObject.ParamSpec.string( 42 | 'text', 43 | 'text', 44 | 'text', 45 | _propFlags, ''), 46 | }, 47 | Signals: { 48 | 'remove-requested': {}, 49 | }, 50 | }, class FlatsealVariableRow extends Gtk.Box { 51 | _init() { 52 | super._init({}); 53 | this._setup(); 54 | } 55 | 56 | _setup() { 57 | this._expression = new RegExp(VAR_REGEXP); 58 | 59 | this._entry.connect('notify::text', this._changed.bind(this)); 60 | this._button.connect('clicked', this._remove.bind(this)); 61 | 62 | this._statusIcon = new FlatsealOverrideStatusIcon(); 63 | this._statusBox.append(this._statusIcon); 64 | 65 | this._validate(); 66 | } 67 | 68 | _remove() { 69 | this.emit('remove-requested'); 70 | } 71 | 72 | _changed() { 73 | this._validate(); 74 | this.notify('text'); 75 | } 76 | 77 | _validate() { 78 | const context = this.get_style_context(); 79 | 80 | if (context.has_class(validity.VALID)) 81 | context.remove_class(validity.VALID); 82 | else if (context.has_class(validity.NOTVALID)) 83 | context.remove_class(validity.NOTVALID); 84 | 85 | if (this._expression.test(this.text)) { 86 | context.add_class(validity.VALID); 87 | this._image.set_tooltip_text(''); 88 | } else { 89 | context.add_class(validity.NOTVALID); 90 | this._image.set_tooltip_text(_notValidMsg); 91 | } 92 | } 93 | 94 | get text() { 95 | if (!this._entry) 96 | return ''; 97 | return this._entry.get_text(); 98 | } 99 | 100 | set text(text) { 101 | if (this.text === text) 102 | return; 103 | this._entry.set_text(text); 104 | } 105 | 106 | get status() { 107 | return this._statusIcon.status; 108 | } 109 | 110 | set status(status) { 111 | this._statusIcon.status = status; 112 | } 113 | }); 114 | -------------------------------------------------------------------------------- /src/widgets/variableRow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 5 | 6 | 7 | center 8 | 11 | 12 | 13 | 14 | 15 | True 16 | True 17 | center 18 | 19 | 20 | 21 | 22 | center 23 | center 24 | 25 | 26 | 27 | 28 | center 29 | window-close-symbolic 30 | 33 | 34 | 35 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /tests/content/.flatpak-info: -------------------------------------------------------------------------------- 1 | [Instance] 2 | flatpak-version=1.0 3 | -------------------------------------------------------------------------------- /tests/content/.flatpak-info.new: -------------------------------------------------------------------------------- 1 | [Instance] 2 | flatpak-version=3000 3 | -------------------------------------------------------------------------------- /tests/content/.flatpak-info.old: -------------------------------------------------------------------------------- 1 | [Instance] 2 | flatpak-version=0.0.1 3 | -------------------------------------------------------------------------------- /tests/content/custom/app/com.test.Extra/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Context] 2 | shared=ipc 3 | -------------------------------------------------------------------------------- /tests/content/extra/app/com.test.Extra/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Context] 2 | shared=network 3 | -------------------------------------------------------------------------------- /tests/content/global/flatpak/overrides/com.test.GlobalRestored: -------------------------------------------------------------------------------- 1 | [Context] 2 | sockets=x11;!wayland 3 | persistent=.test3 4 | filesystems=!~/test2;!~/test3;~/test4 5 | 6 | [Environment] 7 | TEST1= 8 | TEST2=override 9 | TEST4=override 10 | 11 | [Session Bus Policy] 12 | org.test.Service-3=none 13 | org.test.Service-4=talk 14 | org.test.Service-2=none 15 | org.test.Service-5=own 16 | -------------------------------------------------------------------------------- /tests/content/global/flatpak/overrides/global: -------------------------------------------------------------------------------- 1 | [Context] 2 | shared=unsupported 3 | sockets=!x11;wayland; 4 | persistent=.test2 5 | filesystems=~/test0;!~/test1;~/test3 6 | 7 | [Environment] 8 | TEST1=global 9 | TEST3=global 10 | 11 | [Session Bus Policy] 12 | org.test.Service-1=none 13 | org.test.Service-3=talk 14 | -------------------------------------------------------------------------------- /tests/content/globalNegated/flatpak/overrides/global: -------------------------------------------------------------------------------- 1 | [Context] 2 | shared=unsupported 3 | sockets=!x11 4 | filesystems=!~/test 5 | 6 | [Environment] 7 | TEST= 8 | 9 | [Session Bus Policy] 10 | org.test.Test=none 11 | -------------------------------------------------------------------------------- /tests/content/globalResetMode/flatpak/overrides/global: -------------------------------------------------------------------------------- 1 | [Context] 2 | filesystems=!host:reset -------------------------------------------------------------------------------- /tests/content/installations.d/extra.conf: -------------------------------------------------------------------------------- 1 | [Installation "extra"] 2 | Path=../tests/content/extra/ 3 | DisplayName=Extra Installation 4 | StorageType=sdcard 5 | 6 | [Installation "custom"] 7 | Path=../tests/content/custom/ 8 | DisplayName=Custom Installation 9 | StorageType=sdcard 10 | Priority=2 11 | -------------------------------------------------------------------------------- /tests/content/statuses/flatpak/overrides/com.test.Statuses: -------------------------------------------------------------------------------- 1 | [Context] 2 | sockets=x11 3 | persistent=.test3 4 | filesystems=~/test3 5 | 6 | [Environment] 7 | TEST2=override 8 | 9 | [Session Bus Policy] 10 | org.test.Service-5=talk 11 | org.test.Service-6=own 12 | -------------------------------------------------------------------------------- /tests/content/statuses/flatpak/overrides/global: -------------------------------------------------------------------------------- 1 | [Context] 2 | shared=unsupported 3 | sockets=!x11;wayland; 4 | persistent=.test2 5 | filesystems=~/test2 6 | 7 | [Environment] 8 | TEST1=global 9 | 10 | [Session Bus Policy] 11 | org.test.Service-3=talk 12 | org.test.Service-4=own 13 | -------------------------------------------------------------------------------- /tests/content/system/flatpak/app/com.test.BaseApp/current/active/metadata: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tchx84/Flatseal/c518eaa5992245e18b3f1659b857a795f1399eb8/tests/content/system/flatpak/app/com.test.BaseApp/current/active/metadata -------------------------------------------------------------------------------- /tests/content/system/flatpak/app/com.test.Basic/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Context] 2 | shared=network;ipc; 3 | sockets=x11;fallback-x11;wayland;pulseaudio;system-bus;session-bus;ssh-auth;pcsc;cups;gpg-agent;inherit-wayland-socket; 4 | devices=dri;input;kvm;shm;usb;all; 5 | features=bluetooth;devel;multiarch;canbus;per-app-dev-shm; 6 | filesystems=host;host-os;host-etc;home;~/test; 7 | persistent=.test; 8 | 9 | [Environment] 10 | TEST=yes 11 | 12 | [Session Bus Policy] 13 | org.test.Service-1=talk 14 | org.test.Service-2=own 15 | 16 | [System Bus Policy] 17 | org.test.Service-3=talk 18 | org.test.Service-4=own 19 | -------------------------------------------------------------------------------- /tests/content/system/flatpak/app/com.test.BasicNegated/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Context] 2 | shared=!network;!ipc; 3 | sockets=!x11;!fallback-x11;!wayland;!pulseaudio;!system-bus;!session-bus;!ssh-auth;!pcsc;!cups;!gpg-agent;!inherit-wayland-socket; 4 | devices=!dri;!input;!kvm;!shm;!usb;!all; 5 | features=!bluetooth;!devel;!multiarch;!canbus;!per-app-dev-shm; 6 | filesystems=!host;!host-os;!host-etc;!home;!~/test; 7 | persistent=tset. 8 | 9 | [Environment] 10 | TEST=no 11 | 12 | [Session Bus Policy] 13 | org.test.Service-1=none 14 | org.test.Service-2=none 15 | 16 | [System Bus Policy] 17 | org.test.Service-3=none 18 | org.test.Service-4=none 19 | -------------------------------------------------------------------------------- /tests/content/system/flatpak/app/com.test.Bus/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Session Bus Policy] 2 | org.test.Service-1=talk 3 | org.test.Service-2=own 4 | -------------------------------------------------------------------------------- /tests/content/system/flatpak/app/com.test.Environment/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Environment] 2 | TEST=yes 3 | -------------------------------------------------------------------------------- /tests/content/system/flatpak/app/com.test.FilesystemWithMode/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Context] 2 | filesystems=host:ro;xdg-documents:ro;home:ro 3 | -------------------------------------------------------------------------------- /tests/content/system/flatpak/app/com.test.Global/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Context] 2 | sockets=x11;!wayland;cups; 3 | persistent=.test1 4 | filesystems=!~/test0;~/test1;~/test2; 5 | 6 | [Environment] 7 | TEST1=original 8 | TEST2=original 9 | 10 | [Session Bus Policy] 11 | org.test.Service-1=talk 12 | org.test.Service-2=own 13 | -------------------------------------------------------------------------------- /tests/content/system/flatpak/app/com.test.GlobalRestored/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Context] 2 | sockets=x11;!wayland;cups; 3 | persistent=.test1 4 | filesystems=!~/test0;~/test1;~/test2; 5 | 6 | [Environment] 7 | TEST1=original 8 | TEST2=original 9 | 10 | [Session Bus Policy] 11 | org.test.Service-1=talk 12 | org.test.Service-2=own 13 | -------------------------------------------------------------------------------- /tests/content/system/flatpak/app/com.test.Increase/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Context] 2 | filesystems=xdg-pictures:ro 3 | -------------------------------------------------------------------------------- /tests/content/system/flatpak/app/com.test.Malformed/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Context] 2 | shared=network; -------------------------------------------------------------------------------- /tests/content/system/flatpak/app/com.test.Negation/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Context] 2 | filesystems=!~/negative;~/positive 3 | -------------------------------------------------------------------------------- /tests/content/system/flatpak/app/com.test.Old/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Context] 2 | filesystems=xdg-pictures 3 | -------------------------------------------------------------------------------- /tests/content/system/flatpak/app/com.test.Overriden/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Context] 2 | shared=network;ipc; 3 | sockets=x11;fallback-x11;wayland;pulseaudio;system-bus;session-bus;ssh-auth;cups;gpg-agent;inherit-wayland-socket; 4 | devices=dri;all; 5 | features=bluetooth;devel;multiarch; 6 | filesystems=host;home;~/test; 7 | -------------------------------------------------------------------------------- /tests/content/system/flatpak/app/com.test.Reduce/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Context] 2 | filesystems=xdg-downloads 3 | -------------------------------------------------------------------------------- /tests/content/system/flatpak/app/com.test.ResetMode/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Context] 2 | filesystems=host; -------------------------------------------------------------------------------- /tests/content/system/flatpak/app/com.test.Statuses/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Context] 2 | sockets=x11;!wayland;cups; 3 | persistent=.test1 4 | filesystems=~/test1 5 | 6 | [Environment] 7 | TEST0=original 8 | TEST1=original 9 | TEST2=original 10 | 11 | [Session Bus Policy] 12 | org.test.Service-1=talk 13 | org.test.Service-2=own 14 | -------------------------------------------------------------------------------- /tests/content/system/flatpak/app/com.test.TrailingSemicolon/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Context] 2 | shared=network 3 | 4 | [Environment] 5 | TEST=YES 6 | -------------------------------------------------------------------------------- /tests/content/system/flatpak/app/com.test.Unsupported/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Context] 2 | shared=unsupported 3 | unsupported=all 4 | filesystems=~/unsupported 5 | -------------------------------------------------------------------------------- /tests/content/system/flatpak/app/com.test.Variables/current/active/metadata: -------------------------------------------------------------------------------- 1 | [Environment] 2 | ENVIRONMENT=set 3 | -------------------------------------------------------------------------------- /tests/content/user/flatpak/overrides/com.test.Basic: -------------------------------------------------------------------------------- 1 | [Context] 2 | shared=!network;!ipc; 3 | sockets=!x11;!fallback-x11;!wayland;!pulseaudio;!system-bus;!session-bus;!ssh-auth;!pcsc;!cups;!gpg-agent;!inherit-wayland-socket; 4 | devices=!dri;!input;!kvm;!shm;!usb;!all; 5 | features=!bluetooth;!devel;!multiarch;!canbus;!per-app-dev-shm; 6 | filesystems=!host;!host-os;!host-etc;!home;!~/test; 7 | persistent=tset. 8 | 9 | [Environment] 10 | TEST=no 11 | 12 | [Session Bus Policy] 13 | org.test.Service-1=none 14 | org.test.Service-2=none 15 | 16 | [System Bus Policy] 17 | org.test.Service-3=none 18 | org.test.Service-4=none 19 | -------------------------------------------------------------------------------- /tests/content/user/flatpak/overrides/com.test.BasicNegated: -------------------------------------------------------------------------------- 1 | [Context] 2 | shared=network;ipc; 3 | sockets=x11;fallback-x11;wayland;pulseaudio;system-bus;session-bus;ssh-auth;pcsc;cups;gpg-agent;inherit-wayland-socket; 4 | devices=dri;input;kvm;shm;usb;all; 5 | features=bluetooth;devel;multiarch;canbus;per-app-dev-shm; 6 | filesystems=host;host-os;host-etc;home;~/test; 7 | persistent=.test; 8 | 9 | [Environment] 10 | TEST=yes 11 | 12 | [Session Bus Policy] 13 | org.test.Service-1=talk 14 | org.test.Service-2=own 15 | 16 | [System Bus Policy] 17 | org.test.Service-3=talk 18 | org.test.Service-4=own 19 | -------------------------------------------------------------------------------- /tests/content/user/flatpak/overrides/com.test.FilesystemWithMode: -------------------------------------------------------------------------------- 1 | [Context] 2 | filesystems=!host;!xdg-documents 3 | -------------------------------------------------------------------------------- /tests/content/user/flatpak/overrides/com.test.Malformed: -------------------------------------------------------------------------------- 1 | malformed -------------------------------------------------------------------------------- /tests/content/user/flatpak/overrides/com.test.Old: -------------------------------------------------------------------------------- 1 | [Context] 2 | filesystems=xdg-pictures:ro;!xdg-pictures 3 | -------------------------------------------------------------------------------- /tests/content/user/flatpak/overrides/com.test.TrailingSemicolon: -------------------------------------------------------------------------------- 1 | [Context] 2 | shared=ipc; 3 | 4 | [Environment] 5 | TEST=; 6 | -------------------------------------------------------------------------------- /tests/content/user/flatpak/overrides/com.test.Unsupported: -------------------------------------------------------------------------------- 1 | [Context] 2 | shared=unsupported 3 | unsupported=always 4 | -------------------------------------------------------------------------------- /tests/content/user/flatpak/overrides/com.test.Variables: -------------------------------------------------------------------------------- 1 | [Environment] 2 | ENVIRONMENT= 3 | -------------------------------------------------------------------------------- /tests/meson.build: -------------------------------------------------------------------------------- 1 | jasmine = find_program('jasmine', required: false) 2 | if jasmine.found() 3 | env = environment() 4 | env.set('LC_ALL', 'C') 5 | 6 | tests = join_paths(meson.current_source_dir(), 'src') 7 | 8 | test('Jasmine tests', jasmine, 9 | env: env, 10 | args: ['--no-config', '--verbose', '--no-color', tests]) 11 | endif 12 | -------------------------------------------------------------------------------- /tests/service.js: -------------------------------------------------------------------------------- 1 | /* eslint class-methods-use-this:, no-unused-vars: */ 2 | 3 | /* service.js 4 | * 5 | * Copyright 2021 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | imports.gi.versions.Gtk = '4.0'; 22 | 23 | const {Gio, GLib, Gtk} = imports.gi; 24 | 25 | const PermissionsIface = ` 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | `; 56 | 57 | 58 | class MockPermissionsStore { 59 | constructor() { 60 | this._store = { 61 | background: { 62 | background: {}, 63 | }, 64 | notifications: { 65 | notification: {}, 66 | }, 67 | devices: { 68 | speakers: {}, 69 | microphone: {}, 70 | camera: {}, 71 | }, 72 | location: { 73 | location: {}, 74 | }, 75 | }; 76 | 77 | this._version = new GLib.Variant('u', 2); 78 | this._dbusId = null; 79 | this._nameId = Gio.bus_own_name( 80 | Gio.BusType.SESSION, 81 | 'com.github.tchx84.Flatseal.PermissionStore', 82 | Gio.BusNameOwnerFlags.NONE, 83 | this._onBusAcquired.bind(this), 84 | null, 85 | null, 86 | ); 87 | } 88 | 89 | _onBusAcquired(connection, name) { 90 | const info = Gio.DBusNodeInfo.new_for_xml(PermissionsIface); 91 | const activationId = connection.register_object( 92 | '/org/freedesktop/impl/portal/PermissionStore', 93 | info.interfaces[0], 94 | this._onCalled.bind(this), 95 | this._onProperty.bind(this), 96 | null, 97 | ); 98 | 99 | if (activationId <= 0) 100 | throw new Error('activationId is ZERO'); 101 | } 102 | 103 | _onCalled(connection, sender, path, iface, method, params, invocation) { 104 | if (method === 'Lookup') { 105 | const [table, id] = params.deep_unpack(); 106 | 107 | if (!(table in this._store) || !(id in this._store[table])) 108 | invocation.return_dbus_error('org.freedesktop.portal.Error.NotFound', ''); 109 | 110 | const data = new GLib.Variant('b', true); 111 | const permissions = new GLib.Variant('(a{sas}v)', [this._store[table][id], data]); 112 | 113 | invocation.return_value(permissions); 114 | } else if (method === 'SetPermission') { 115 | const [table, create, id, appId, permissions] = params.deep_unpack(); 116 | 117 | this._store[table][id][appId] = permissions; 118 | 119 | invocation.return_value(null); 120 | } else if (method === 'List') { 121 | var ids = []; 122 | const [table] = params.deep_unpack(); 123 | 124 | if (table in this._store) 125 | ids = Object.keys(this._store[table]); 126 | 127 | const value = new GLib.Variant('(as)', [ids]); 128 | invocation.return_value(value); 129 | } else if (method === 'DeletePermission') { 130 | const [table, id, appId] = params.deep_unpack(); 131 | 132 | if (table in this._store && id in this._store[table] && appId in this._store[table][id]) 133 | delete this._store[table][id][appId]; 134 | 135 | invocation.return_value(null); 136 | } else if (method === 'testPartialTable') { 137 | delete this._store['devices']['microphone']; 138 | 139 | invocation.return_value(null); 140 | } 141 | } 142 | 143 | _onProperty(connection, sender, path, iface, key) { 144 | return this._version; 145 | } 146 | 147 | shutdown() { 148 | Gio.bus_unown_name(this._nameId); 149 | } 150 | } 151 | 152 | Gtk.init(); 153 | 154 | const service = new MockPermissionsStore(); 155 | 156 | const loop = new GLib.MainLoop(null, false); 157 | loop.run(); 158 | -------------------------------------------------------------------------------- /tests/src/testPathRow.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: */ 2 | 3 | /* testPathRow.js 4 | * 5 | * Copyright 2020 Martin Abente Lahaye 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | const {setup} = imports.utils; 22 | setup(); 23 | 24 | const {FlatsealPathRow, mode, validity} = imports.widgets.pathRow; 25 | 26 | 27 | describe('FlatsealPathRow', function() { 28 | var row; 29 | 30 | beforeEach(function() { 31 | row = new FlatsealPathRow(); 32 | }); 33 | 34 | it('starts empty', function() { 35 | expect(row.text).toEqual(''); 36 | }); 37 | 38 | it('processes path correctly', function() { 39 | const text = 'home:ro'; 40 | row.text = text; 41 | 42 | expect(row.text).toEqual(text); 43 | }); 44 | 45 | it('sets ready-only style class', function() { 46 | row.text = 'home:ro'; 47 | const context = row.get_style_context(); 48 | 49 | expect(context.has_class(mode.READONLY)).toBe(true); 50 | expect(context.has_class(mode.READWRITE)).toBe(false); 51 | expect(context.has_class(mode.CREATE)).toBe(false); 52 | }); 53 | 54 | it('sets ready-write style class', function() { 55 | row.text = 'home:rw'; 56 | const context = row.get_style_context(); 57 | 58 | expect(context.has_class(mode.READONLY)).toBe(false); 59 | expect(context.has_class(mode.READWRITE)).toBe(true); 60 | expect(context.has_class(mode.CREATE)).toBe(false); 61 | }); 62 | 63 | it('sets ready-write style class (default)', function() { 64 | row.text = 'home'; 65 | const context = row.get_style_context(); 66 | 67 | expect(context.has_class(mode.READONLY)).toBe(false); 68 | expect(context.has_class(mode.READWRITE)).toBe(true); 69 | expect(context.has_class(mode.CREATE)).toBe(false); 70 | }); 71 | 72 | it('sets create style class', function() { 73 | row.text = 'home:create'; 74 | const context = row.get_style_context(); 75 | 76 | expect(context.has_class(mode.READONLY)).toBe(false); 77 | expect(context.has_class(mode.READWRITE)).toBe(false); 78 | expect(context.has_class(mode.CREATE)).toBe(true); 79 | }); 80 | 81 | function _handles(description, path, _mode) { 82 | it(`handles ${description} paths (${_mode ? _mode : 'default'})`, function() { 83 | row.text = `${path}${_mode}`; 84 | const context = row.get_style_context(); 85 | 86 | expect(context.has_class(validity.VALID)).toBe(true); 87 | expect(context.has_class(validity.NOTVALID)).toBe(false); 88 | }); 89 | } 90 | _handles('absolute', '/home/.test', ''); 91 | _handles('absolute', '/home/.test', ':ro'); 92 | _handles('absolute', '/home/.test', ':rw'); 93 | _handles('absolute', '/home/.test', ':create'); 94 | _handles('absolute', '/home/.test/', ''); 95 | _handles('relative', '~/.test', ''); 96 | _handles('relative', '~/.test', ':ro'); 97 | _handles('relative', '~/.test', ':rw'); 98 | _handles('relative', '~/.test', ':create'); 99 | _handles('relative', '~/.test/', ''); 100 | _handles('relative', '~/.local/share/Folder/Games/common/Console Classics/uncompressed GAMEs/Old_Game_wVersion3.bin', ':ro'); 101 | _handles('relative', '!~/.TelegramDesktop', ''); 102 | _handles('token-based', 'home/.test', ''); 103 | _handles('token-based', 'home/.test', ':ro'); 104 | _handles('token-based', 'home/.test', ':rw'); 105 | _handles('token-based', 'home/.test', ':create'); 106 | _handles('token-based', 'home/.test/', ''); 107 | _handles('token-based', 'xdg-download/Telegram Desktop:create', ''); 108 | _handles('token-based', '!xdg-download', ''); 109 | _handles('token-based', '!xdg-download', ':reset'); 110 | 111 | function _catches(description, path, _mode) { 112 | it(`catches ${description} paths (${_mode ? _mode : 'default'})`, function() { 113 | row.text = `${path}${_mode}`; 114 | const context = row.get_style_context(); 115 | 116 | expect(context.has_class(validity.VALID)).toBe(false); 117 | expect(context.has_class(validity.NOTVALID)).toBe(true); 118 | }); 119 | } 120 | 121 | _catches('not-valid empty', '', ''); 122 | _catches('not-valid absolute', '/', ''); 123 | _catches('not-valid relative', '~/', ''); 124 | _catches('not-valid absolute', '/home/ .test ', ''); 125 | _catches('not-valid relative', '~/ .test ', ''); 126 | _catches('not-valid token-based', 'home ', ':ro'); 127 | _catches('not-valid token-based', 'home/', ''); 128 | _catches('not-valid token-based', 'home/.test/ ', ''); 129 | _catches('not-valid token-based', 'home-non-valid', ''); 130 | _catches('not-valid token-based', 'jome/.test ', ''); 131 | _catches('not-valid mode', 'home', ':'); 132 | _catches('not-valid mode', 'home', ':not'); 133 | _catches('not-valid negation', '!!~/.TelegramDesktop', ''); 134 | _catches('not-valid negation', '!~/.TelegramDesktop', ':'); 135 | _catches('not-valid negation', '!~/.TelegramDesktop', ':ro'); 136 | }); 137 | -------------------------------------------------------------------------------- /tests/src/testPathsViewer.js: -------------------------------------------------------------------------------- 1 | /* testPathsViewer.js 2 | * 3 | * Copyright 2020 Martin Abente Lahaye 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | const {setup} = imports.utils; 20 | setup(); 21 | 22 | const {FlatsealPathsViewer} = imports.widgets.pathsViewer; 23 | const {FlatsealPathRow} = imports.widgets.pathRow; 24 | 25 | const _paths = 'home;host;xdg-desktop'; 26 | 27 | 28 | describe('FlatsealPathsViewer', function() { 29 | var viewer; 30 | 31 | beforeEach(function() { 32 | viewer = new FlatsealPathsViewer( 33 | (v) => v.join(';'), 34 | (v) => v.split(';'), 35 | FlatsealPathRow, 36 | ); 37 | }); 38 | 39 | it('starts empty', function() { 40 | expect(viewer.text).toEqual(''); 41 | }); 42 | 43 | it('processes paths correctly', function() { 44 | viewer.text = _paths; 45 | expect(viewer.text).toEqual(_paths); 46 | }); 47 | 48 | it('handles new paths', function() { 49 | viewer.text = _paths; 50 | viewer.add('~/Steam'); 51 | expect(viewer.text).toEqual(`${_paths};~/Steam`); 52 | }); 53 | 54 | it('resets paths', function() { 55 | viewer.add(''); 56 | viewer.text = ''; 57 | expect(viewer.text).toEqual(''); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /tests/src/utils.js: -------------------------------------------------------------------------------- 1 | /* exported setup update has hasOnly hasInTotal startService 2 | stopService getValueFromService waitForService partialService */ 3 | /* eslint no-extend-native: */ 4 | 5 | /* utils.js 6 | * 7 | * Copyright 2020 Martin Abente Lahaye 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | imports.gi.versions.Gtk = '4.0'; 24 | 25 | const {gettext} = imports; 26 | 27 | const {Gio, GLib, Gtk} = imports.gi; 28 | 29 | 30 | const TestPermissionStoreIface = ` 31 | 32 | 33 | 34 | 35 | 36 | 37 | `; 38 | 39 | 40 | function setup() { 41 | Gtk.init(); 42 | 43 | /* XXX this shouldn't be needed */ 44 | const {format} = imports; 45 | String.prototype.format = format.format; 46 | 47 | window._ = gettext.gettext; 48 | 49 | const src = GLib.build_filenamev([ 50 | GLib.get_current_dir(), 51 | 'src', 52 | 'com.github.tchx84.Flatseal.src.gresource', 53 | ]); 54 | 55 | const data = GLib.build_filenamev([ 56 | GLib.get_current_dir(), 57 | 'src', 58 | 'com.github.tchx84.Flatseal.data.gresource', 59 | ]); 60 | 61 | Gio.Resource.load(src)._register(); 62 | Gio.Resource.load(data)._register(); 63 | 64 | imports.searchPath.unshift('resource:///com/github/tchx84/Flatseal/js'); 65 | } 66 | 67 | 68 | function update() { 69 | const context = GLib.MainContext.default(); 70 | while (context.pending()) 71 | context.iteration(true); 72 | } 73 | 74 | 75 | function has(path, group, key, value) { 76 | const keyFile = new GLib.KeyFile(); 77 | keyFile.load_from_file(path, 0); 78 | 79 | const [keys] = keyFile.get_keys(group); 80 | if (!keys.includes(key)) 81 | return false; 82 | 83 | const values = keyFile.get_value(group, key); 84 | return values.split(';').indexOf(value) !== -1; 85 | } 86 | 87 | 88 | function hasOnly(path, group, key, value) { 89 | const keyFile = new GLib.KeyFile(); 90 | keyFile.load_from_file(path, 0); 91 | 92 | const [keys] = keyFile.get_keys(group); 93 | const values = keyFile.get_value(group, key); 94 | const list = values.split(';'); 95 | 96 | return keys.length === 1 && list.length === 1 && list.indexOf(value) !== -1; 97 | } 98 | 99 | 100 | function hasInTotal(path) { 101 | let count = 0; 102 | 103 | const keyFile = new GLib.KeyFile(); 104 | keyFile.load_from_file(path, 0); 105 | 106 | const [groups] = keyFile.get_groups(); 107 | 108 | groups.forEach(group => { 109 | const [keys] = keyFile.get_keys(group); 110 | 111 | keys.forEach(key => { 112 | const values = keyFile.get_value(group, key); 113 | count += values.split(';').length; 114 | }); 115 | }); 116 | 117 | return count; 118 | } 119 | 120 | 121 | function startService() { 122 | GLib.setenv( 123 | 'FLATSEAL_PORTAL_BUS_NAME', 124 | 'com.github.tchx84.Flatseal.PermissionStore', 125 | true); 126 | const service = GLib.build_filenamev([ 127 | '..', 128 | 'tests', 129 | 'service.js', 130 | ]); 131 | window.service = Gio.Subprocess.new(['gjs', service], null); 132 | } 133 | 134 | 135 | function stopService() { 136 | window.service.force_exit(); 137 | } 138 | 139 | 140 | function getValueFromService(table, id, allowed, appId) { 141 | const {PermissionsIface} = imports.models.portals; 142 | const Proxy = Gio.DBusProxy.makeProxyWrapper(PermissionsIface); 143 | 144 | const proxy = new Proxy( 145 | Gio.DBus.session, 146 | GLib.getenv('FLATSEAL_PORTAL_BUS_NAME'), 147 | '/org/freedesktop/impl/portal/PermissionStore'); 148 | 149 | let appIds; 150 | try { 151 | [appIds] = proxy.LookupSync(table, id); 152 | } catch (err) { 153 | appIds = null; 154 | } 155 | 156 | // check if no entry in the permission store 157 | if (allowed === null && (appIds === null || !(appId in appIds))) 158 | return true; 159 | 160 | const value = appId in appIds && appIds[appId][0] === allowed; 161 | return value; 162 | } 163 | 164 | function waitForService() { 165 | const {PermissionsIface} = imports.models.portals; 166 | var version = null; 167 | 168 | do { 169 | GLib.usleep(1000000); 170 | 171 | const Proxy = Gio.DBusProxy.makeProxyWrapper(PermissionsIface); 172 | const proxy = new Proxy( 173 | Gio.DBus.session, 174 | GLib.getenv('FLATSEAL_PORTAL_BUS_NAME'), 175 | '/org/freedesktop/impl/portal/PermissionStore'); 176 | version = proxy.version; // eslint-disable-line prefer-destructuring 177 | } while (version === null); 178 | } 179 | 180 | function partialService() { 181 | const Proxy = Gio.DBusProxy.makeProxyWrapper(TestPermissionStoreIface); 182 | 183 | const proxy = new Proxy( 184 | Gio.DBus.session, 185 | GLib.getenv('FLATSEAL_PORTAL_BUS_NAME'), 186 | '/org/freedesktop/impl/portal/PermissionStore'); 187 | 188 | proxy.testPartialTableSync(); 189 | } 190 | --------------------------------------------------------------------------------
13 | Flatseal is a graphical utility to review and modify permissions from your Flatpak applications. 14 |